From 55db77b12596c4d49a176a1001883fb653405180 Mon Sep 17 00:00:00 2001 From: Ghislain B Date: Sun, 9 Apr 2017 00:36:51 -0400 Subject: [PATCH] initial commit aurelia-bootstrap-select --- .vscode/launch.json | 28 + README.md | 20 +- aurelia-bootstrap-select/.editorconfig | 14 + aurelia-bootstrap-select/.eslintrc.json | 3 + aurelia-bootstrap-select/.gitignore | 6 + aurelia-bootstrap-select/.npmignore | 7 + aurelia-bootstrap-select/LICENSE | 20 + aurelia-bootstrap-select/README.md | 374 +++++++++++ aurelia-bootstrap-select/build/args.js | 14 + .../build/babel-options.js | 46 ++ aurelia-bootstrap-select/build/paths.js | 16 + aurelia-bootstrap-select/build/tasks/build.js | 71 +++ aurelia-bootstrap-select/build/tasks/clean.js | 10 + aurelia-bootstrap-select/build/tasks/dev.js | 21 + aurelia-bootstrap-select/build/tasks/doc.js | 14 + aurelia-bootstrap-select/build/tasks/lint.js | 11 + .../build/tasks/prepare-release.js | 40 ++ aurelia-bootstrap-select/build/tasks/test.js | 49 ++ aurelia-bootstrap-select/config.js | 15 + .../dist/amd/abp-select.html | 45 ++ .../dist/amd/abp-select.js | 570 +++++++++++++++++ aurelia-bootstrap-select/dist/amd/index.js | 21 + .../dist/amd/picker-config.js | 21 + .../dist/amd/picker-global-options.js | 30 + .../dist/amd/util-service.js | 66 ++ .../dist/commonjs/abp-select.html | 45 ++ .../dist/commonjs/abp-select.js | 570 +++++++++++++++++ .../dist/commonjs/index.js | 24 + .../dist/commonjs/picker-config.js | 17 + .../dist/commonjs/picker-global-options.js | 28 + .../dist/commonjs/util-service.js | 56 ++ .../dist/es2015/abp-select.html | 45 ++ .../dist/es2015/abp-select.js | 493 +++++++++++++++ aurelia-bootstrap-select/dist/es2015/index.js | 14 + .../dist/es2015/picker-config.js | 8 + .../dist/es2015/picker-global-options.js | 24 + .../dist/es2015/util-service.js | 40 ++ .../dist/system/abp-select.html | 45 ++ .../dist/system/abp-select.js | 579 ++++++++++++++++++ aurelia-bootstrap-select/dist/system/index.js | 31 + .../dist/system/picker-config.js | 30 + .../dist/system/picker-global-options.js | 39 ++ .../dist/system/util-service.js | 73 +++ aurelia-bootstrap-select/doc/CHANGELOG.md | 15 + aurelia-bootstrap-select/doc/api.json | 1 + aurelia-bootstrap-select/gulpfile.js | 3 + aurelia-bootstrap-select/karma.conf.js | 76 +++ aurelia-bootstrap-select/package.json | 83 +++ aurelia-bootstrap-select/src/abp-select.html | 45 ++ aurelia-bootstrap-select/src/abp-select.js | 458 ++++++++++++++ aurelia-bootstrap-select/src/index.js | 17 + aurelia-bootstrap-select/src/picker-config.js | 8 + .../src/picker-global-options.js | 29 + aurelia-bootstrap-select/src/util-service.js | 79 +++ aurelia-bootstrap-select/test/setup.js | 1 + .../test/unit/configure.spec.js | 21 + client-cli/aurelia_project/aurelia.json | 14 + client-cli/index.html | 1 + client-cli/package.json | 4 +- client-cli/src/bootstrap-plugins.html | 48 ++ client-cli/src/bootstrap-plugins.js | 81 ++- client-cli/src/main.js | 2 + .../src/resources/behaviors/optional.js | 21 + .../resources/elements/bootstrap-select.html | 7 + .../resources/elements/bootstrap-select.js | 81 +++ .../src/resources/elements/bselect.html | 45 ++ client-cli/src/resources/elements/bselect.js | 426 +++++++++++++ .../elements/picker-global-options.js | 30 + .../src/resources/elements/util-service.js | 79 +++ client-cli/src/resources/index.js | 8 + .../resources/value-converters/stringify.js | 5 + client-wp/package.json | 3 + client-wp/src/bootstrap-plugins.html | 48 ++ client-wp/src/bootstrap-plugins.js | 81 ++- client-wp/src/main.js | 2 + client-wp/src/resources/behaviors/optional.js | 21 + .../resources/elements/bootstrap-select.html | 7 + .../resources/elements/bootstrap-select.js | 81 +++ client-wp/src/resources/elements/bselect.html | 45 ++ client-wp/src/resources/elements/bselect.js | 426 +++++++++++++ .../elements/picker-global-options.js | 30 + .../src/resources/elements/util-service.js | 79 +++ client-wp/src/resources/index.js | 8 + .../resources/value-converters/stringify.js | 5 + jsconfig.json | 11 + tsconfig.json | 11 + 86 files changed, 6217 insertions(+), 22 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 aurelia-bootstrap-select/.editorconfig create mode 100644 aurelia-bootstrap-select/.eslintrc.json create mode 100644 aurelia-bootstrap-select/.gitignore create mode 100644 aurelia-bootstrap-select/.npmignore create mode 100644 aurelia-bootstrap-select/LICENSE create mode 100644 aurelia-bootstrap-select/README.md create mode 100644 aurelia-bootstrap-select/build/args.js create mode 100644 aurelia-bootstrap-select/build/babel-options.js create mode 100644 aurelia-bootstrap-select/build/paths.js create mode 100644 aurelia-bootstrap-select/build/tasks/build.js create mode 100644 aurelia-bootstrap-select/build/tasks/clean.js create mode 100644 aurelia-bootstrap-select/build/tasks/dev.js create mode 100644 aurelia-bootstrap-select/build/tasks/doc.js create mode 100644 aurelia-bootstrap-select/build/tasks/lint.js create mode 100644 aurelia-bootstrap-select/build/tasks/prepare-release.js create mode 100644 aurelia-bootstrap-select/build/tasks/test.js create mode 100644 aurelia-bootstrap-select/config.js create mode 100644 aurelia-bootstrap-select/dist/amd/abp-select.html create mode 100644 aurelia-bootstrap-select/dist/amd/abp-select.js create mode 100644 aurelia-bootstrap-select/dist/amd/index.js create mode 100644 aurelia-bootstrap-select/dist/amd/picker-config.js create mode 100644 aurelia-bootstrap-select/dist/amd/picker-global-options.js create mode 100644 aurelia-bootstrap-select/dist/amd/util-service.js create mode 100644 aurelia-bootstrap-select/dist/commonjs/abp-select.html create mode 100644 aurelia-bootstrap-select/dist/commonjs/abp-select.js create mode 100644 aurelia-bootstrap-select/dist/commonjs/index.js create mode 100644 aurelia-bootstrap-select/dist/commonjs/picker-config.js create mode 100644 aurelia-bootstrap-select/dist/commonjs/picker-global-options.js create mode 100644 aurelia-bootstrap-select/dist/commonjs/util-service.js create mode 100644 aurelia-bootstrap-select/dist/es2015/abp-select.html create mode 100644 aurelia-bootstrap-select/dist/es2015/abp-select.js create mode 100644 aurelia-bootstrap-select/dist/es2015/index.js create mode 100644 aurelia-bootstrap-select/dist/es2015/picker-config.js create mode 100644 aurelia-bootstrap-select/dist/es2015/picker-global-options.js create mode 100644 aurelia-bootstrap-select/dist/es2015/util-service.js create mode 100644 aurelia-bootstrap-select/dist/system/abp-select.html create mode 100644 aurelia-bootstrap-select/dist/system/abp-select.js create mode 100644 aurelia-bootstrap-select/dist/system/index.js create mode 100644 aurelia-bootstrap-select/dist/system/picker-config.js create mode 100644 aurelia-bootstrap-select/dist/system/picker-global-options.js create mode 100644 aurelia-bootstrap-select/dist/system/util-service.js create mode 100644 aurelia-bootstrap-select/doc/CHANGELOG.md create mode 100644 aurelia-bootstrap-select/doc/api.json create mode 100644 aurelia-bootstrap-select/gulpfile.js create mode 100644 aurelia-bootstrap-select/karma.conf.js create mode 100644 aurelia-bootstrap-select/package.json create mode 100644 aurelia-bootstrap-select/src/abp-select.html create mode 100644 aurelia-bootstrap-select/src/abp-select.js create mode 100644 aurelia-bootstrap-select/src/index.js create mode 100644 aurelia-bootstrap-select/src/picker-config.js create mode 100644 aurelia-bootstrap-select/src/picker-global-options.js create mode 100644 aurelia-bootstrap-select/src/util-service.js create mode 100644 aurelia-bootstrap-select/test/setup.js create mode 100644 aurelia-bootstrap-select/test/unit/configure.spec.js create mode 100644 client-cli/src/resources/behaviors/optional.js create mode 100644 client-cli/src/resources/elements/bootstrap-select.html create mode 100644 client-cli/src/resources/elements/bootstrap-select.js create mode 100644 client-cli/src/resources/elements/bselect.html create mode 100644 client-cli/src/resources/elements/bselect.js create mode 100644 client-cli/src/resources/elements/picker-global-options.js create mode 100644 client-cli/src/resources/elements/util-service.js create mode 100644 client-cli/src/resources/index.js create mode 100644 client-cli/src/resources/value-converters/stringify.js create mode 100644 client-wp/src/resources/behaviors/optional.js create mode 100644 client-wp/src/resources/elements/bootstrap-select.html create mode 100644 client-wp/src/resources/elements/bootstrap-select.js create mode 100644 client-wp/src/resources/elements/bselect.html create mode 100644 client-wp/src/resources/elements/bselect.js create mode 100644 client-wp/src/resources/elements/picker-global-options.js create mode 100644 client-wp/src/resources/elements/util-service.js create mode 100644 client-wp/src/resources/index.js create mode 100644 client-wp/src/resources/value-converters/stringify.js create mode 100644 jsconfig.json create mode 100644 tsconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..67c9e9689 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome", + "url": "http://localhost:9000", + "webRoot": "${workspaceRoot}" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${file}" + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Port", + "address": "localhost", + "port": 5858 + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 720143000..85c80aeb6 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ # Aurelia-Bootstrap-Plugins ### Introduction -`Aurelia-Bootstrap-Plugins` was coded to bridge with a set of commonly used 3rd party Bootstrap addons. The goeal of these Plugins set is to support, as much as possible, the full suite of (`options`, `methods` & `events`) of their original 3rd party addons. +`Aurelia-Bootstrap-Plugins` was coded to bridge with a set of commonly used 3rd party Bootstrap addons. The goal of these Plugins set is to support, as much as possible, the full suite of (`options`, `methods` & `events`) of their original 3rd party addons while being to easily call them in Aurelia. -### Available 3rd party addons +### Available plugins * [Aurelia-Bootstrap-Datetimepicker](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/tree/master/aurelia-bootstrap-datetimepicker) on [NPM](https://www.npmjs.com/package/aurelia-bootstrap-datetimepicker) / source [Eonasdan Bootstrap Datepicker](https://eonasdan.github.io/bootstrap-datetimepicker/) * [Aurelia-Bootstrap-Tagsinput](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/tree/master/aurelia-bootstrap-tagsinput) on [NPM](https://www.npmjs.com/package/aurelia-bootstrap-tagsinput) / source [Bootstrap Tags Input](http://bootstrap-tagsinput.github.io/bootstrap-tagsinput/examples/) +* [Aurelia-Bootstrap-Select](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/tree/master/aurelia-bootstrap-select) on [NPM](https://www.npmjs.com/package/aurelia-bootstrap-select) / source [Bootstrap-Select](http://silviomoreto.github.io/bootstrap-select/) + +### Available plugins (separate module) +* [Aurelia-Slickgrid](https://github.com/ghiscoding/aurelia-slickgrid) on [NPM](https://www.npmjs.com/package/aurelia-slickgrid) / source [Slickgrid](https://github.com/mleibman/SlickGrid) + +### Planned plugins * `Aurelia-Bootstrap-Typeahead` / source [Typeahead.js](http://twitter.github.io/typeahead.js/examples/) * will be available soon, got a working proof of concept with remote/prefetch. -### Planned 3rd party addons -* [Bootstrap Select](http://silviomoreto.github.io/bootstrap-select/) ## Samples A quick Aurelia skeleton was put in place with all the currently available `Aurelia-Bootstrap-Plugins` @@ -32,4 +36,10 @@ npm start ``` ### License -[MIT License](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/LICENSE) \ No newline at end of file +[MIT License](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/LICENSE) + +## Contributions/Comments +Contributions are welcome. This plugin was created to help the community (and myself), if you wish to suggest something and/or want to make a PR (Pull Request), please feel free to do so. + +## Use it, like it? +You like and use an Aurelia-Bootstrap-Plugin, please click on the :star: and spead the word. \ No newline at end of file diff --git a/aurelia-bootstrap-select/.editorconfig b/aurelia-bootstrap-select/.editorconfig new file mode 100644 index 000000000..1033e2df6 --- /dev/null +++ b/aurelia-bootstrap-select/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# 2 space indentation +[**.*] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/aurelia-bootstrap-select/.eslintrc.json b/aurelia-bootstrap-select/.eslintrc.json new file mode 100644 index 000000000..6cbb37da8 --- /dev/null +++ b/aurelia-bootstrap-select/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/aurelia-tools/.eslintrc.json" +} diff --git a/aurelia-bootstrap-select/.gitignore b/aurelia-bootstrap-select/.gitignore new file mode 100644 index 000000000..628d07879 --- /dev/null +++ b/aurelia-bootstrap-select/.gitignore @@ -0,0 +1,6 @@ +node_modules +jspm_packages +bower_components +.idea +.DS_STORE +npm-debug.log diff --git a/aurelia-bootstrap-select/.npmignore b/aurelia-bootstrap-select/.npmignore new file mode 100644 index 000000000..0daa8f078 --- /dev/null +++ b/aurelia-bootstrap-select/.npmignore @@ -0,0 +1,7 @@ +jspm_packages +bower_components +.idea +screenshots +client-cli +client-wp +npm-debug.log diff --git a/aurelia-bootstrap-select/LICENSE b/aurelia-bootstrap-select/LICENSE new file mode 100644 index 000000000..8f9ee73ca --- /dev/null +++ b/aurelia-bootstrap-select/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016, https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins + +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: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +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. \ No newline at end of file diff --git a/aurelia-bootstrap-select/README.md b/aurelia-bootstrap-select/README.md new file mode 100644 index 000000000..d49d8760d --- /dev/null +++ b/aurelia-bootstrap-select/README.md @@ -0,0 +1,374 @@ +# Aurelia-Bootstrap-Select + +### Introduction +An Aurelia Custom Element for the 3rd party addon [Bootstrap-Select](http://silviomoreto.github.io/bootstrap-select/) + + +### Usage +A quick example of the code in action. + +**Note**: We use the `collection.bind` attribute to pass the collection of all select options. + +```html + +``` + + + +### Selected-Value vs Selected-Item + +For conveniencies, we provide 2 bindable attributes (both are `two-way` binding). The first `selected-value` provides a way to pull the index(es) or (option value), while the second binding `selected-item` is giving us the item(s) or most commonly the object(s) selected. + +**Note:** in a collection of strings only, these 2 bindable attributes would have the exact same output and so there isn't much benefit to use both in that particular case. + +Example: + +Given this collection of objects +```javascript +let myCollection = [ + { id: 1, option: 'Ketchup', company: 'Heinz' }, + { id: 2, option: 'Mustard', company: 'French\'s' } +]; +``` + +```html + +``` + +_if the 2nd option is selected, the ouput would be:_ + +```javascript +selected-item // output --> { id: 2, option: 'Mustard', company: 'French\'s' } +selected-value // output --> 2 +``` + + + +### Available Options +Every options of `Bootstrap Select` can be called through `picker-options.bind=""`. For the complete list, please visit the official site [Bootstrap Select - Options](http://silviomoreto.github.io/bootstrap-select/options/). + +**NOTE:** +The picker options can also be defined globally through `main.js` via a `config.options` configuration, see [Global Options](#globaloption) + +Examples + +_from the View_ +```html + +``` + +_from the ViewModel_ +```html + +``` +```javascript +export class Example { + pickerOptions = { + actionsBox: true, + dropupAuto: true + }; +} +``` + + + +### Mapping Data Structure +A default mapping data structure is used by the tool to apply certain styling or do certain action simply by reading a property pulled from the collection (for example, a property `disabled` in the collection can be used to disabled an option from the select). The list of the mapping is the following + +```javascript +mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', // used by optgroup + groupDisabled: 'disabled', // used by optgroup + icon: 'icon', + maxOptions: 'maxOptions', // used by optgroup + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' +} +``` + +Example + +Let say we want to use subtext that is referenced in the collection has `company`, in that case we will want to rename the mapping from `subtext` to `company` (as shown in the example below). + +_from the View, use the `data-mapping-structure` attribute_ +```html + +``` + +_in the ViewModel_ +```javascript +// redefined the subtext mapping as company +let newMapping = { + subtext: 'company' +}; +``` + +**NOTE:** +The mapping data structure can also be defined globally through `main.js` via a `config.extra` configuration, see [Global Options](#globaloption) + + + +### Available Methods/Functions +Again every single methods which comes with `Bootstrap Select` are available. For the complete list, please visit the official site [Bootstrap Select - Functions](http://silviomoreto.github.io/bootstrap-select/methods/). + +To have access to the methods/functions, you will need to expose the element itself through `element.bind` that will then expose the methods _(also note that doing so will also give you access to `events`, `options` and `methods`)_. + +Example + +_View (exposing the element)_ +```html + +``` + +_ViewModel (calling the method)_ +```javascript +export class Example { + @bindable picker; + + pickerChanged() { + this.picker.methods.selectAll(); + } +} +``` + + + +### Extra Methods/Functions +To provide more functionality, we added extra methods that are also exposed the same way as mentioned in previous section [Available Methods](#methods). The list of extra methods is the following: +* disableOptgroupByIndex(index, isDisable = true) +* disableOptgroupByLabel(label, isDisable = true) + +Example + +_ViewModel (calling the method)_ +```javascript +export class Example { + @bindable picker; + + pickerChanged() { + // to disable the optgroup 'Breads', we can call + this.picker.methods.disableOptgroupByLabel('Breads', true); + } +} +``` + + + +### Available Events +Every events of `Bootstrap Select` are, as no surprises, available as well. For the complete list, please visit the official site [Bootstrap Select - Events](http://silviomoreto.github.io/bootstrap-select/options/#events). + +To have access to the `events`, you will need to expose the element itself through `element.bind` to expose the methods _(also note that doing so will also give you access to `events`, `options` and `methods`)_. + +**Note** +The events are called with the syntax of `onEvent` which differs from the original syntax. Example, for the `dp.change`, we would use the `onChange` event. + +Example + +_View (exposing the element)_ +```html + +``` + +_ViewModel (calling the onEvent trigger)_ +```javascript +export class Example { + pickerChanged() { + this.picker.events.onChanged = (e) => console.log('onChanged'); + this.picker.events.onHide = (e) => console.log('onHide'); + this.picker.events.onHidden = (e) => console.log('onHidden'); + this.picker.events.onLoaded = (e) => console.log('onLoaded'); + this.picker.events.onRendered = (e) => console.log('onRendered'); + this.picker.events.onRefreshed = (e) => console.log('onRefreshed'); + this.picker.events.onShow = (e) => console.log('onShow'); + this.picker.events.onShown = (e) => console.log('onShown'); + } +} +``` + +### Disabled +There is multiple `disabled` options available. You can disable any of the following: +* option + * _by a mapping property (refer to [mapping data structure](#mapping)_ +* optgroup + * _by a [mapping](#mapping) property or a [method](#extramethods)_ +* element + * _by adding `disabled` directly on the element_ + +### Multiple (select) +To make the `Bootstrap Select` be a multi-select, simply add the `multiple` attribute to the element. + +**Note** The attribute is used as a boolean attribute, so writing it this way `multiple="false"` will not make it a multi-select. + +### Object-Key (attribute) +When using a collection a objects, the tool will use an attribute called `object-key` (by default is set to `id`) to know which property of the object to do comparison + +Example: + +Given this collection of objects +```javascript +let myCollection = [ + { id: 1, option: 'Ketchup', company: 'Heinz' }, + { id: 2, option: 'Mustard', company: 'French\'s' } +]; + +_on View_ +```html + +``` + +### Optgroup +To have optgroup in your select list, just use the attribute `has-optgroup`. The optgroup will use the default mapping property of `groupLabel` which is `group` (refer to [Mapping Data Structure](#mapping) if you want to change it) + +**Note** The attribute is used as a boolean attribute, so writing it this way `has-optgroup="false"` will not use the optgroup. + +Example: + +Given this collection of objects +```javascript +let myCollection = [ + { id: 1, option: 'Ketchup', group: 'Condiments' }, + { id: 2, option: 'Mustard', group: 'Condiments' }, + { id: 10, option: 'Steam', group: 'Breads' }, + { id: 12. option: 'Toasted', group: 'Breads' }, +]; +``` +_in View, it will automatically use the group property_ +```html + +``` + +## Installation +You can run the examples or build your own by doing the following. + +### Aurelia-CLI / Webpack +```bash +npm install --save aurelia-bootstrap-select +``` + + + +#### Aurelia-CLI +For `CLI` you will need to add (`aurelia-bootstrap-select`) to your `aurelia.json` file. The exported class is `abp-select`. +```javascript +{ + "name": "bootstrap-select", + "path": "../node_modules/bootstrap-select/dist", + "main": "js/bootstrap-select.min", + "resources": [ + "css/bootstrap-select.min.css" + ] +}, +{ + "name": "aurelia-bootstrap-select", + "path": "../node_modules/aurelia-bootstrap-select/dist/amd", + "main": "index", + "resources": ["**/*.{css,html}"] +}, +``` + +_index.html_ +```html + +``` + + + +#### Aurelia-Webpack +`Bootstrap-Select` and possibly others require to have the same `jQuery` accross the bundle. You will need to modify your `webpack.config.babel.js` for this to work correctly. + + +```diff +const ENV... ++ const ProvidePlugin = require('webpack/lib/ProvidePlugin')('webpack/lib/ContextReplacementPlugin') +let config = generateConfig( +{ + entry: { + 'app': ['./src/main' /* this is filled by the aurelia-webpack-plugin */], + 'aurelia-bootstrap': coreBundles.bootstrap, + 'aurelia': coreBundles.aurelia.filter(pkg => coreBundles.bootstrap.indexOf(pkg) === -1) + }, + output: { + path: outDir + }, ++ plugins: [ ++ new ProvidePlugin({ ++ $: "jquery", ++ jQuery: "jquery", ++ 'window.jQuery': 'jquery', ++ 'window.Tether': 'tether', ++ Tether: 'tether' ++ }) ++ ], ++ resolve: { ++ alias: { ++ // Force all modules to use the same jquery version. ++ 'jquery': path.join(__dirname, 'node_modules/jquery/src/jquery') ++ } ++ } +}, +``` + + + +### Aurelia (main.js) +Make the plugin available globally in your `main.js` file. Please note the exported class is `abp-tags-input` + +#### For WebPack only (main.js) +```javascript +import 'bootstrap-select/dist/css/bootstrap-select.min.css'; +``` + +#### CLI/WebPack (main.js) +```javascript +export function configure(aurelia) { + aurelia.use + .standardConfiguration() + .developmentLogging() + .plugin('aurelia-bootstrap-select'); + + aurelia.start().then(() => aurelia.setRoot()); +} +``` + + + +### Global Options +You can change any of the global options directly in the `main.js` through a `config` as shown below: + +```javascript +export function configure(aurelia) { + aurelia.use + .standardConfiguration() + .developmentLogging() + .plugin('aurelia-bootstrap-select', config => { + // extra attributes, with config.extra + config.extra.mappingDataStructure = { + subtext: 'company' + }; + + // or any picker options, with config.options + config.options.width = 'fit'; + }); + + aurelia.start().then(() => aurelia.setRoot()); +} +``` + +## License +[MIT License](https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/blob/master/LICENSE) + +## Contributions/Comments +Contributions are welcome. This plugin was created to help the community (and myself), if you wish to suggest something and/or want to make a PR (Pull Request), please feel free to do so. + +## Use it, like it? +You like and use an Aurelia-Bootstrap-Plugin, please click on the :star: and spead the word. \ No newline at end of file diff --git a/aurelia-bootstrap-select/build/args.js b/aurelia-bootstrap-select/build/args.js new file mode 100644 index 000000000..f905f4858 --- /dev/null +++ b/aurelia-bootstrap-select/build/args.js @@ -0,0 +1,14 @@ +var yargs = require('yargs'); + +var argv = yargs.argv; +var validBumpTypes = 'major|minor|patch|prerelease'.split('|'); +var bump = (argv.bump || 'patch').toLowerCase(); + +if (validBumpTypes.indexOf(bump) === -1) { + throw new Error('Unrecognized bump "' + bump + '".'); +} + +module.exports = { + bump: bump, + depth: parseInt(argv.depth || '0') +}; diff --git a/aurelia-bootstrap-select/build/babel-options.js b/aurelia-bootstrap-select/build/babel-options.js new file mode 100644 index 000000000..b26717dbf --- /dev/null +++ b/aurelia-bootstrap-select/build/babel-options.js @@ -0,0 +1,46 @@ +var path = require('path'); +var paths = require('./paths'); + +exports.base = function() { + return { + filename: '', + filenameRelative: '', + sourceMap: true, + sourceRoot: '', + moduleRoot: path.resolve('src').replace(/\\/g, '/'), + moduleIds: false, + comments: false, + compact: false, + code:true, + presets: [ 'es2015-loose', 'stage-1'], + plugins: [ + 'syntax-flow', + 'transform-decorators-legacy', + 'transform-flow-strip-types' + ] + }; +} + +exports.commonjs = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-commonjs'); + return options; +}; + +exports.amd = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-amd'); + return options; +}; + +exports.system = function() { + var options = exports.base(); + options.plugins.push('transform-es2015-modules-systemjs'); + return options; +}; + +exports.es2015 = function() { + var options = exports.base(); + options.presets = ['stage-1'] + return options; +}; diff --git a/aurelia-bootstrap-select/build/paths.js b/aurelia-bootstrap-select/build/paths.js new file mode 100644 index 000000000..45375febf --- /dev/null +++ b/aurelia-bootstrap-select/build/paths.js @@ -0,0 +1,16 @@ +var appRoot = 'src/'; +var outputRoot = 'dist/'; + +module.exports = { + root: appRoot, + source: appRoot + '**/*.js', + html: appRoot + '**/*.html', + css: appRoot + '**/*.css', + scss: appRoot + 'styles/**/*.scss', + scssRoot: appRoot + '**/*.scss', + style: 'styles/**/*.css', + output: outputRoot, + doc: './doc', + e2eSpecsSrc: 'test/e2e/src/*.js', + e2eSpecsDist: 'test/e2e/dist/' +}; diff --git a/aurelia-bootstrap-select/build/tasks/build.js b/aurelia-bootstrap-select/build/tasks/build.js new file mode 100644 index 000000000..2b312904f --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/build.js @@ -0,0 +1,71 @@ +let gulp = require('gulp'); +let runSequence = require('run-sequence'); +let to5 = require('gulp-babel'); +let paths = require('../paths'); +let sass = require('gulp-sass'); +let compilerOptions = require('../babel-options'); +let assign = Object.assign || require('object.assign'); + +gulp.task('build-html', function() { + return gulp.src(paths.html) + .pipe(gulp.dest(paths.output + 'es2015')) + .pipe(gulp.dest(paths.output + 'commonjs')) + .pipe(gulp.dest(paths.output + 'amd')) + .pipe(gulp.dest(paths.output + 'system')); +}); + +gulp.task('build-css', function() { + return gulp.src(paths.css) + .pipe(gulp.dest(paths.output + 'es2015')) + .pipe(gulp.dest(paths.output + 'commonjs')) + .pipe(gulp.dest(paths.output + 'amd')) + .pipe(gulp.dest(paths.output + 'system')); +}); + +gulp.task('build-scss', function() { + return gulp.src(paths.scss) + .pipe(gulp.dest(paths.output + 'styles/sass')) + .pipe(sass().on('error', sass.logError)) + .pipe(gulp.dest(paths.output + 'styles/css')); +}); + +gulp.task('build-scss-root', function() { + return gulp.src(paths.scssRoot) + .pipe(sass().on('error', sass.logError)) + .pipe(gulp.dest(paths.output + 'es2015')) + .pipe(gulp.dest(paths.output + 'commonjs')) + .pipe(gulp.dest(paths.output + 'amd')) + .pipe(gulp.dest(paths.output + 'system')); +}); + +gulp.task('build-es2015', function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.es2015()))) + .pipe(gulp.dest(paths.output + 'es2015')); +}); + +gulp.task('build-commonjs', function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.commonjs()))) + .pipe(gulp.dest(paths.output + 'commonjs')); +}); + +gulp.task('build-amd', function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.amd()))) + .pipe(gulp.dest(paths.output + 'amd')); +}); + +gulp.task('build-system', function() { + return gulp.src(paths.source) + .pipe(to5(assign({}, compilerOptions.system()))) + .pipe(gulp.dest(paths.output + 'system')); +}); + +gulp.task('build', function(callback) { + return runSequence( + 'clean', + ['build-html', 'build-scss', 'build-scss-root', 'build-css', 'build-es2015', 'build-commonjs', 'build-amd', 'build-system'], + callback + ); +}); diff --git a/aurelia-bootstrap-select/build/tasks/clean.js b/aurelia-bootstrap-select/build/tasks/clean.js new file mode 100644 index 000000000..897eed38e --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/clean.js @@ -0,0 +1,10 @@ +var gulp = require('gulp'); +var paths = require('../paths'); +var del = require('del'); +var vinylPaths = require('vinyl-paths'); + +// deletes all files in the output path +gulp.task('clean', function() { + return gulp.src([paths.output]) + .pipe(vinylPaths(del)); +}); diff --git a/aurelia-bootstrap-select/build/tasks/dev.js b/aurelia-bootstrap-select/build/tasks/dev.js new file mode 100644 index 000000000..8fb9c76ff --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/dev.js @@ -0,0 +1,21 @@ +var gulp = require('gulp'); +var tools = require('aurelia-tools'); +var args = require('../args'); + +// source code for the tasks called in this file +// is located at: https://github.com/aurelia/tools/blob/master/src/dev.js + +// updates dependencies in this folder +// from folders in the parent directory +gulp.task('update-own-deps', function() { + tools.updateOwnDependenciesFromLocalRepositories(args.depth); +}); + +// quickly pulls in all of the aurelia +// github repos, placing them up one directory +// from where the command is executed, +// then runs `npm install` +// and `gulp build` for each repo +gulp.task('build-dev-env', function() { + tools.buildDevEnv(); +}); diff --git a/aurelia-bootstrap-select/build/tasks/doc.js b/aurelia-bootstrap-select/build/tasks/doc.js new file mode 100644 index 000000000..ea027cb4c --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/doc.js @@ -0,0 +1,14 @@ +var gulp = require('gulp'); +var tools = require('aurelia-tools'); +var paths = require('../paths'); +var yuidoc = require('gulp-yuidoc'); + +gulp.task('doc-generate', function(){ + return gulp.src(paths.source) + .pipe(yuidoc.parser(null, 'api.json')) + .pipe(gulp.dest(paths.doc)); +}); + +gulp.task('doc', ['doc-generate'], function(){ + tools.transformAPIModel(paths.doc); +}); diff --git a/aurelia-bootstrap-select/build/tasks/lint.js b/aurelia-bootstrap-select/build/tasks/lint.js new file mode 100644 index 000000000..e758acc7b --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/lint.js @@ -0,0 +1,11 @@ +var gulp = require('gulp'); +var paths = require('../paths'); +var eslint = require('gulp-eslint'); + +// runs eslint on all .js files +gulp.task('lint', function() { + return gulp.src(paths.source) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failOnError()); +}); diff --git a/aurelia-bootstrap-select/build/tasks/prepare-release.js b/aurelia-bootstrap-select/build/tasks/prepare-release.js new file mode 100644 index 000000000..cc85a41e5 --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/prepare-release.js @@ -0,0 +1,40 @@ +var gulp = require('gulp'); +var runSequence = require('run-sequence'); +var paths = require('../paths'); +var changelog = require('conventional-changelog'); +var fs = require('fs'); +var bump = require('gulp-bump'); +var args = require('../args'); + +// utilizes the bump plugin to bump the +// semver for the repo +gulp.task('bump-version', function() { + return gulp.src(['./package.json']) + .pipe(bump({type: args.bump})) //major|minor|patch|prerelease + .pipe(gulp.dest('./')); +}); + +// generates the CHANGELOG.md file based on commit +// from git commit messages +gulp.task('changelog', function(callback) { + var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); + + return changelog({ + repository: pkg.repository.url, + version: pkg.version, + file: paths.doc + '/CHANGELOG.md' + }, function(err, log) { + fs.writeFileSync(paths.doc + '/CHANGELOG.md', log); + }); +}); + +// calls the listed sequence of tasks in order +gulp.task('prepare-release', function(callback) { + return runSequence( + 'build', + //'lint', + 'bump-version', + 'changelog', + callback + ); +}); diff --git a/aurelia-bootstrap-select/build/tasks/test.js b/aurelia-bootstrap-select/build/tasks/test.js new file mode 100644 index 000000000..4e3f6a5de --- /dev/null +++ b/aurelia-bootstrap-select/build/tasks/test.js @@ -0,0 +1,49 @@ +var gulp = require('gulp'); +var Karma = require('karma').Server; + +/** + * Run test once and exit + */ +gulp.task('test', function(done) { + new Karma({ + configFile: __dirname + '/../../karma.conf.js', + singleRun: true + }, done).start(); +}); + +/** + * Watch for file changes and re-run tests on each change + */ +gulp.task('tdd', function(done) { + new Karma({ + configFile: __dirname + '/../../karma.conf.js' + }, done).start(); +}); + +/** + * Run test once with code coverage and exit + */ +gulp.task('cover', function(done) { + new Karma({ + configFile: __dirname + '/../../karma.conf.js', + singleRun: true, + reporters: ['coverage'], + preprocessors: { + 'test/**/*.js': ['babel'], + 'src/**/*.js': ['babel', 'coverage'] + }, + coverageReporter: { + includeAllSources: true, + instrumenters: { + isparta: require('isparta') + }, + instrumenter: { + 'src/**/*.js': 'isparta' + }, + reporters: [ + { type: 'html', dir: 'coverage' }, + { type: 'text' } + ] + } + }, done).start(); +}); diff --git a/aurelia-bootstrap-select/config.js b/aurelia-bootstrap-select/config.js new file mode 100644 index 000000000..1d93b7045 --- /dev/null +++ b/aurelia-bootstrap-select/config.js @@ -0,0 +1,15 @@ +System.config({ + defaultJSExtensions: true, + transpiler: false, + paths: { + "github:*": "jspm_packages/github/*", + "npm:*": "jspm_packages/npm/*" + }, + + map: { + "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0", + "npm:aurelia-polyfills@1.0.0": { + "aurelia-pal": "npm:aurelia-pal@1.0.0" + } + } +}); diff --git a/aurelia-bootstrap-select/dist/amd/abp-select.html b/aurelia-bootstrap-select/dist/amd/abp-select.html new file mode 100644 index 000000000..ea1d663c8 --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/abp-select.html @@ -0,0 +1,45 @@ + diff --git a/aurelia-bootstrap-select/dist/amd/abp-select.js b/aurelia-bootstrap-select/dist/amd/abp-select.js new file mode 100644 index 000000000..fcc0b2d56 --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/abp-select.js @@ -0,0 +1,570 @@ +define(['exports', 'aurelia-framework', './util-service', 'jquery', './picker-global-options', 'bootstrap-select'], function (exports, _aureliaFramework, _utilService, _jquery, _pickerGlobalOptions) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.OptionalBindingBehavior = exports.AbpSelectCustomElement = undefined; + + var _jquery2 = _interopRequireDefault(_jquery); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + function _initDefineProp(target, property, descriptor, context) { + if (!descriptor) return; + Object.defineProperty(target, property, { + enumerable: descriptor.enumerable, + configurable: descriptor.configurable, + writable: descriptor.writable, + value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 + }); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { + var desc = {}; + Object['ke' + 'ys'](descriptor).forEach(function (key) { + desc[key] = descriptor[key]; + }); + desc.enumerable = !!desc.enumerable; + desc.configurable = !!desc.configurable; + + if ('value' in desc || desc.initializer) { + desc.writable = true; + } + + desc = decorators.slice().reverse().reduce(function (desc, decorator) { + return decorator(target, property, desc) || desc; + }, desc); + + if (context && desc.initializer !== void 0) { + desc.value = desc.initializer ? desc.initializer.call(context) : void 0; + desc.initializer = undefined; + } + + if (desc.initializer === void 0) { + Object['define' + 'Property'](target, property, desc); + desc = null; + } + + return desc; + } + + function _initializerWarningHelper(descriptor, context) { + throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.'); + } + + var _dec, _dec2, _dec3, _dec4, _class, _desc, _value, _class2, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _descriptor7, _descriptor8, _descriptor9, _descriptor10, _descriptor11, _descriptor12, _descriptor13, _descriptor14, _descriptor15, _descriptor16, _descriptor17, _descriptor18, _descriptor19, _descriptor20, _descriptor21, _descriptor22; + + var AbpSelectCustomElement = exports.AbpSelectCustomElement = (_dec = (0, _aureliaFramework.inject)(Element, _utilService.UtilService), _dec2 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec3 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec4 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec(_class = (_class2 = function () { + function AbpSelectCustomElement(elm, utilService) { + _classCallCheck(this, AbpSelectCustomElement); + + _initDefineProp(this, 'element', _descriptor, this); + + _initDefineProp(this, 'selectedItem', _descriptor2, this); + + _initDefineProp(this, 'selectedValue', _descriptor3, this); + + _initDefineProp(this, 'class', _descriptor4, this); + + _initDefineProp(this, 'collection', _descriptor5, this); + + _initDefineProp(this, 'dataMappingStructure', _descriptor6, this); + + _initDefineProp(this, 'disabled', _descriptor7, this); + + _initDefineProp(this, 'emptyOnNull', _descriptor8, this); + + _initDefineProp(this, 'hasOptgroup', _descriptor9, this); + + _initDefineProp(this, 'multiple', _descriptor10, this); + + _initDefineProp(this, 'objectKey', _descriptor11, this); + + _initDefineProp(this, 'pickerOptions', _descriptor12, this); + + _initDefineProp(this, 'placeholder', _descriptor13, this); + + _initDefineProp(this, 'selected', _descriptor14, this); + + _initDefineProp(this, 'onChanged', _descriptor15, this); + + _initDefineProp(this, 'onHide', _descriptor16, this); + + _initDefineProp(this, 'onHidden', _descriptor17, this); + + _initDefineProp(this, 'onLoaded', _descriptor18, this); + + _initDefineProp(this, 'onRendered', _descriptor19, this); + + _initDefineProp(this, 'onRefreshed', _descriptor20, this); + + _initDefineProp(this, 'onShow', _descriptor21, this); + + _initDefineProp(this, 'onShown', _descriptor22, this); + + this.elm = elm; + this.util = utilService; + } + + AbpSelectCustomElement.prototype.attached = function attached() { + this.domElm = (0, _jquery2.default)(this.elm).find('.selectpicker'); + + var events = this.applyExposeEvents(); + var methods = this.exposeMethods(); + + var pickerOptions = Object.assign({}, _pickerGlobalOptions.globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + }; + + AbpSelectCustomElement.prototype.bind = function bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + var originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + var originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + }; + + AbpSelectCustomElement.prototype.applyExposeEvents = function applyExposeEvents() { + var _this = this; + + var events = {}; + + this.domElm.on('show.bs.select', function (e) { + if (typeof _this.onShow === 'function') { + _this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', function (e) { + if (typeof _this.onShown === 'function') { + _this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', function (e) { + if (typeof _this.onHide === 'function') { + _this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', function (e) { + if (typeof _this.onHidden === 'function') { + _this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', function (e) { + if (typeof _this.onLoaded === 'function') { + _this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', function (e) { + if (typeof _this.onRendered === 'function') { + _this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', function (e) { + if (typeof _this.onRefreshed === 'function') { + _this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + if (typeof _this.onChanged === 'function') { + _this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + }; + + AbpSelectCustomElement.prototype.exposeMethods = function exposeMethods() { + var _this2 = this; + + var methods = { + deselectAll: function deselectAll() { + return _this2.domElm.selectpicker('deselectAll'); + }, + destroy: function destroy() { + return _this2.domElm.selectpicker('destroy'); + }, + disableOptgroupByIndex: function disableOptgroupByIndex(index) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (_this2.domElm.find('optgroup')[index]) { + _this2.domElm.find('optgroup')[index].prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + } + }, + disableOptgroupByLabel: function disableOptgroupByLabel(label) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + _this2.domElm.find('optgroup[label=' + label + ']').prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + }, + mobile: function mobile() { + return _this2.domElm.selectpicker('mobile'); + }, + refresh: function refresh() { + return _this2.domElm.selectpicker('refresh'); + }, + render: function render() { + return _this2.domElm.selectpicker('render'); + }, + val: function val(value) { + return _this2.domElm.selectpicker('val', value); + }, + selectAll: function selectAll() { + return _this2.domElm.selectpicker('selectAll'); + }, + setStyle: function setStyle(style) { + var isAddingTheClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (style.includes('btn')) { + var action = isAddingTheClass ? 'add' : 'remove'; + _this2.domElm.selectpicker('setStyle', style, action); + } else { + _this2.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + }; + + AbpSelectCustomElement.prototype.detached = function detached() { + this.domElm.selectpicker('destroy'); + }; + + AbpSelectCustomElement.prototype.getGroupedCollection = function getGroupedCollection() { + var groupingPropName = this.getMappingProperty('groupLabel'); + var collectionGroupedAsObject = this.collection.reduce(function (groups, y) { + var key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + return Object.keys(collectionGroupedAsObject).map(function (k) { + return collectionGroupedAsObject[k]; + }); + }; + + AbpSelectCustomElement.prototype.getMappingProperty = function getMappingProperty(type) { + var dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValueFromIndex = function getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValue = function getMappingPropertyValue(inputArray, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMergedMappingStructure = function getMergedMappingStructure() { + var dataMappingStructure = Object.assign({}, _pickerGlobalOptions.globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + }; + + AbpSelectCustomElement.prototype.findItems = function findItems(collection, newValue, objectKey) { + var _this3 = this; + + var foundItems = []; + var searchingItems = []; + var selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + var _loop = function _loop() { + if (_isArray) { + if (_i >= _iterator.length) return 'break'; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) return 'break'; + _ref = _i.value; + } + + var searchItem = _ref; + + var searchFilter = _this3.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + var foundItem = collection.find(function (item) { + return _this3.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(_this3.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + }; + + for (var _iterator = searchingItems, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + var _ret = _loop(); + + if (_ret === 'break') break; + } + + return selection; + }; + + AbpSelectCustomElement.prototype.isEmptySelection = function isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + }; + + AbpSelectCustomElement.prototype.isNotEmptySelection = function isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + }; + + AbpSelectCustomElement.prototype.isSelected = function isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + }; + + AbpSelectCustomElement.prototype.renderSelection = function renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + }; + + AbpSelectCustomElement.prototype.selectedItemChanged = function selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]; + } + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.selectedValueChanged = function selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + this.selectedItem = selection.items.length > 0 ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.watchOnLoadedToRenderPreSelection = function watchOnLoadedToRenderPreSelection() { + var _this4 = this; + + this.domElm.on('loaded.bs.select', function (e) { + var newValue = _this4._originalSelectedIndexes || _this4._originalSelectedObjects; + var selection = _this4.findItems(_this4.collection, newValue, _this4.objectKey); + if (selection.indexes) { + _this4.selectedValue = selection.indexes; + } else { + _this4.selectedValue = _this4.util.isObject(_this4.collection[0]) ? _this4.collection[0][_this4.objectKey] : _this4.collection[0]; + } + _this4.selectedItem = selection.items ? selection.items : _this4.collection[0]; + _this4.renderSelection(selection); + }); + }; + + AbpSelectCustomElement.prototype.watchOnChangedToUpdateValueAndItemObjects = function watchOnChangedToUpdateValueAndItemObjects() { + var _this5 = this; + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + _this5.selectedValue = _this5.domElm.selectpicker('val'); + var selection = _this5.findItems(_this5.collection, _this5.selectedValue, _this5.objectKey); + if (selection.indexes) { + _this5.domElm.selectpicker('val', selection.indexes); + } + + _this5.selectedValue = selection.indexes; + _this5.selectedItem = selection.items; + }); + }; + + return AbpSelectCustomElement; + }(), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, 'element', [_dec2], { + enumerable: true, + initializer: null + }), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, 'selectedItem', [_dec3], { + enumerable: true, + initializer: null + }), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, 'selectedValue', [_dec4], { + enumerable: true, + initializer: null + }), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, 'class', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, 'collection', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return []; + } + }), _descriptor6 = _applyDecoratedDescriptor(_class2.prototype, 'dataMappingStructure', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor7 = _applyDecoratedDescriptor(_class2.prototype, 'disabled', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor8 = _applyDecoratedDescriptor(_class2.prototype, 'emptyOnNull', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor9 = _applyDecoratedDescriptor(_class2.prototype, 'hasOptgroup', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor10 = _applyDecoratedDescriptor(_class2.prototype, 'multiple', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor11 = _applyDecoratedDescriptor(_class2.prototype, 'objectKey', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return 'id'; + } + }), _descriptor12 = _applyDecoratedDescriptor(_class2.prototype, 'pickerOptions', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor13 = _applyDecoratedDescriptor(_class2.prototype, 'placeholder', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor14 = _applyDecoratedDescriptor(_class2.prototype, 'selected', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor15 = _applyDecoratedDescriptor(_class2.prototype, 'onChanged', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor16 = _applyDecoratedDescriptor(_class2.prototype, 'onHide', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor17 = _applyDecoratedDescriptor(_class2.prototype, 'onHidden', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor18 = _applyDecoratedDescriptor(_class2.prototype, 'onLoaded', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor19 = _applyDecoratedDescriptor(_class2.prototype, 'onRendered', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor20 = _applyDecoratedDescriptor(_class2.prototype, 'onRefreshed', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor21 = _applyDecoratedDescriptor(_class2.prototype, 'onShow', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + }), _descriptor22 = _applyDecoratedDescriptor(_class2.prototype, 'onShown', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null + })), _class2)) || _class); + + var OptionalBindingBehavior = exports.OptionalBindingBehavior = function () { + function OptionalBindingBehavior() { + _classCallCheck(this, OptionalBindingBehavior); + } + + OptionalBindingBehavior.prototype.bind = function bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = function (val) { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + }; + + OptionalBindingBehavior.prototype.unbind = function unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + }; + + return OptionalBindingBehavior; + }(); +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/amd/index.js b/aurelia-bootstrap-select/dist/amd/index.js new file mode 100644 index 000000000..3d6a4239a --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/index.js @@ -0,0 +1,21 @@ +define(['exports', './abp-select', './picker-config'], function (exports, _abpSelect, _pickerConfig) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.PickerConfig = exports.AbpSelectCustomElement = undefined; + exports.configure = configure; + function configure(aurelia, callback) { + aurelia.globalResources('./abp-select'); + + var config = new _pickerConfig.PickerConfig(); + + if (typeof callback === 'function') { + callback(config); + } + } + + exports.AbpSelectCustomElement = _abpSelect.AbpSelectCustomElement; + exports.PickerConfig = _pickerConfig.PickerConfig; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/amd/picker-config.js b/aurelia-bootstrap-select/dist/amd/picker-config.js new file mode 100644 index 000000000..d16a9c43f --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/picker-config.js @@ -0,0 +1,21 @@ +define(['exports', './picker-global-options'], function (exports, _pickerGlobalOptions) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + exports.PickerConfig = undefined; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var PickerConfig = exports.PickerConfig = function PickerConfig() { + _classCallCheck(this, PickerConfig); + + this.extra = _pickerGlobalOptions.globalExtraOptions; + this.options = _pickerGlobalOptions.globalPickerOptions; + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/amd/picker-global-options.js b/aurelia-bootstrap-select/dist/amd/picker-global-options.js new file mode 100644 index 000000000..265a073a6 --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/picker-global-options.js @@ -0,0 +1,30 @@ +define(['exports'], function (exports) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + var globalExtraOptions = exports.globalExtraOptions = { + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } + }; + + var globalPickerOptions = exports.globalPickerOptions = { + dropupAuto: true, + showTick: true, + width: 'auto' + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/amd/util-service.js b/aurelia-bootstrap-select/dist/amd/util-service.js new file mode 100644 index 000000000..5c1b7728a --- /dev/null +++ b/aurelia-bootstrap-select/dist/amd/util-service.js @@ -0,0 +1,66 @@ +define(['exports'], function (exports) { + 'use strict'; + + Object.defineProperty(exports, "__esModule", { + value: true + }); + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var UtilService = exports.UtilService = function () { + function UtilService() { + _classCallCheck(this, UtilService); + } + + UtilService.prototype.isArrayEqual = function isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + }; + + UtilService.prototype.isEqual = function isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + }; + + UtilService.prototype.isObjectArray = function isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && _typeof(inputArrray[0]) === 'object'; + }; + + UtilService.prototype.isObject = function isObject(arg) { + return (typeof arg === 'undefined' ? 'undefined' : _typeof(arg)) === 'object'; + }; + + UtilService.prototype.isString = function isString(arg) { + return typeof arg === 'string' || arg instanceof String; + }; + + UtilService.prototype.isStringArray = function isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + }; + + UtilService.prototype.parseBool = function parseBool(value) { + return (/^(true|1)$/i.test(value) + ); + }; + + return UtilService; + }(); +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/commonjs/abp-select.html b/aurelia-bootstrap-select/dist/commonjs/abp-select.html new file mode 100644 index 000000000..ea1d663c8 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/abp-select.html @@ -0,0 +1,45 @@ + diff --git a/aurelia-bootstrap-select/dist/commonjs/abp-select.js b/aurelia-bootstrap-select/dist/commonjs/abp-select.js new file mode 100644 index 000000000..a73fa2a62 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/abp-select.js @@ -0,0 +1,570 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.OptionalBindingBehavior = exports.AbpSelectCustomElement = undefined; + +var _dec, _dec2, _dec3, _dec4, _class, _desc, _value, _class2, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _descriptor7, _descriptor8, _descriptor9, _descriptor10, _descriptor11, _descriptor12, _descriptor13, _descriptor14, _descriptor15, _descriptor16, _descriptor17, _descriptor18, _descriptor19, _descriptor20, _descriptor21, _descriptor22; + +var _aureliaFramework = require('aurelia-framework'); + +var _utilService = require('./util-service'); + +var _jquery = require('jquery'); + +var _jquery2 = _interopRequireDefault(_jquery); + +require('bootstrap-select'); + +var _pickerGlobalOptions = require('./picker-global-options'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _initDefineProp(target, property, descriptor, context) { + if (!descriptor) return; + Object.defineProperty(target, property, { + enumerable: descriptor.enumerable, + configurable: descriptor.configurable, + writable: descriptor.writable, + value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 + }); +} + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { + var desc = {}; + Object['ke' + 'ys'](descriptor).forEach(function (key) { + desc[key] = descriptor[key]; + }); + desc.enumerable = !!desc.enumerable; + desc.configurable = !!desc.configurable; + + if ('value' in desc || desc.initializer) { + desc.writable = true; + } + + desc = decorators.slice().reverse().reduce(function (desc, decorator) { + return decorator(target, property, desc) || desc; + }, desc); + + if (context && desc.initializer !== void 0) { + desc.value = desc.initializer ? desc.initializer.call(context) : void 0; + desc.initializer = undefined; + } + + if (desc.initializer === void 0) { + Object['define' + 'Property'](target, property, desc); + desc = null; + } + + return desc; +} + +function _initializerWarningHelper(descriptor, context) { + throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.'); +} + +var AbpSelectCustomElement = exports.AbpSelectCustomElement = (_dec = (0, _aureliaFramework.inject)(Element, _utilService.UtilService), _dec2 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec3 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec4 = (0, _aureliaFramework.bindable)({ defaultBindingMode: _aureliaFramework.bindingMode.twoWay }), _dec(_class = (_class2 = function () { + function AbpSelectCustomElement(elm, utilService) { + _classCallCheck(this, AbpSelectCustomElement); + + _initDefineProp(this, 'element', _descriptor, this); + + _initDefineProp(this, 'selectedItem', _descriptor2, this); + + _initDefineProp(this, 'selectedValue', _descriptor3, this); + + _initDefineProp(this, 'class', _descriptor4, this); + + _initDefineProp(this, 'collection', _descriptor5, this); + + _initDefineProp(this, 'dataMappingStructure', _descriptor6, this); + + _initDefineProp(this, 'disabled', _descriptor7, this); + + _initDefineProp(this, 'emptyOnNull', _descriptor8, this); + + _initDefineProp(this, 'hasOptgroup', _descriptor9, this); + + _initDefineProp(this, 'multiple', _descriptor10, this); + + _initDefineProp(this, 'objectKey', _descriptor11, this); + + _initDefineProp(this, 'pickerOptions', _descriptor12, this); + + _initDefineProp(this, 'placeholder', _descriptor13, this); + + _initDefineProp(this, 'selected', _descriptor14, this); + + _initDefineProp(this, 'onChanged', _descriptor15, this); + + _initDefineProp(this, 'onHide', _descriptor16, this); + + _initDefineProp(this, 'onHidden', _descriptor17, this); + + _initDefineProp(this, 'onLoaded', _descriptor18, this); + + _initDefineProp(this, 'onRendered', _descriptor19, this); + + _initDefineProp(this, 'onRefreshed', _descriptor20, this); + + _initDefineProp(this, 'onShow', _descriptor21, this); + + _initDefineProp(this, 'onShown', _descriptor22, this); + + this.elm = elm; + this.util = utilService; + } + + AbpSelectCustomElement.prototype.attached = function attached() { + this.domElm = (0, _jquery2.default)(this.elm).find('.selectpicker'); + + var events = this.applyExposeEvents(); + var methods = this.exposeMethods(); + + var pickerOptions = Object.assign({}, _pickerGlobalOptions.globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + }; + + AbpSelectCustomElement.prototype.bind = function bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + var originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + var originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + }; + + AbpSelectCustomElement.prototype.applyExposeEvents = function applyExposeEvents() { + var _this = this; + + var events = {}; + + this.domElm.on('show.bs.select', function (e) { + if (typeof _this.onShow === 'function') { + _this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', function (e) { + if (typeof _this.onShown === 'function') { + _this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', function (e) { + if (typeof _this.onHide === 'function') { + _this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', function (e) { + if (typeof _this.onHidden === 'function') { + _this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', function (e) { + if (typeof _this.onLoaded === 'function') { + _this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', function (e) { + if (typeof _this.onRendered === 'function') { + _this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', function (e) { + if (typeof _this.onRefreshed === 'function') { + _this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + if (typeof _this.onChanged === 'function') { + _this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + }; + + AbpSelectCustomElement.prototype.exposeMethods = function exposeMethods() { + var _this2 = this; + + var methods = { + deselectAll: function deselectAll() { + return _this2.domElm.selectpicker('deselectAll'); + }, + destroy: function destroy() { + return _this2.domElm.selectpicker('destroy'); + }, + disableOptgroupByIndex: function disableOptgroupByIndex(index) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (_this2.domElm.find('optgroup')[index]) { + _this2.domElm.find('optgroup')[index].prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + } + }, + disableOptgroupByLabel: function disableOptgroupByLabel(label) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + _this2.domElm.find('optgroup[label=' + label + ']').prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + }, + mobile: function mobile() { + return _this2.domElm.selectpicker('mobile'); + }, + refresh: function refresh() { + return _this2.domElm.selectpicker('refresh'); + }, + render: function render() { + return _this2.domElm.selectpicker('render'); + }, + val: function val(value) { + return _this2.domElm.selectpicker('val', value); + }, + selectAll: function selectAll() { + return _this2.domElm.selectpicker('selectAll'); + }, + setStyle: function setStyle(style) { + var isAddingTheClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (style.includes('btn')) { + var action = isAddingTheClass ? 'add' : 'remove'; + _this2.domElm.selectpicker('setStyle', style, action); + } else { + _this2.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + }; + + AbpSelectCustomElement.prototype.detached = function detached() { + this.domElm.selectpicker('destroy'); + }; + + AbpSelectCustomElement.prototype.getGroupedCollection = function getGroupedCollection() { + var groupingPropName = this.getMappingProperty('groupLabel'); + var collectionGroupedAsObject = this.collection.reduce(function (groups, y) { + var key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + return Object.keys(collectionGroupedAsObject).map(function (k) { + return collectionGroupedAsObject[k]; + }); + }; + + AbpSelectCustomElement.prototype.getMappingProperty = function getMappingProperty(type) { + var dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValueFromIndex = function getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValue = function getMappingPropertyValue(inputArray, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMergedMappingStructure = function getMergedMappingStructure() { + var dataMappingStructure = Object.assign({}, _pickerGlobalOptions.globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + }; + + AbpSelectCustomElement.prototype.findItems = function findItems(collection, newValue, objectKey) { + var _this3 = this; + + var foundItems = []; + var searchingItems = []; + var selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + var _loop = function _loop() { + if (_isArray) { + if (_i >= _iterator.length) return 'break'; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) return 'break'; + _ref = _i.value; + } + + var searchItem = _ref; + + var searchFilter = _this3.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + var foundItem = collection.find(function (item) { + return _this3.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(_this3.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + }; + + for (var _iterator = searchingItems, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + var _ret = _loop(); + + if (_ret === 'break') break; + } + + return selection; + }; + + AbpSelectCustomElement.prototype.isEmptySelection = function isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + }; + + AbpSelectCustomElement.prototype.isNotEmptySelection = function isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + }; + + AbpSelectCustomElement.prototype.isSelected = function isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + }; + + AbpSelectCustomElement.prototype.renderSelection = function renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + }; + + AbpSelectCustomElement.prototype.selectedItemChanged = function selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]; + } + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.selectedValueChanged = function selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + this.selectedItem = selection.items.length > 0 ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.watchOnLoadedToRenderPreSelection = function watchOnLoadedToRenderPreSelection() { + var _this4 = this; + + this.domElm.on('loaded.bs.select', function (e) { + var newValue = _this4._originalSelectedIndexes || _this4._originalSelectedObjects; + var selection = _this4.findItems(_this4.collection, newValue, _this4.objectKey); + if (selection.indexes) { + _this4.selectedValue = selection.indexes; + } else { + _this4.selectedValue = _this4.util.isObject(_this4.collection[0]) ? _this4.collection[0][_this4.objectKey] : _this4.collection[0]; + } + _this4.selectedItem = selection.items ? selection.items : _this4.collection[0]; + _this4.renderSelection(selection); + }); + }; + + AbpSelectCustomElement.prototype.watchOnChangedToUpdateValueAndItemObjects = function watchOnChangedToUpdateValueAndItemObjects() { + var _this5 = this; + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + _this5.selectedValue = _this5.domElm.selectpicker('val'); + var selection = _this5.findItems(_this5.collection, _this5.selectedValue, _this5.objectKey); + if (selection.indexes) { + _this5.domElm.selectpicker('val', selection.indexes); + } + + _this5.selectedValue = selection.indexes; + _this5.selectedItem = selection.items; + }); + }; + + return AbpSelectCustomElement; +}(), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, 'element', [_dec2], { + enumerable: true, + initializer: null +}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, 'selectedItem', [_dec3], { + enumerable: true, + initializer: null +}), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, 'selectedValue', [_dec4], { + enumerable: true, + initializer: null +}), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, 'class', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, 'collection', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return []; + } +}), _descriptor6 = _applyDecoratedDescriptor(_class2.prototype, 'dataMappingStructure', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor7 = _applyDecoratedDescriptor(_class2.prototype, 'disabled', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } +}), _descriptor8 = _applyDecoratedDescriptor(_class2.prototype, 'emptyOnNull', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } +}), _descriptor9 = _applyDecoratedDescriptor(_class2.prototype, 'hasOptgroup', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } +}), _descriptor10 = _applyDecoratedDescriptor(_class2.prototype, 'multiple', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } +}), _descriptor11 = _applyDecoratedDescriptor(_class2.prototype, 'objectKey', [_aureliaFramework.bindable], { + enumerable: true, + initializer: function initializer() { + return 'id'; + } +}), _descriptor12 = _applyDecoratedDescriptor(_class2.prototype, 'pickerOptions', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor13 = _applyDecoratedDescriptor(_class2.prototype, 'placeholder', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor14 = _applyDecoratedDescriptor(_class2.prototype, 'selected', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor15 = _applyDecoratedDescriptor(_class2.prototype, 'onChanged', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor16 = _applyDecoratedDescriptor(_class2.prototype, 'onHide', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor17 = _applyDecoratedDescriptor(_class2.prototype, 'onHidden', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor18 = _applyDecoratedDescriptor(_class2.prototype, 'onLoaded', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor19 = _applyDecoratedDescriptor(_class2.prototype, 'onRendered', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor20 = _applyDecoratedDescriptor(_class2.prototype, 'onRefreshed', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor21 = _applyDecoratedDescriptor(_class2.prototype, 'onShow', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +}), _descriptor22 = _applyDecoratedDescriptor(_class2.prototype, 'onShown', [_aureliaFramework.bindable], { + enumerable: true, + initializer: null +})), _class2)) || _class); + +var OptionalBindingBehavior = exports.OptionalBindingBehavior = function () { + function OptionalBindingBehavior() { + _classCallCheck(this, OptionalBindingBehavior); + } + + OptionalBindingBehavior.prototype.bind = function bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = function (val) { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + }; + + OptionalBindingBehavior.prototype.unbind = function unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + }; + + return OptionalBindingBehavior; +}(); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/commonjs/index.js b/aurelia-bootstrap-select/dist/commonjs/index.js new file mode 100644 index 000000000..bb1b51618 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/index.js @@ -0,0 +1,24 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.PickerConfig = exports.AbpSelectCustomElement = undefined; +exports.configure = configure; + +var _abpSelect = require('./abp-select'); + +var _pickerConfig = require('./picker-config'); + +function configure(aurelia, callback) { + aurelia.globalResources('./abp-select'); + + var config = new _pickerConfig.PickerConfig(); + + if (typeof callback === 'function') { + callback(config); + } +} + +exports.AbpSelectCustomElement = _abpSelect.AbpSelectCustomElement; +exports.PickerConfig = _pickerConfig.PickerConfig; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/commonjs/picker-config.js b/aurelia-bootstrap-select/dist/commonjs/picker-config.js new file mode 100644 index 000000000..4fd0c1a27 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/picker-config.js @@ -0,0 +1,17 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.PickerConfig = undefined; + +var _pickerGlobalOptions = require('./picker-global-options'); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var PickerConfig = exports.PickerConfig = function PickerConfig() { + _classCallCheck(this, PickerConfig); + + this.extra = _pickerGlobalOptions.globalExtraOptions; + this.options = _pickerGlobalOptions.globalPickerOptions; +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/commonjs/picker-global-options.js b/aurelia-bootstrap-select/dist/commonjs/picker-global-options.js new file mode 100644 index 000000000..501b501b3 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/picker-global-options.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var globalExtraOptions = exports.globalExtraOptions = { + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } +}; + +var globalPickerOptions = exports.globalPickerOptions = { + dropupAuto: true, + showTick: true, + width: 'auto' +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/commonjs/util-service.js b/aurelia-bootstrap-select/dist/commonjs/util-service.js new file mode 100644 index 000000000..0641f3ca5 --- /dev/null +++ b/aurelia-bootstrap-select/dist/commonjs/util-service.js @@ -0,0 +1,56 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var UtilService = exports.UtilService = function () { + function UtilService() { + _classCallCheck(this, UtilService); + } + + UtilService.prototype.isArrayEqual = function isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + }; + + UtilService.prototype.isEqual = function isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + }; + + UtilService.prototype.isObjectArray = function isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && _typeof(inputArrray[0]) === 'object'; + }; + + UtilService.prototype.isObject = function isObject(arg) { + return (typeof arg === 'undefined' ? 'undefined' : _typeof(arg)) === 'object'; + }; + + UtilService.prototype.isString = function isString(arg) { + return typeof arg === 'string' || arg instanceof String; + }; + + UtilService.prototype.isStringArray = function isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + }; + + UtilService.prototype.parseBool = function parseBool(value) { + return (/^(true|1)$/i.test(value) + ); + }; + + return UtilService; +}(); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/es2015/abp-select.html b/aurelia-bootstrap-select/dist/es2015/abp-select.html new file mode 100644 index 000000000..ea1d663c8 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/abp-select.html @@ -0,0 +1,45 @@ + diff --git a/aurelia-bootstrap-select/dist/es2015/abp-select.js b/aurelia-bootstrap-select/dist/es2015/abp-select.js new file mode 100644 index 000000000..e35b8c054 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/abp-select.js @@ -0,0 +1,493 @@ +var _dec, _dec2, _dec3, _dec4, _class, _desc, _value, _class2, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _descriptor7, _descriptor8, _descriptor9, _descriptor10, _descriptor11, _descriptor12, _descriptor13, _descriptor14, _descriptor15, _descriptor16, _descriptor17, _descriptor18, _descriptor19, _descriptor20, _descriptor21, _descriptor22; + +function _initDefineProp(target, property, descriptor, context) { + if (!descriptor) return; + Object.defineProperty(target, property, { + enumerable: descriptor.enumerable, + configurable: descriptor.configurable, + writable: descriptor.writable, + value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 + }); +} + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { + var desc = {}; + Object['ke' + 'ys'](descriptor).forEach(function (key) { + desc[key] = descriptor[key]; + }); + desc.enumerable = !!desc.enumerable; + desc.configurable = !!desc.configurable; + + if ('value' in desc || desc.initializer) { + desc.writable = true; + } + + desc = decorators.slice().reverse().reduce(function (desc, decorator) { + return decorator(target, property, desc) || desc; + }, desc); + + if (context && desc.initializer !== void 0) { + desc.value = desc.initializer ? desc.initializer.call(context) : void 0; + desc.initializer = undefined; + } + + if (desc.initializer === void 0) { + Object['define' + 'Property'](target, property, desc); + desc = null; + } + + return desc; +} + +function _initializerWarningHelper(descriptor, context) { + throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.'); +} + +import { inject, bindable, bindingMode } from 'aurelia-framework'; +import { UtilService } from './util-service'; +import $ from 'jquery'; +import 'bootstrap-select'; +import { globalExtraOptions, globalPickerOptions } from './picker-global-options'; + +export let AbpSelectCustomElement = (_dec = inject(Element, UtilService), _dec2 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec3 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec4 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec(_class = (_class2 = class AbpSelectCustomElement { + + constructor(elm, utilService) { + _initDefineProp(this, 'element', _descriptor, this); + + _initDefineProp(this, 'selectedItem', _descriptor2, this); + + _initDefineProp(this, 'selectedValue', _descriptor3, this); + + _initDefineProp(this, 'class', _descriptor4, this); + + _initDefineProp(this, 'collection', _descriptor5, this); + + _initDefineProp(this, 'dataMappingStructure', _descriptor6, this); + + _initDefineProp(this, 'disabled', _descriptor7, this); + + _initDefineProp(this, 'emptyOnNull', _descriptor8, this); + + _initDefineProp(this, 'hasOptgroup', _descriptor9, this); + + _initDefineProp(this, 'multiple', _descriptor10, this); + + _initDefineProp(this, 'objectKey', _descriptor11, this); + + _initDefineProp(this, 'pickerOptions', _descriptor12, this); + + _initDefineProp(this, 'placeholder', _descriptor13, this); + + _initDefineProp(this, 'selected', _descriptor14, this); + + _initDefineProp(this, 'onChanged', _descriptor15, this); + + _initDefineProp(this, 'onHide', _descriptor16, this); + + _initDefineProp(this, 'onHidden', _descriptor17, this); + + _initDefineProp(this, 'onLoaded', _descriptor18, this); + + _initDefineProp(this, 'onRendered', _descriptor19, this); + + _initDefineProp(this, 'onRefreshed', _descriptor20, this); + + _initDefineProp(this, 'onShow', _descriptor21, this); + + _initDefineProp(this, 'onShown', _descriptor22, this); + + this.elm = elm; + this.util = utilService; + } + + attached() { + this.domElm = $(this.elm).find('.selectpicker'); + + let events = this.applyExposeEvents(); + let methods = this.exposeMethods(); + + let pickerOptions = Object.assign({}, globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + } + + bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + let originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + let originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + } + + applyExposeEvents() { + let events = {}; + + this.domElm.on('show.bs.select', e => { + if (typeof this.onShow === 'function') { + this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', e => { + if (typeof this.onShown === 'function') { + this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', e => { + if (typeof this.onHide === 'function') { + this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', e => { + if (typeof this.onHidden === 'function') { + this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', e => { + if (typeof this.onLoaded === 'function') { + this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', e => { + if (typeof this.onRendered === 'function') { + this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', e => { + if (typeof this.onRefreshed === 'function') { + this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + if (typeof this.onChanged === 'function') { + this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + } + + exposeMethods() { + let methods = { + deselectAll: () => this.domElm.selectpicker('deselectAll'), + destroy: () => this.domElm.selectpicker('destroy'), + disableOptgroupByIndex: (index, isDisable = true) => { + if (this.domElm.find('optgroup')[index]) { + this.domElm.find('optgroup')[index].prop('disabled', isDisable); + this.domElm.selectpicker('refresh'); + } + }, + disableOptgroupByLabel: (label, isDisable = true) => { + this.domElm.find(`optgroup[label=${label}]`).prop('disabled', isDisable); + this.domElm.selectpicker('refresh'); + }, + mobile: () => this.domElm.selectpicker('mobile'), + refresh: () => this.domElm.selectpicker('refresh'), + render: () => this.domElm.selectpicker('render'), + val: value => this.domElm.selectpicker('val', value), + selectAll: () => this.domElm.selectpicker('selectAll'), + setStyle: (style, isAddingTheClass = true) => { + if (style.includes('btn')) { + let action = isAddingTheClass ? 'add' : 'remove'; + this.domElm.selectpicker('setStyle', style, action); + } else { + this.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + } + + detached() { + this.domElm.selectpicker('destroy'); + } + + getGroupedCollection() { + let groupingPropName = this.getMappingProperty('groupLabel'); + let collectionGroupedAsObject = this.collection.reduce((groups, y) => { + let key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + return Object.keys(collectionGroupedAsObject).map(k => collectionGroupedAsObject[k]); + } + + getMappingProperty(type) { + let dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + } + + getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + } + + getMappingPropertyValue(inputArray, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + } + + getMergedMappingStructure() { + let dataMappingStructure = Object.assign({}, globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + } + + findItems(collection, newValue, objectKey) { + let foundItems = []; + let searchingItems = []; + let selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + for (let searchItem of searchingItems) { + let searchFilter = this.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + let foundItem = collection.find(item => { + return this.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(this.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + } + + return selection; + } + + isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + } + + isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + } + + isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + } + + renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + } + + selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]; + } + } + + this.renderSelection(selection); + } + } + + selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + this.selectedItem = selection.items.length > 0 ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + } + + watchOnLoadedToRenderPreSelection() { + this.domElm.on('loaded.bs.select', e => { + let newValue = this._originalSelectedIndexes || this._originalSelectedObjects; + let selection = this.findItems(this.collection, newValue, this.objectKey); + if (selection.indexes) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]; + } + this.selectedItem = selection.items ? selection.items : this.collection[0]; + this.renderSelection(selection); + }); + } + + watchOnChangedToUpdateValueAndItemObjects() { + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + this.selectedValue = this.domElm.selectpicker('val'); + let selection = this.findItems(this.collection, this.selectedValue, this.objectKey); + if (selection.indexes) { + this.domElm.selectpicker('val', selection.indexes); + } + + this.selectedValue = selection.indexes; + this.selectedItem = selection.items; + }); + } +}, (_descriptor = _applyDecoratedDescriptor(_class2.prototype, 'element', [_dec2], { + enumerable: true, + initializer: null +}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, 'selectedItem', [_dec3], { + enumerable: true, + initializer: null +}), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, 'selectedValue', [_dec4], { + enumerable: true, + initializer: null +}), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, 'class', [bindable], { + enumerable: true, + initializer: null +}), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, 'collection', [bindable], { + enumerable: true, + initializer: function () { + return []; + } +}), _descriptor6 = _applyDecoratedDescriptor(_class2.prototype, 'dataMappingStructure', [bindable], { + enumerable: true, + initializer: null +}), _descriptor7 = _applyDecoratedDescriptor(_class2.prototype, 'disabled', [bindable], { + enumerable: true, + initializer: function () { + return false; + } +}), _descriptor8 = _applyDecoratedDescriptor(_class2.prototype, 'emptyOnNull', [bindable], { + enumerable: true, + initializer: function () { + return false; + } +}), _descriptor9 = _applyDecoratedDescriptor(_class2.prototype, 'hasOptgroup', [bindable], { + enumerable: true, + initializer: function () { + return false; + } +}), _descriptor10 = _applyDecoratedDescriptor(_class2.prototype, 'multiple', [bindable], { + enumerable: true, + initializer: function () { + return false; + } +}), _descriptor11 = _applyDecoratedDescriptor(_class2.prototype, 'objectKey', [bindable], { + enumerable: true, + initializer: function () { + return 'id'; + } +}), _descriptor12 = _applyDecoratedDescriptor(_class2.prototype, 'pickerOptions', [bindable], { + enumerable: true, + initializer: null +}), _descriptor13 = _applyDecoratedDescriptor(_class2.prototype, 'placeholder', [bindable], { + enumerable: true, + initializer: null +}), _descriptor14 = _applyDecoratedDescriptor(_class2.prototype, 'selected', [bindable], { + enumerable: true, + initializer: null +}), _descriptor15 = _applyDecoratedDescriptor(_class2.prototype, 'onChanged', [bindable], { + enumerable: true, + initializer: null +}), _descriptor16 = _applyDecoratedDescriptor(_class2.prototype, 'onHide', [bindable], { + enumerable: true, + initializer: null +}), _descriptor17 = _applyDecoratedDescriptor(_class2.prototype, 'onHidden', [bindable], { + enumerable: true, + initializer: null +}), _descriptor18 = _applyDecoratedDescriptor(_class2.prototype, 'onLoaded', [bindable], { + enumerable: true, + initializer: null +}), _descriptor19 = _applyDecoratedDescriptor(_class2.prototype, 'onRendered', [bindable], { + enumerable: true, + initializer: null +}), _descriptor20 = _applyDecoratedDescriptor(_class2.prototype, 'onRefreshed', [bindable], { + enumerable: true, + initializer: null +}), _descriptor21 = _applyDecoratedDescriptor(_class2.prototype, 'onShow', [bindable], { + enumerable: true, + initializer: null +}), _descriptor22 = _applyDecoratedDescriptor(_class2.prototype, 'onShown', [bindable], { + enumerable: true, + initializer: null +})), _class2)) || _class); + +export let OptionalBindingBehavior = class OptionalBindingBehavior { + bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = val => { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + } + + unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + } +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/es2015/index.js b/aurelia-bootstrap-select/dist/es2015/index.js new file mode 100644 index 000000000..ce2b51750 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/index.js @@ -0,0 +1,14 @@ +import { AbpSelectCustomElement } from './abp-select'; +import { PickerConfig } from './picker-config'; + +export function configure(aurelia, callback) { + aurelia.globalResources('./abp-select'); + + let config = new PickerConfig(); + + if (typeof callback === 'function') { + callback(config); + } +} + +export { AbpSelectCustomElement, PickerConfig }; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/es2015/picker-config.js b/aurelia-bootstrap-select/dist/es2015/picker-config.js new file mode 100644 index 000000000..f255e8881 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/picker-config.js @@ -0,0 +1,8 @@ +import { globalExtraOptions, globalPickerOptions } from './picker-global-options'; + +export let PickerConfig = class PickerConfig { + constructor() { + this.extra = globalExtraOptions; + this.options = globalPickerOptions; + } +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/es2015/picker-global-options.js b/aurelia-bootstrap-select/dist/es2015/picker-global-options.js new file mode 100644 index 000000000..64a3a56b6 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/picker-global-options.js @@ -0,0 +1,24 @@ + +export let globalExtraOptions = { + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } +}; + +export let globalPickerOptions = { + dropupAuto: true, + showTick: true, + width: 'auto' +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/es2015/util-service.js b/aurelia-bootstrap-select/dist/es2015/util-service.js new file mode 100644 index 000000000..3de4428c9 --- /dev/null +++ b/aurelia-bootstrap-select/dist/es2015/util-service.js @@ -0,0 +1,40 @@ +export let UtilService = class UtilService { + isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + for (let i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + + isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + } + + isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'object'; + } + + isObject(arg) { + return typeof arg === 'object'; + } + + isString(arg) { + return typeof arg === 'string' || arg instanceof String; + } + + isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + } + + parseBool(value) { + return (/^(true|1)$/i.test(value) + ); + } +}; \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/system/abp-select.html b/aurelia-bootstrap-select/dist/system/abp-select.html new file mode 100644 index 000000000..ea1d663c8 --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/abp-select.html @@ -0,0 +1,45 @@ + diff --git a/aurelia-bootstrap-select/dist/system/abp-select.js b/aurelia-bootstrap-select/dist/system/abp-select.js new file mode 100644 index 000000000..18f35a1ec --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/abp-select.js @@ -0,0 +1,579 @@ +'use strict'; + +System.register(['aurelia-framework', './util-service', 'jquery', 'bootstrap-select', './picker-global-options'], function (_export, _context) { + "use strict"; + + var inject, bindable, bindingMode, UtilService, $, globalExtraOptions, globalPickerOptions, _dec, _dec2, _dec3, _dec4, _class, _desc, _value, _class2, _descriptor, _descriptor2, _descriptor3, _descriptor4, _descriptor5, _descriptor6, _descriptor7, _descriptor8, _descriptor9, _descriptor10, _descriptor11, _descriptor12, _descriptor13, _descriptor14, _descriptor15, _descriptor16, _descriptor17, _descriptor18, _descriptor19, _descriptor20, _descriptor21, _descriptor22, AbpSelectCustomElement, OptionalBindingBehavior; + + function _initDefineProp(target, property, descriptor, context) { + if (!descriptor) return; + Object.defineProperty(target, property, { + enumerable: descriptor.enumerable, + configurable: descriptor.configurable, + writable: descriptor.writable, + value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 + }); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { + var desc = {}; + Object['ke' + 'ys'](descriptor).forEach(function (key) { + desc[key] = descriptor[key]; + }); + desc.enumerable = !!desc.enumerable; + desc.configurable = !!desc.configurable; + + if ('value' in desc || desc.initializer) { + desc.writable = true; + } + + desc = decorators.slice().reverse().reduce(function (desc, decorator) { + return decorator(target, property, desc) || desc; + }, desc); + + if (context && desc.initializer !== void 0) { + desc.value = desc.initializer ? desc.initializer.call(context) : void 0; + desc.initializer = undefined; + } + + if (desc.initializer === void 0) { + Object['define' + 'Property'](target, property, desc); + desc = null; + } + + return desc; + } + + function _initializerWarningHelper(descriptor, context) { + throw new Error('Decorating class property failed. Please ensure that transform-class-properties is enabled.'); + } + + return { + setters: [function (_aureliaFramework) { + inject = _aureliaFramework.inject; + bindable = _aureliaFramework.bindable; + bindingMode = _aureliaFramework.bindingMode; + }, function (_utilService) { + UtilService = _utilService.UtilService; + }, function (_jquery) { + $ = _jquery.default; + }, function (_bootstrapSelect) {}, function (_pickerGlobalOptions) { + globalExtraOptions = _pickerGlobalOptions.globalExtraOptions; + globalPickerOptions = _pickerGlobalOptions.globalPickerOptions; + }], + execute: function () { + _export('AbpSelectCustomElement', AbpSelectCustomElement = (_dec = inject(Element, UtilService), _dec2 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec3 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec4 = bindable({ defaultBindingMode: bindingMode.twoWay }), _dec(_class = (_class2 = function () { + function AbpSelectCustomElement(elm, utilService) { + _classCallCheck(this, AbpSelectCustomElement); + + _initDefineProp(this, 'element', _descriptor, this); + + _initDefineProp(this, 'selectedItem', _descriptor2, this); + + _initDefineProp(this, 'selectedValue', _descriptor3, this); + + _initDefineProp(this, 'class', _descriptor4, this); + + _initDefineProp(this, 'collection', _descriptor5, this); + + _initDefineProp(this, 'dataMappingStructure', _descriptor6, this); + + _initDefineProp(this, 'disabled', _descriptor7, this); + + _initDefineProp(this, 'emptyOnNull', _descriptor8, this); + + _initDefineProp(this, 'hasOptgroup', _descriptor9, this); + + _initDefineProp(this, 'multiple', _descriptor10, this); + + _initDefineProp(this, 'objectKey', _descriptor11, this); + + _initDefineProp(this, 'pickerOptions', _descriptor12, this); + + _initDefineProp(this, 'placeholder', _descriptor13, this); + + _initDefineProp(this, 'selected', _descriptor14, this); + + _initDefineProp(this, 'onChanged', _descriptor15, this); + + _initDefineProp(this, 'onHide', _descriptor16, this); + + _initDefineProp(this, 'onHidden', _descriptor17, this); + + _initDefineProp(this, 'onLoaded', _descriptor18, this); + + _initDefineProp(this, 'onRendered', _descriptor19, this); + + _initDefineProp(this, 'onRefreshed', _descriptor20, this); + + _initDefineProp(this, 'onShow', _descriptor21, this); + + _initDefineProp(this, 'onShown', _descriptor22, this); + + this.elm = elm; + this.util = utilService; + } + + AbpSelectCustomElement.prototype.attached = function attached() { + this.domElm = $(this.elm).find('.selectpicker'); + + var events = this.applyExposeEvents(); + var methods = this.exposeMethods(); + + var pickerOptions = Object.assign({}, globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + }; + + AbpSelectCustomElement.prototype.bind = function bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + var originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + var originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + }; + + AbpSelectCustomElement.prototype.applyExposeEvents = function applyExposeEvents() { + var _this = this; + + var events = {}; + + this.domElm.on('show.bs.select', function (e) { + if (typeof _this.onShow === 'function') { + _this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', function (e) { + if (typeof _this.onShown === 'function') { + _this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', function (e) { + if (typeof _this.onHide === 'function') { + _this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', function (e) { + if (typeof _this.onHidden === 'function') { + _this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', function (e) { + if (typeof _this.onLoaded === 'function') { + _this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', function (e) { + if (typeof _this.onRendered === 'function') { + _this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', function (e) { + if (typeof _this.onRefreshed === 'function') { + _this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + if (typeof _this.onChanged === 'function') { + _this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + }; + + AbpSelectCustomElement.prototype.exposeMethods = function exposeMethods() { + var _this2 = this; + + var methods = { + deselectAll: function deselectAll() { + return _this2.domElm.selectpicker('deselectAll'); + }, + destroy: function destroy() { + return _this2.domElm.selectpicker('destroy'); + }, + disableOptgroupByIndex: function disableOptgroupByIndex(index) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (_this2.domElm.find('optgroup')[index]) { + _this2.domElm.find('optgroup')[index].prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + } + }, + disableOptgroupByLabel: function disableOptgroupByLabel(label) { + var isDisable = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + _this2.domElm.find('optgroup[label=' + label + ']').prop('disabled', isDisable); + _this2.domElm.selectpicker('refresh'); + }, + mobile: function mobile() { + return _this2.domElm.selectpicker('mobile'); + }, + refresh: function refresh() { + return _this2.domElm.selectpicker('refresh'); + }, + render: function render() { + return _this2.domElm.selectpicker('render'); + }, + val: function val(value) { + return _this2.domElm.selectpicker('val', value); + }, + selectAll: function selectAll() { + return _this2.domElm.selectpicker('selectAll'); + }, + setStyle: function setStyle(style) { + var isAddingTheClass = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + + if (style.includes('btn')) { + var action = isAddingTheClass ? 'add' : 'remove'; + _this2.domElm.selectpicker('setStyle', style, action); + } else { + _this2.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + }; + + AbpSelectCustomElement.prototype.detached = function detached() { + this.domElm.selectpicker('destroy'); + }; + + AbpSelectCustomElement.prototype.getGroupedCollection = function getGroupedCollection() { + var groupingPropName = this.getMappingProperty('groupLabel'); + var collectionGroupedAsObject = this.collection.reduce(function (groups, y) { + var key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + return Object.keys(collectionGroupedAsObject).map(function (k) { + return collectionGroupedAsObject[k]; + }); + }; + + AbpSelectCustomElement.prototype.getMappingProperty = function getMappingProperty(type) { + var dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValueFromIndex = function getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMappingPropertyValue = function getMappingPropertyValue(inputArray, searchPropName) { + var propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + }; + + AbpSelectCustomElement.prototype.getMergedMappingStructure = function getMergedMappingStructure() { + var dataMappingStructure = Object.assign({}, globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + }; + + AbpSelectCustomElement.prototype.findItems = function findItems(collection, newValue, objectKey) { + var _this3 = this; + + var foundItems = []; + var searchingItems = []; + var selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + var _loop = function _loop() { + if (_isArray) { + if (_i >= _iterator.length) return 'break'; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) return 'break'; + _ref = _i.value; + } + + var searchItem = _ref; + + var searchFilter = _this3.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + var foundItem = collection.find(function (item) { + return _this3.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(_this3.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + }; + + for (var _iterator = searchingItems, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; + + var _ret = _loop(); + + if (_ret === 'break') break; + } + + return selection; + }; + + AbpSelectCustomElement.prototype.isEmptySelection = function isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + }; + + AbpSelectCustomElement.prototype.isNotEmptySelection = function isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + }; + + AbpSelectCustomElement.prototype.isSelected = function isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + }; + + AbpSelectCustomElement.prototype.renderSelection = function renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + }; + + AbpSelectCustomElement.prototype.selectedItemChanged = function selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]; + } + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.selectedValueChanged = function selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + var selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + if (!this.util.parseBool(this.emptyOnNull) && !this.multiple || this.isNotEmptySelection(selection)) { + this.selectedItem = selection.items.length > 0 ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + }; + + AbpSelectCustomElement.prototype.watchOnLoadedToRenderPreSelection = function watchOnLoadedToRenderPreSelection() { + var _this4 = this; + + this.domElm.on('loaded.bs.select', function (e) { + var newValue = _this4._originalSelectedIndexes || _this4._originalSelectedObjects; + var selection = _this4.findItems(_this4.collection, newValue, _this4.objectKey); + if (selection.indexes) { + _this4.selectedValue = selection.indexes; + } else { + _this4.selectedValue = _this4.util.isObject(_this4.collection[0]) ? _this4.collection[0][_this4.objectKey] : _this4.collection[0]; + } + _this4.selectedItem = selection.items ? selection.items : _this4.collection[0]; + _this4.renderSelection(selection); + }); + }; + + AbpSelectCustomElement.prototype.watchOnChangedToUpdateValueAndItemObjects = function watchOnChangedToUpdateValueAndItemObjects() { + var _this5 = this; + + this.domElm.on('changed.bs.select', function (e, clickedIndex, newValue, oldValue) { + _this5.selectedValue = _this5.domElm.selectpicker('val'); + var selection = _this5.findItems(_this5.collection, _this5.selectedValue, _this5.objectKey); + if (selection.indexes) { + _this5.domElm.selectpicker('val', selection.indexes); + } + + _this5.selectedValue = selection.indexes; + _this5.selectedItem = selection.items; + }); + }; + + return AbpSelectCustomElement; + }(), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, 'element', [_dec2], { + enumerable: true, + initializer: null + }), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, 'selectedItem', [_dec3], { + enumerable: true, + initializer: null + }), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, 'selectedValue', [_dec4], { + enumerable: true, + initializer: null + }), _descriptor4 = _applyDecoratedDescriptor(_class2.prototype, 'class', [bindable], { + enumerable: true, + initializer: null + }), _descriptor5 = _applyDecoratedDescriptor(_class2.prototype, 'collection', [bindable], { + enumerable: true, + initializer: function initializer() { + return []; + } + }), _descriptor6 = _applyDecoratedDescriptor(_class2.prototype, 'dataMappingStructure', [bindable], { + enumerable: true, + initializer: null + }), _descriptor7 = _applyDecoratedDescriptor(_class2.prototype, 'disabled', [bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor8 = _applyDecoratedDescriptor(_class2.prototype, 'emptyOnNull', [bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor9 = _applyDecoratedDescriptor(_class2.prototype, 'hasOptgroup', [bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor10 = _applyDecoratedDescriptor(_class2.prototype, 'multiple', [bindable], { + enumerable: true, + initializer: function initializer() { + return false; + } + }), _descriptor11 = _applyDecoratedDescriptor(_class2.prototype, 'objectKey', [bindable], { + enumerable: true, + initializer: function initializer() { + return 'id'; + } + }), _descriptor12 = _applyDecoratedDescriptor(_class2.prototype, 'pickerOptions', [bindable], { + enumerable: true, + initializer: null + }), _descriptor13 = _applyDecoratedDescriptor(_class2.prototype, 'placeholder', [bindable], { + enumerable: true, + initializer: null + }), _descriptor14 = _applyDecoratedDescriptor(_class2.prototype, 'selected', [bindable], { + enumerable: true, + initializer: null + }), _descriptor15 = _applyDecoratedDescriptor(_class2.prototype, 'onChanged', [bindable], { + enumerable: true, + initializer: null + }), _descriptor16 = _applyDecoratedDescriptor(_class2.prototype, 'onHide', [bindable], { + enumerable: true, + initializer: null + }), _descriptor17 = _applyDecoratedDescriptor(_class2.prototype, 'onHidden', [bindable], { + enumerable: true, + initializer: null + }), _descriptor18 = _applyDecoratedDescriptor(_class2.prototype, 'onLoaded', [bindable], { + enumerable: true, + initializer: null + }), _descriptor19 = _applyDecoratedDescriptor(_class2.prototype, 'onRendered', [bindable], { + enumerable: true, + initializer: null + }), _descriptor20 = _applyDecoratedDescriptor(_class2.prototype, 'onRefreshed', [bindable], { + enumerable: true, + initializer: null + }), _descriptor21 = _applyDecoratedDescriptor(_class2.prototype, 'onShow', [bindable], { + enumerable: true, + initializer: null + }), _descriptor22 = _applyDecoratedDescriptor(_class2.prototype, 'onShown', [bindable], { + enumerable: true, + initializer: null + })), _class2)) || _class)); + + _export('AbpSelectCustomElement', AbpSelectCustomElement); + + _export('OptionalBindingBehavior', OptionalBindingBehavior = function () { + function OptionalBindingBehavior() { + _classCallCheck(this, OptionalBindingBehavior); + } + + OptionalBindingBehavior.prototype.bind = function bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = function (val) { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + }; + + OptionalBindingBehavior.prototype.unbind = function unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + }; + + return OptionalBindingBehavior; + }()); + + _export('OptionalBindingBehavior', OptionalBindingBehavior); + } + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/system/index.js b/aurelia-bootstrap-select/dist/system/index.js new file mode 100644 index 000000000..39991056d --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/index.js @@ -0,0 +1,31 @@ +'use strict'; + +System.register(['./abp-select', './picker-config'], function (_export, _context) { + "use strict"; + + var AbpSelectCustomElement, PickerConfig; + function configure(aurelia, callback) { + aurelia.globalResources('./abp-select'); + + var config = new PickerConfig(); + + if (typeof callback === 'function') { + callback(config); + } + } + + _export('configure', configure); + + return { + setters: [function (_abpSelect) { + AbpSelectCustomElement = _abpSelect.AbpSelectCustomElement; + }, function (_pickerConfig) { + PickerConfig = _pickerConfig.PickerConfig; + }], + execute: function () { + _export('AbpSelectCustomElement', AbpSelectCustomElement); + + _export('PickerConfig', PickerConfig); + } + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/system/picker-config.js b/aurelia-bootstrap-select/dist/system/picker-config.js new file mode 100644 index 000000000..3b097e277 --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/picker-config.js @@ -0,0 +1,30 @@ +'use strict'; + +System.register(['./picker-global-options'], function (_export, _context) { + "use strict"; + + var globalExtraOptions, globalPickerOptions, PickerConfig; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + return { + setters: [function (_pickerGlobalOptions) { + globalExtraOptions = _pickerGlobalOptions.globalExtraOptions; + globalPickerOptions = _pickerGlobalOptions.globalPickerOptions; + }], + execute: function () { + _export('PickerConfig', PickerConfig = function PickerConfig() { + _classCallCheck(this, PickerConfig); + + this.extra = globalExtraOptions; + this.options = globalPickerOptions; + }); + + _export('PickerConfig', PickerConfig); + } + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/system/picker-global-options.js b/aurelia-bootstrap-select/dist/system/picker-global-options.js new file mode 100644 index 000000000..13564d002 --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/picker-global-options.js @@ -0,0 +1,39 @@ +'use strict'; + +System.register([], function (_export, _context) { + "use strict"; + + var globalExtraOptions, globalPickerOptions; + return { + setters: [], + execute: function () { + _export('globalExtraOptions', globalExtraOptions = { + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } + }); + + _export('globalExtraOptions', globalExtraOptions); + + _export('globalPickerOptions', globalPickerOptions = { + dropupAuto: true, + showTick: true, + width: 'auto' + }); + + _export('globalPickerOptions', globalPickerOptions); + } + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/dist/system/util-service.js b/aurelia-bootstrap-select/dist/system/util-service.js new file mode 100644 index 000000000..1709bfb62 --- /dev/null +++ b/aurelia-bootstrap-select/dist/system/util-service.js @@ -0,0 +1,73 @@ +'use strict'; + +System.register([], function (_export, _context) { + "use strict"; + + var _typeof, UtilService; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + return { + setters: [], + execute: function () { + _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + + _export('UtilService', UtilService = function () { + function UtilService() { + _classCallCheck(this, UtilService); + } + + UtilService.prototype.isArrayEqual = function isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + }; + + UtilService.prototype.isEqual = function isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + }; + + UtilService.prototype.isObjectArray = function isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && _typeof(inputArrray[0]) === 'object'; + }; + + UtilService.prototype.isObject = function isObject(arg) { + return (typeof arg === 'undefined' ? 'undefined' : _typeof(arg)) === 'object'; + }; + + UtilService.prototype.isString = function isString(arg) { + return typeof arg === 'string' || arg instanceof String; + }; + + UtilService.prototype.isStringArray = function isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + }; + + UtilService.prototype.parseBool = function parseBool(value) { + return (/^(true|1)$/i.test(value) + ); + }; + + return UtilService; + }()); + + _export('UtilService', UtilService); + } + }; +}); \ No newline at end of file diff --git a/aurelia-bootstrap-select/doc/CHANGELOG.md b/aurelia-bootstrap-select/doc/CHANGELOG.md new file mode 100644 index 000000000..21e86a78a --- /dev/null +++ b/aurelia-bootstrap-select/doc/CHANGELOG.md @@ -0,0 +1,15 @@ +### 0.3.0 (2017-01-11) + +* Breaking Change: renamed `createDatagrid()` to simply `createGrid()` +* Add missing `Plugins` directly in `aurelia-slickgrid` until `slickgrid-es6` resolve it's own bundle issue with the `Plugins`. +* Add `Aurelia-Webpack` sample available in `client-wp` folder + + +### 0.2.0 (2017-01-11) + +* First official working `Aurelia-Slickgrid` version + + +### 0.1.0 (2017-01-08) + +* Initial `Aurelia-Slickgrid` commit diff --git a/aurelia-bootstrap-select/doc/api.json b/aurelia-bootstrap-select/doc/api.json new file mode 100644 index 000000000..6de3c0a92 --- /dev/null +++ b/aurelia-bootstrap-select/doc/api.json @@ -0,0 +1 @@ +{"classes":[],"methods":[],"properties":[],"events":[]} \ No newline at end of file diff --git a/aurelia-bootstrap-select/gulpfile.js b/aurelia-bootstrap-select/gulpfile.js new file mode 100644 index 000000000..4b28ec106 --- /dev/null +++ b/aurelia-bootstrap-select/gulpfile.js @@ -0,0 +1,3 @@ +// all gulp tasks are located in the ./build/tasks directory +// gulp configuration is in files in ./build directory +require('require-dir')('build/tasks'); diff --git a/aurelia-bootstrap-select/karma.conf.js b/aurelia-bootstrap-select/karma.conf.js new file mode 100644 index 000000000..03e0ca273 --- /dev/null +++ b/aurelia-bootstrap-select/karma.conf.js @@ -0,0 +1,76 @@ +// Karma configuration +// Generated on Fri Dec 05 2014 16:49:29 GMT-0500 (EST) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jspm', 'jasmine'], + + jspm: { + // Edit this to your needs + loadFiles: ['test/setup.js', 'test/unit/**/*.js'], + serveFiles: ['src/**/*.js'], + paths: { + '*': '*', + 'github:*': 'jspm_packages/github/*', + 'npm:*': 'jspm_packages/npm/*' + } + }, + + // list of files / patterns to load in the browser + files: [], + + // list of files to exclude + exclude: [], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'test/**/*.js': ['babel'], + 'src/**/*.js': ['babel'] + }, + 'babelPreprocessor': { + options: { + sourceMap: 'inline', + presets: [ 'es2015-loose', 'stage-1'], + plugins: [ + 'syntax-flow', + 'transform-decorators-legacy', + 'transform-flow-strip-types' + ] + } + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Chrome'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); +}; diff --git a/aurelia-bootstrap-select/package.json b/aurelia-bootstrap-select/package.json new file mode 100644 index 000000000..3fe41a80c --- /dev/null +++ b/aurelia-bootstrap-select/package.json @@ -0,0 +1,83 @@ +{ + "name": "aurelia-bootstrap-select", + "version": "0.1.0", + "description": "An Aurelia Custom Element for the 3rd party addon [Bootstrap-Select]", + "keywords": [ + "aurelia", + "bootstrap", + "bootstrap-select", + "selectPicker" + ], + "aurelia": { + "build": { + "resources": [ + "aurelia-bootstrap-select/abp-select", + "aurelia-bootstrap-select/picker-config" + ] + } + }, + "homepage": "https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/aurelia-bootstrap-select", + "bugs": { + "url": "https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/aurelia-bootstrap-select/issues" + }, + "license": "MIT", + "author": "Ghislain B.", + "main": "dist/commonjs/index.js", + "repository": { + "type": "git", + "url": "https://github.com/ghiscoding/Aurelia-Bootstrap-Plugins/aurelia-bootstrap-select" + }, + "dependencies": { + "bootstrap": "^3.3.7", + "jquery": "^3.1.1", + "bootstrap-select": "^1.12.2" + }, + "devDependencies": { + "aurelia-tools": "^0.2.4", + "babel": "^6.5.2", + "babel-eslint": "^6.1.2", + "babel-plugin-syntax-flow": "^6.8.0", + "babel-plugin-transform-decorators-legacy": "^1.3.4", + "babel-plugin-transform-es2015-modules-amd": "^6.8.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.11.5", + "babel-plugin-transform-es2015-modules-systemjs": "^6.11.6", + "babel-plugin-transform-flow-strip-types": "^6.8.0", + "babel-preset-es2015": "^6.9.0", + "babel-preset-es2015-loose": "^7.0.0", + "babel-preset-stage-1": "^6.5.0", + "conventional-changelog": "1.1.0", + "del": "^2.2.1", + "gulp": "^3.9.1", + "gulp-babel": "^6.1.2", + "gulp-bump": "^2.2.0", + "gulp-eslint": "^3.0.1", + "gulp-sass": "^3.1.0", + "gulp-yuidoc": "^0.1.2", + "isparta": "^4.0.0", + "istanbul": "^1.0.0-alpha.2", + "jasmine-core": "^2.4.1", + "karma": "^1.1.2", + "karma-babel-preprocessor": "^6.0.1", + "karma-chrome-launcher": "^1.0.1", + "karma-coverage": "^1.1.1", + "karma-jasmine": "^1.0.2", + "karma-jspm": "2.2.0", + "object.assign": "^4.0.4", + "require-dir": "^0.3.0", + "run-sequence": "^1.2.2", + "vinyl-paths": "^2.1.0", + "yargs": "^4.8.1" + }, + "jspm": { + "registry": "npm", + "main": "index", + "format": "amd", + "directories": { + "dist": "dist/amd" + }, + "peerDependencies": {}, + "devDependencies": { + "aurelia-polyfills": "^1.1.1" + } + } +} diff --git a/aurelia-bootstrap-select/src/abp-select.html b/aurelia-bootstrap-select/src/abp-select.html new file mode 100644 index 000000000..ea1d663c8 --- /dev/null +++ b/aurelia-bootstrap-select/src/abp-select.html @@ -0,0 +1,45 @@ + diff --git a/aurelia-bootstrap-select/src/abp-select.js b/aurelia-bootstrap-select/src/abp-select.js new file mode 100644 index 000000000..1aa81ee58 --- /dev/null +++ b/aurelia-bootstrap-select/src/abp-select.js @@ -0,0 +1,458 @@ +import {inject, bindable, bindingMode} from 'aurelia-framework'; +import {UtilService} from './util-service'; +import $ from 'jquery'; +import 'bootstrap-select'; +import {globalExtraOptions, globalPickerOptions} from './picker-global-options'; +//import 'bootstrap-select/dist/css/bootstrap-select.min.css'; + +@inject(Element, UtilService) +export class AbpSelectCustomElement { + @bindable({defaultBindingMode: bindingMode.twoWay}) element; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedItem; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedValue; + @bindable class; + @bindable collection = []; + @bindable dataMappingStructure; + @bindable disabled = false; + @bindable emptyOnNull = false; + @bindable hasOptgroup = false; + @bindable multiple = false; + @bindable objectKey = 'id'; + @bindable pickerOptions; + @bindable placeholder; + @bindable selected; + + // events (from the View) + @bindable onChanged; + @bindable onHide; + @bindable onHidden; + @bindable onLoaded; + @bindable onRendered; + @bindable onRefreshed; + @bindable onShow; + @bindable onShown; + + // variables + _originalSelectedIndexes; + _originalSelectedObjects; + + constructor(elm, utilService) { + this.elm = elm; + this.util = utilService; + } + + attached() { + // reference to the DOM element + this.domElm = $(this.elm).find('.selectpicker'); + + // expose events & methods + let events = this.applyExposeEvents(); + let methods = this.exposeMethods(); + + // finally create the bootstrap-select with all options + let pickerOptions = Object.assign({}, globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + // expose the element object to the outside + // this will be useful for calling events/methods/options from the outside + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + } + + /** + * Keep original value(s) that could be passed by the user ViewModel. + * If nothing was passed, it will default to first option of select + */ + bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + let originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + let originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + // make a deep clone copy to avoid object pointer issues + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + } + + /** + * Apply/expose selectpicker events + * Each event has 2 ways of triggering an event (from the View as an attribute or from the ViewModel has a function call) + */ + applyExposeEvents() { + let events = {}; + + this.domElm.on('show.bs.select', (e) => { + if (typeof this.onShow === 'function') { + this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', (e) => { + if (typeof this.onShown === 'function') { + this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', (e) => { + if (typeof this.onHide === 'function') { + this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', (e) => { + if (typeof this.onHidden === 'function') { + this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', (e) => { + if (typeof this.onLoaded === 'function') { + this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', (e) => { + if (typeof this.onRendered === 'function') { + this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', (e) => { + if (typeof this.onRefreshed === 'function') { + this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + if (typeof this.onChanged === 'function') { + this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + } + + /** + * Expose selectpicker methods + */ + exposeMethods() { + let methods = { + deselectAll: () => this.domElm.selectpicker('deselectAll'), + destroy: () => this.domElm.selectpicker('destroy'), + disableOptgroupByIndex: (index, isDisable = true) => { + if (this.domElm.find('optgroup')[index]) { + this.domElm.find('optgroup')[index].prop('disabled', isDisable); + this.domElm.selectpicker('refresh'); + } + }, + disableOptgroupByLabel: (label, isDisable = true) => { + this.domElm.find(`optgroup[label=${label}]`).prop('disabled', isDisable); + this.domElm.selectpicker('refresh'); + }, + mobile: () => this.domElm.selectpicker('mobile'), + refresh: () => this.domElm.selectpicker('refresh'), + render: () => this.domElm.selectpicker('render'), + val: (value) => this.domElm.selectpicker('val', value), + selectAll: () => this.domElm.selectpicker('selectAll'), + setStyle: (style, isAddingTheClass = true) => { + if (style.includes('btn')) { + let action = isAddingTheClass ? 'add' : 'remove'; + this.domElm.selectpicker('setStyle', style, action); + } else { + this.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + } + + detached() { + this.domElm.selectpicker('destroy'); + } + + /** + * Get the grouped collection by the mapping 'groupLabel' property + * @return {array} groupedCollection + */ + getGroupedCollection() { + // group the array by the mapping group property + // ex input: [{ id: 12, option: 'Steam', group: 'Breads' }, { id: 4, option: 'Mayonnaise', group: 'Condiments' }] + // output: { Breads: [{ id: 12, option: 'Steam', group: 'Breads' }], Condiments: [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }]} + let groupingPropName = this.getMappingProperty('groupLabel'); + let collectionGroupedAsObject = this.collection.reduce((groups, y) => { + let key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + // then recreate an array with previously found subgroup + // output: [ [{ id: 12, option: 'Steam', group: 'Breads' }], [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }] ] + return Object.keys(collectionGroupedAsObject).map(k => collectionGroupedAsObject[k]); + } + + /** + * Get data structure mapping property + * @param {string} type + * @return {string} mappingPropertyName + */ + getMappingProperty(type) { + let dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + } + + /** + * From an input array, find the mapping property value by an index provided + * Example: getMappingPropertyValueFromIndex(option, 2, 'groupLabel') => 'groupLabel' or the user custom group + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + } + + /** + * From an input array, find the mapping property value + * Example: getMappingPropertyValue(option, 'divider') => 'divider' or the user custom divider + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValue(inputArray, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + } + + /** + * Get merged data structure mapping + * @return {array} mergedDataMappingStructure + */ + getMergedMappingStructure() { + let dataMappingStructure = Object.assign({}, globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + } + + /** + * Find item(s) in the collection. + * To support the "multiple" selection attribute and to make our life and code easier, we will use arrays to search and return the found item(s). + * The array advantage is that even if we are not using "multiple", we will still have 1 value at the end (the only difference is that it's inside an array). + * @param {array} collection + * @param {any} newValue + * @param {string} objectKey + * @return {object} found selection, the output is an object with structure of selection = { indexes: [], items: [] }; + */ + findItems(collection, newValue, objectKey) { + let foundItems = []; + let searchingItems = []; + let selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + for (let searchItem of searchingItems) { + let searchFilter = this.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + let foundItem = collection.find(item => { + // for comparison, we're using == mostly because indexes are passed as string because of html + return this.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(this.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + } + + return selection; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + } + + /** + * From a selection option (from View), we want to know if the item is selected + * @param {any} option + */ + isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + } + + /** + * Select the item in the UI element from a selection object passed + * The structure is:: selection = { indexes: [], items: [] }; + * @param {object} selection object + */ + renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + } + + /** + * On selected item (ids) changed, we will update the selectedValue(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + // get selected indexes (ids), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + // value could be an object, if so we will use the objectKey (object.id by default) + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + } + + this.renderSelection(selection); + } + } + + /** + * On selected value (string/object) changed, we will update the selectedItem(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + // get selected items (string/object), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + this.selectedItem = (selection.items.length > 0) ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + } + + /** + * onLoaded trigger, we will also check if user pre-selected any option(s). + * Pre-selection of options can be done by index and/or by item object. + * If provided, we will make them part of the select index/item and render selection on UI as well + */ + watchOnLoadedToRenderPreSelection() { + this.domElm.on('loaded.bs.select', (e) => { + let newValue = this._originalSelectedIndexes || this._originalSelectedObjects; + let selection = this.findItems(this.collection, newValue, this.objectKey); + if (selection.indexes) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + this.selectedItem = selection.items ? selection.items : this.collection[0]; + this.renderSelection(selection); + }); + } + + /** + * onChanged trigger, we will update our index(es) and item object(s) + */ + watchOnChangedToUpdateValueAndItemObjects() { + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + this.selectedValue = this.domElm.selectpicker('val'); + let selection = this.findItems(this.collection, this.selectedValue, this.objectKey); + if (selection.indexes) { + this.domElm.selectpicker('val', selection.indexes); + } + + // refresh the bindable value/item + this.selectedValue = selection.indexes; + this.selectedItem = selection.items; + }); + } +} // class > end + +export class OptionalBindingBehavior { + bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = val => { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + } + + unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + } +} diff --git a/aurelia-bootstrap-select/src/index.js b/aurelia-bootstrap-select/src/index.js new file mode 100644 index 000000000..ca9d830e1 --- /dev/null +++ b/aurelia-bootstrap-select/src/index.js @@ -0,0 +1,17 @@ +import {AbpSelectCustomElement} from './abp-select'; +import {PickerConfig} from './picker-config'; + +export function configure(aurelia, callback) { + aurelia.globalResources('./abp-select'); + + let config = new PickerConfig(); + + if (typeof callback === 'function') { + callback(config); + } +} + +export { + AbpSelectCustomElement, + PickerConfig +}; diff --git a/aurelia-bootstrap-select/src/picker-config.js b/aurelia-bootstrap-select/src/picker-config.js new file mode 100644 index 000000000..2509a75ad --- /dev/null +++ b/aurelia-bootstrap-select/src/picker-config.js @@ -0,0 +1,8 @@ +import {globalExtraOptions, globalPickerOptions} from './picker-global-options'; + +export class PickerConfig { + constructor() { + this.extra = globalExtraOptions; + this.options = globalPickerOptions; + } +} diff --git a/aurelia-bootstrap-select/src/picker-global-options.js b/aurelia-bootstrap-select/src/picker-global-options.js new file mode 100644 index 000000000..81eccdd19 --- /dev/null +++ b/aurelia-bootstrap-select/src/picker-global-options.js @@ -0,0 +1,29 @@ +/** + * Extra options that can be passed to the Custom Element + */ +export let globalExtraOptions = { + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + groupLabel: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } +}; + +/** + * Options that can be passed to the Bootstrap-Select directly + */ +export let globalPickerOptions = { + dropupAuto: true, + showTick: true, + width: 'auto' +}; diff --git a/aurelia-bootstrap-select/src/util-service.js b/aurelia-bootstrap-select/src/util-service.js new file mode 100644 index 000000000..e3e981c6d --- /dev/null +++ b/aurelia-bootstrap-select/src/util-service.js @@ -0,0 +1,79 @@ +export class UtilService { + /** + * Are 2 provided arrays equal + * @param {array} a + * @param {array} b + * @return {bool} is eual + */ + isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + // If you don't care about the order of the elements inside + // the array, you should sort both arrays here. + + for (let i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + + /** + * Are 2 provided variables equal + * @param {any} a + * @param {any} b + * @return {bool} is eual + */ + isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + } + + /** + * Find if the input array is an array of objects or not + * @param {array} input array + * @return {bool} result + */ + isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'object'; + } + + /** + * Find if the input argument is an object or not + * @param {any} argument + * @return {bool} result + */ + isObject(arg) { + return typeof arg === 'object'; + } + + /** + * Find if the input argument is a string or not + * @param {any} argument + * @return {bool} result + */ + isString(arg) { + return typeof arg === 'string' || arg instanceof String; + } + + /** + * Find if the input array is an array of strings or not + * @param {array} input array + * @return {bool} result + */ + isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + } + + /** + * Parse the argument to a boolean output + * @param {any} input value + * @return {bool} result + */ + parseBool(value) { + return (/^(true|1)$/i).test(value); + } +} diff --git a/aurelia-bootstrap-select/test/setup.js b/aurelia-bootstrap-select/test/setup.js new file mode 100644 index 000000000..a518a5e1f --- /dev/null +++ b/aurelia-bootstrap-select/test/setup.js @@ -0,0 +1 @@ +import 'aurelia-polyfills'; \ No newline at end of file diff --git a/aurelia-bootstrap-select/test/unit/configure.spec.js b/aurelia-bootstrap-select/test/unit/configure.spec.js new file mode 100644 index 000000000..9cdb57dcc --- /dev/null +++ b/aurelia-bootstrap-select/test/unit/configure.spec.js @@ -0,0 +1,21 @@ +import {configure} from '../../src/index'; + +class ConfigStub { + globalResources(...resources) { + this.resources = resources; + } +} + +describe('the Aurelia configuration', () => { + var mockedConfiguration; + + beforeEach(() => { + mockedConfiguration = new ConfigStub(); + configure(mockedConfiguration); + }); + + it('should register a global resource', () => { + expect(mockedConfiguration.resources).toContain('./hello-world'); + }); + +}); diff --git a/client-cli/aurelia_project/aurelia.json b/client-cli/aurelia_project/aurelia.json index 6dacef752..3e9142dae 100644 --- a/client-cli/aurelia_project/aurelia.json +++ b/client-cli/aurelia_project/aurelia.json @@ -126,6 +126,20 @@ "css/bootstrap.css" ] }, + { + "name": "bootstrap-select", + "path": "../node_modules/bootstrap-select/dist", + "main": "js/bootstrap-select.min", + "resources": [ + "css/bootstrap-select.min.css" + ] + }, + { + "name": "aurelia-bootstrap-select", + "path": "../node_modules/aurelia-bootstrap-select/dist/amd", + "main": "index", + "resources": ["**/*.{css,html}"] + }, { "name": "bootstrap-tagsinput", "path": "../node_modules/bootstrap-tagsinput", diff --git a/client-cli/index.html b/client-cli/index.html index 09e368e9d..ce573c9fd 100644 --- a/client-cli/index.html +++ b/client-cli/index.html @@ -7,6 +7,7 @@ + diff --git a/client-cli/package.json b/client-cli/package.json index 44d4289c5..6b659a7fc 100644 --- a/client-cli/package.json +++ b/client-cli/package.json @@ -22,10 +22,13 @@ "dependencies": { "aurelia-bootstrap-datetimepicker": "^1.0.2", "aurelia-bootstrap-tagsinput": "^1.0.0", + "aurelia-bootstrap-select": "^0.1.0", "aurelia-bootstrapper": "^1.0.0", + "aurelia-cli": "^0.28.0", "aurelia-http-client": "^1.0.4", "bluebird": "^3.4.1", "bootstrap": "^3.3.7", + "bootstrap-select": "^1.12.2", "eonasdan-bootstrap-datetimepicker": "^4.17.46", "font-awesome": "^4.7.0", "jquery": "^3.1.0", @@ -33,7 +36,6 @@ }, "peerDependencies": {}, "devDependencies": { - "aurelia-cli": "^0.24.0", "aurelia-testing": "^1.0.0-beta.2.0.0", "aurelia-tools": "^0.2.4", "babel-eslint": "^6.1.2", diff --git a/client-cli/src/bootstrap-plugins.html b/client-cli/src/bootstrap-plugins.html index fc3a1a5ad..b823768a8 100644 --- a/client-cli/src/bootstrap-plugins.html +++ b/client-cli/src/bootstrap-plugins.html @@ -58,6 +58,54 @@

Eonasdan - Date Time Picker

+ +
+
+
+ +

Bootstrap-Select

+
+
+ +
Value: ${camping}
Item: ${campingValue}
+
+ +
+ + +
Value: ${condimentValue}
Item: ${condiment | stringify}
+
+ +
+ + +
Value: ${condimentStyledValue}
Item: ${condimentStyled | stringify}
+
+ +
+ + +
Values: ${picnicValue}
Items: ${picnic | stringify}
+
+ +
+ +
+
+ + + +
+
diff --git a/client-cli/src/bootstrap-plugins.js b/client-cli/src/bootstrap-plugins.js index 146e24998..63c003666 100644 --- a/client-cli/src/bootstrap-plugins.js +++ b/client-cli/src/bootstrap-plugins.js @@ -1,31 +1,76 @@ -import {bindable, inject} from "aurelia-framework"; -import moment from 'moment'; +import {bindable, inject} from 'aurelia-framework'; +@inject() export class Edit { @bindable picker; + @bindable selectCamping; + @bindable selectCondiment; + @bindable selectStyledCondiment; + @bindable selectPicnic; @bindable tag; - pickerOptions = {allowInputToggle: true}; dateEntered = null; post = {}; picker; tag; isEditing = false; + isOptgroupBreadDisabled = false; + selectMappingStructure = { + subtext: 'company' + }; + allCampingStuff = ['Tent', 'Flashlight', 'Sleeping Bag']; + allSelectionWithGroups = [ + { id: 1, option: 'Relish', company: 'Sweet', group: 'Condiments' }, + { id: 12, option: 'Steam', group: 'Breads' }, + { id: 11, option: 'Plain', disabled: false, group: 'Breads' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', group: 'Condiments' }, + { id: 3, option: 'Ketchup', company: 'Heinz', group: 'Condiments' }, + { id: 2, option: 'Mustard', company: 'French\'s', group: 'Condiments' }, + { id: 13, option: 'Toasted', group: 'Breads', disabled: true } + ]; + allCondiments = [ + { id: 1, option: 'Ketchup', company: 'Heinz' }, + { id: 2, option: 'Mustard', company: 'French\'s', divider: true }, + { id: 3, option: 'Relish', company: 'Sweet', style: 'background: #5cb85c; color: #fff;', title: 'Alternate Title' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', icon: 'glyphicon-heart' } + ]; + allStyledCondiments = [ + { id: 1, option: 'Mustard', company: 'French\'s', content: 'Mustard' }, + { id: 2, option: 'Ketchup', company: 'Heinz', content: 'Ketchup' }, + { id: 3, option: 'Relish', company: 'Sweet', content: 'Relish' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', disabled: true, content: 'Mayonnaise' } + ]; + selectOptions = { + liveSearch: true, + showSubtext: true, + showTick: true, + selectedTextFormat: 'count > 3' + }; constructor() { this.post = { - categories: 'News,Javascript', - dateEntered: '2005-05-05 10:00' - } + categories: 'News,Javascript,C#' + }; + this.myDateObject = new Date(2017, 1, 1); + this.camping = 'Flashlight'; + // this.picnic = [{ id: 2}, {id: 4 }]; + // this.condiment = { id: 4 }; + // this.campingValue = 'Sleeping Bag'; + this.picnicValue = [1, 2]; + // this.condimentValue = 3; } pickerChanged() { this.picker.events.onChange = (e) => console.log('onChange'); this.picker.events.onUpdate = (e) => console.log('onUpdate'); - this.picker.methods.daysOfWeekDisabled([0,6]); // disable Sunday & Saturday + this.picker.methods.daysOfWeekDisabled([0, 6]); // disable Sunday & Saturday } - tagChanged() { + selectPicnicChanged() { + this.selectPicnic.events.onChanged = (e) => console.log('onChanged'); + } + + tagChanged() { this.tag.events.onBeforeItemAdd = (e) => console.log('onBeforeItemAdd'); this.tag.events.onBeforeItemRemove = (e) => console.log('onBeforeItemRemove'); this.tag.events.onItemAdded = (e) => console.log('onItemAdded'); @@ -45,6 +90,11 @@ export class Edit { this.myDateObject = new Date(dateStr); } + toggleOptgroupBreads() { + this.isOptgroupBreadDisabled = !this.isOptgroupBreadDisabled; + this.selectPicnic.methods.disableOptgroupByLabel('Breads', this.isOptgroupBreadDisabled); + } + removeAllTag() { setTimeout(() => this.tag.methods.removeAll(), 1000); } @@ -52,4 +102,19 @@ export class Edit { removeTag(tagName) { this.tag.methods.remove(tagName); } + + preSelectFirstOptions() { + // Change selection by item (object/string) + this.camping = 'Tent'; + this.picnic = [ { 'id': 2, 'option': 'Mustard', 'company': 'French\'s' } ]; + this.condiment = { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', icon: 'glyphicon-heart' }; + this.condimentStyled = { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', disabled: true, content: 'Mayonnaise' }; + } + preSelectSecondOptions() { + // Change selection by value (id) + this.campingValue = 'Sleeping Bag'; + this.picnicValue = [1, 3, 4, 12]; + this.condimentValue = 3; + this.condimentStyledValue = 3; + } } diff --git a/client-cli/src/main.js b/client-cli/src/main.js index 3becdcd88..2a7c5e78d 100644 --- a/client-cli/src/main.js +++ b/client-cli/src/main.js @@ -12,6 +12,7 @@ Promise.config({ export function configure(aurelia) { aurelia.use .standardConfiguration() + .feature('resources') .plugin('aurelia-bootstrap-datetimepicker', config => { // extra attributes, with config.extra config.extra.iconBase = 'font-awesome'; @@ -20,6 +21,7 @@ export function configure(aurelia) { // or even any picker options, with config.options config.options.showTodayButton = true; }) + .plugin('aurelia-bootstrap-select') .plugin('aurelia-bootstrap-tagsinput'); if (environment.debug) { diff --git a/client-cli/src/resources/behaviors/optional.js b/client-cli/src/resources/behaviors/optional.js new file mode 100644 index 000000000..ad601156b --- /dev/null +++ b/client-cli/src/resources/behaviors/optional.js @@ -0,0 +1,21 @@ +export class OptionalBindingBehavior { + bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = val => { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + } + + unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + } +} \ No newline at end of file diff --git a/client-cli/src/resources/elements/bootstrap-select.html b/client-cli/src/resources/elements/bootstrap-select.html new file mode 100644 index 000000000..f94194297 --- /dev/null +++ b/client-cli/src/resources/elements/bootstrap-select.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/client-cli/src/resources/elements/bootstrap-select.js b/client-cli/src/resources/elements/bootstrap-select.js new file mode 100644 index 000000000..3fc93258f --- /dev/null +++ b/client-cli/src/resources/elements/bootstrap-select.js @@ -0,0 +1,81 @@ +import {inject, bindable, bindingMode, observable} from 'aurelia-framework'; +import $ from 'jquery'; +import 'bootstrap-select'; + +@inject(Element) +export class BootstrapSelect { + @bindable items; + @bindable({ defaultBindingMode: bindingMode.twoWay }) value; + @bindable({ defaultBindingMode: bindingMode.twoWay }) selectedItem; + @bindable options = {}; + @observable selectedIndex = 0; + + constructor(element) { + this.element = element; + } + + + selectedIndexChanged(newValue, oldValue) { + console.log("selectedIndexChanged"); + console.log(newValue); + console.log(this.items); + this.value = this.items[this.selectedIndex]; + $(this.element.firstElementChild).selectpicker('refresh'); + console.log(this.value); + } + + valueChanged(newValue, oldValue) { + console.log("valueChanged"); + console.log(newValue); + console.log("oldValue"); + console.log(oldValue); + this.selectedIndex = this.items.indexOf(this.value); + if(this.selectedIndex >= 0) { + this.selectedItem = this.value; + //$(this.element.firstElementChild).selectpicker('val', this.selectedIndex); + } + console.log("value & index"); + console.log(this.value); + console.log(this.selectedIndex); + $(this.element.firstElementChild).selectpicker('refresh'); + } + + optionsChanged(newValue, oldValue) { + console.log("optionsChanged"); + } + + itemsChanged(newValue, oldValue) { + $(this.element.firstElementChild).selectpicker('refresh'); + } + + bind() { + + } + + attached() { + $(this.element.firstElementChild).selectpicker(this.options) + .on('changed.bs.select', event => { + let selectedOptions = event.target.selectedOptions; + this.value = selectedOptions[0].model; + console.log(this.value); + }) + .on('loaded.bs.select', (e) => { + $(this.element.firstElementChild).selectpicker('val', "2"); + }); + + } + + detached() { + $(this.element.firstElementChild).selectpicker('destroy'); + } +} + +export class NumberToStringValueConverter { + toView(value) { + return value.toString(10); + } + + fromView(value) { + return +value; + } +} \ No newline at end of file diff --git a/client-cli/src/resources/elements/bselect.html b/client-cli/src/resources/elements/bselect.html new file mode 100644 index 000000000..918eff241 --- /dev/null +++ b/client-cli/src/resources/elements/bselect.html @@ -0,0 +1,45 @@ + diff --git a/client-cli/src/resources/elements/bselect.js b/client-cli/src/resources/elements/bselect.js new file mode 100644 index 000000000..692845796 --- /dev/null +++ b/client-cli/src/resources/elements/bselect.js @@ -0,0 +1,426 @@ +import {inject, bindable, bindingMode} from 'aurelia-framework'; +import {UtilService} from './util-service'; +import $ from 'jquery'; +import 'bootstrap-select'; +import {globalExtraOptions, globalPickerOptions} from './picker-global-options'; +//import 'bootstrap-select/dist/css/bootstrap-select.min.css'; + +@inject(Element, UtilService) +export class BselectCustomElement { + @bindable({defaultBindingMode: bindingMode.twoWay}) element; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedItem; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedValue; + @bindable class; + @bindable collection = []; + @bindable dataMappingStructure; + @bindable disabled = false; + @bindable emptyOnNull = false; + @bindable hasOptgroup = false; + @bindable multiple = false; + @bindable objectKey = 'id'; + @bindable pickerOptions; + @bindable placeholder; + @bindable selected; + + // events (from the View) + @bindable onChanged; + @bindable onHide; + @bindable onHidden; + @bindable onLoaded; + @bindable onRendered; + @bindable onRefreshed; + @bindable onShow; + @bindable onShown; + + // variables + _originalSelectedIndexes; + _originalSelectedObjects; + + constructor(elm, utilService) { + this.elm = elm; + this.util = utilService; + } + + attached() { + // reference to the DOM element + this.domElm = $(this.elm).find('.selectpicker'); + + // expose events & methods + let events = this.applyExposeEvents(); + let methods = this.exposeMethods(); + + // finally create the bootstrap-select with all options + let pickerOptions = Object.assign({}, globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + // expose the element object to the outside + // this will be useful for calling events/methods/options from the outside + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + } + + /** + * Keep original value(s) that could be passed by the user ViewModel. + * If nothing was passed, it will default to first option of select + */ + bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + let originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + let originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + // make a deep clone copy to avoid object pointer issues + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + } + + /** + * Apply/expose selectpicker events + * Each event has 2 ways of triggering an event (from the View as an attribute or from the ViewModel has a function call) + */ + applyExposeEvents() { + let events = {}; + + this.domElm.on('show.bs.select', (e) => { + if (typeof this.onShow === 'function') { + this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', (e) => { + if (typeof this.onShown === 'function') { + this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', (e) => { + if (typeof this.onHide === 'function') { + this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', (e) => { + if (typeof this.onHidden === 'function') { + this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', (e) => { + if (typeof this.onLoaded === 'function') { + this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', (e) => { + if (typeof this.onRendered === 'function') { + this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', (e) => { + if (typeof this.onRefreshed === 'function') { + this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + if (typeof this.onChanged === 'function') { + this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + } + + /** + * Expose selectpicker methods + */ + exposeMethods() { + let methods = { + deselectAll: () => this.domElm.selectpicker('deselectAll'), + destroy: () => this.domElm.selectpicker('destroy'), + mobile: () => this.domElm.selectpicker('mobile'), + refresh: () => this.domElm.selectpicker('refresh'), + render: () => this.domElm.selectpicker('render'), + val: (value) => this.domElm.selectpicker('val', value), + selectAll: () => this.domElm.selectpicker('selectAll'), + setStyle: (style, isAddingTheClass = true) => { + if (style.includes('btn')) { + let action = isAddingTheClass ? 'add' : 'remove'; + this.domElm.selectpicker('setStyle', style, action); + } else { + this.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + } + + detached() { + this.domElm.selectpicker('destroy'); + } + + /** + * Get the grouped collection by the mapping 'group' property + * @return {array} groupedCollection + */ + getGroupedCollection() { + // group the array by the mapping group property + // ex input: [{ id: 12, option: 'Steam', group: 'Breads' }, { id: 4, option: 'Mayonnaise', group: 'Condiments' }] + // output: { Breads: [{ id: 12, option: 'Steam', group: 'Breads' }], Condiments: [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }]} + let groupingPropName = this.getMappingProperty('group'); + let collectionGroupedAsObject = this.collection.reduce((groups, y) => { + let key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + // then recreate an array with previously found subgroup + // output: [ [{ id: 12, option: 'Steam', group: 'Breads' }], [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }] ] + return Object.keys(collectionGroupedAsObject).map(k => collectionGroupedAsObject[k]); + } + + /** + * Get data structure mapping property + * @param {string} type + * @return {string} mappingPropertyName + */ + getMappingProperty(type) { + let dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + } + + /** + * From an input array, find the mapping property value by an index provided + * Example: getMappingPropertyValueFromIndex(option, 2, 'group') => 'group' or the user custom group + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + } + + /** + * From an input array, find the mapping property value + * Example: getMappingPropertyValue(option, 'divider') => 'divider' or the user custom divider + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValue(inputArray, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + } + + /** + * Get merged data structure mapping + * @return {array} mergedDataMappingStructure + */ + getMergedMappingStructure() { + let dataMappingStructure = Object.assign({}, globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + } + + /** + * Find item(s) in the collection. + * To support the "multiple" selection attribute and to make our life and code easier, we will use arrays to search and return the found item(s). + * The array advantage is that even if we are not using "multiple", we will still have 1 value at the end (the only difference is that it's inside an array). + * @param {array} collection + * @param {any} newValue + * @param {string} objectKey + * @return {object} found selection, the output is an object with structure of selection = { indexes: [], items: [] }; + */ + findItems(collection, newValue, objectKey) { + let foundItems = []; + let searchingItems = []; + let selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + for (let searchItem of searchingItems) { + let searchFilter = this.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + let foundItem = collection.find(item => { + // for comparison, we're using == mostly because indexes are passed as string because of html + return this.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(this.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + } + + return selection; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + } + + /** + * From a selection option (from View), we want to know if the item is selected + * @param {any} option + */ + isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + } + + /** + * Select the item in the UI element from a selection object passed + * The structure is:: selection = { indexes: [], items: [] }; + * @param {object} selection object + */ + renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + } + + /** + * On selected item (ids) changed, we will update the selectedValue(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + // get selected indexes (ids), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + // value could be an object, if so we will use the objectKey (object.id by default) + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + } + + this.renderSelection(selection); + } + } + + /** + * On selected value (string/object) changed, we will update the selectedItem(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + // get selected items (string/object), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + this.selectedItem = (selection.items.length > 0) ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + } + + /** + * onLoaded trigger, we will also check if user pre-selected any option(s). + * Pre-selection of options can be done by index and/or by item object. + * If provided, we will make them part of the select index/item and render selection on UI as well + */ + watchOnLoadedToRenderPreSelection() { + this.domElm.on('loaded.bs.select', (e) => { + let newValue = this._originalSelectedIndexes || this._originalSelectedObjects; + let selection = this.findItems(this.collection, newValue, this.objectKey); + if (selection.indexes) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + this.selectedItem = selection.items ? selection.items : this.collection[0]; + this.renderSelection(selection); + }); + } + + /** + * onChanged trigger, we will update our index(es) and item object(s) + */ + watchOnChangedToUpdateValueAndItemObjects() { + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + this.selectedValue = this.domElm.selectpicker('val'); + let selection = this.findItems(this.collection, this.selectedValue, this.objectKey); + if (selection.indexes) { + this.domElm.selectpicker('val', selection.indexes); + } + + // refresh the bindable value/item + this.selectedValue = selection.indexes; + this.selectedItem = selection.items; + }); + } +} // class > end diff --git a/client-cli/src/resources/elements/picker-global-options.js b/client-cli/src/resources/elements/picker-global-options.js new file mode 100644 index 000000000..d28932ec8 --- /dev/null +++ b/client-cli/src/resources/elements/picker-global-options.js @@ -0,0 +1,30 @@ +/** + * Extra options that can be passed to the Custom Element + */ +export let globalExtraOptions = { + bootstrapVersion: 3, + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + group: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } +}; + +/** + * Options that can be passed to the Bootstrap-Select directly + */ +export let globalPickerOptions = { + dropupAuto: true, + showTick: true, + size: 'auto' +}; diff --git a/client-cli/src/resources/elements/util-service.js b/client-cli/src/resources/elements/util-service.js new file mode 100644 index 000000000..e3e981c6d --- /dev/null +++ b/client-cli/src/resources/elements/util-service.js @@ -0,0 +1,79 @@ +export class UtilService { + /** + * Are 2 provided arrays equal + * @param {array} a + * @param {array} b + * @return {bool} is eual + */ + isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + // If you don't care about the order of the elements inside + // the array, you should sort both arrays here. + + for (let i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + + /** + * Are 2 provided variables equal + * @param {any} a + * @param {any} b + * @return {bool} is eual + */ + isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + } + + /** + * Find if the input array is an array of objects or not + * @param {array} input array + * @return {bool} result + */ + isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'object'; + } + + /** + * Find if the input argument is an object or not + * @param {any} argument + * @return {bool} result + */ + isObject(arg) { + return typeof arg === 'object'; + } + + /** + * Find if the input argument is a string or not + * @param {any} argument + * @return {bool} result + */ + isString(arg) { + return typeof arg === 'string' || arg instanceof String; + } + + /** + * Find if the input array is an array of strings or not + * @param {array} input array + * @return {bool} result + */ + isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + } + + /** + * Parse the argument to a boolean output + * @param {any} input value + * @return {bool} result + */ + parseBool(value) { + return (/^(true|1)$/i).test(value); + } +} diff --git a/client-cli/src/resources/index.js b/client-cli/src/resources/index.js new file mode 100644 index 000000000..d7a84d78a --- /dev/null +++ b/client-cli/src/resources/index.js @@ -0,0 +1,8 @@ +export function configure(config) { + config.globalResources([ + './elements/bselect', + './elements/bootstrap-select', + //'./behaviors/optional.js', + './value-converters/stringify' + ]); +} \ No newline at end of file diff --git a/client-cli/src/resources/value-converters/stringify.js b/client-cli/src/resources/value-converters/stringify.js new file mode 100644 index 000000000..2db10e6b2 --- /dev/null +++ b/client-cli/src/resources/value-converters/stringify.js @@ -0,0 +1,5 @@ +export class StringifyValueConverter { + toView(value) { + return JSON.stringify(value, null, 4 ); + } +} \ No newline at end of file diff --git a/client-wp/package.json b/client-wp/package.json index c8793db31..acfdda8c3 100644 --- a/client-wp/package.json +++ b/client-wp/package.json @@ -61,6 +61,7 @@ }, "dependencies": { "aurelia-bootstrap-datetimepicker": "^1.0.2", + "aurelia-bootstrap-select": "^0.1.0", "aurelia-bootstrap-tagsinput": "^1.0.0", "aurelia-bootstrapper-webpack": "^1.1.0", "aurelia-event-aggregator": "^1.0.1", @@ -80,6 +81,7 @@ "babel-polyfill": "^6.23.0", "bluebird": "^3.4.7", "bootstrap": "^3.3.7", + "bootstrap-select": "^1.12.2", "eonasdan-bootstrap-datetimepicker": "^4.17.46", "font-awesome": "^4.7.0", "isomorphic-fetch": "^2.2.1", @@ -107,6 +109,7 @@ "@easy-webpack/core": "^2.0.1", "aurelia-protractor-plugin": "^1.0.1", "aurelia-tools": "^1.0.0", + "babel-eslint": "^7.2.1", "babel-plugin-transform-class-properties": "^6.23.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-preset-env": "^1.1.10", diff --git a/client-wp/src/bootstrap-plugins.html b/client-wp/src/bootstrap-plugins.html index fc3a1a5ad..b823768a8 100644 --- a/client-wp/src/bootstrap-plugins.html +++ b/client-wp/src/bootstrap-plugins.html @@ -58,6 +58,54 @@

Eonasdan - Date Time Picker

+ +
+
+
+ +

Bootstrap-Select

+
+
+ +
Value: ${camping}
Item: ${campingValue}
+
+ +
+ + +
Value: ${condimentValue}
Item: ${condiment | stringify}
+
+ +
+ + +
Value: ${condimentStyledValue}
Item: ${condimentStyled | stringify}
+
+ +
+ + +
Values: ${picnicValue}
Items: ${picnic | stringify}
+
+ +
+ +
+
+ + + +
+
diff --git a/client-wp/src/bootstrap-plugins.js b/client-wp/src/bootstrap-plugins.js index 146e24998..63c003666 100644 --- a/client-wp/src/bootstrap-plugins.js +++ b/client-wp/src/bootstrap-plugins.js @@ -1,31 +1,76 @@ -import {bindable, inject} from "aurelia-framework"; -import moment from 'moment'; +import {bindable, inject} from 'aurelia-framework'; +@inject() export class Edit { @bindable picker; + @bindable selectCamping; + @bindable selectCondiment; + @bindable selectStyledCondiment; + @bindable selectPicnic; @bindable tag; - pickerOptions = {allowInputToggle: true}; dateEntered = null; post = {}; picker; tag; isEditing = false; + isOptgroupBreadDisabled = false; + selectMappingStructure = { + subtext: 'company' + }; + allCampingStuff = ['Tent', 'Flashlight', 'Sleeping Bag']; + allSelectionWithGroups = [ + { id: 1, option: 'Relish', company: 'Sweet', group: 'Condiments' }, + { id: 12, option: 'Steam', group: 'Breads' }, + { id: 11, option: 'Plain', disabled: false, group: 'Breads' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', group: 'Condiments' }, + { id: 3, option: 'Ketchup', company: 'Heinz', group: 'Condiments' }, + { id: 2, option: 'Mustard', company: 'French\'s', group: 'Condiments' }, + { id: 13, option: 'Toasted', group: 'Breads', disabled: true } + ]; + allCondiments = [ + { id: 1, option: 'Ketchup', company: 'Heinz' }, + { id: 2, option: 'Mustard', company: 'French\'s', divider: true }, + { id: 3, option: 'Relish', company: 'Sweet', style: 'background: #5cb85c; color: #fff;', title: 'Alternate Title' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', icon: 'glyphicon-heart' } + ]; + allStyledCondiments = [ + { id: 1, option: 'Mustard', company: 'French\'s', content: 'Mustard' }, + { id: 2, option: 'Ketchup', company: 'Heinz', content: 'Ketchup' }, + { id: 3, option: 'Relish', company: 'Sweet', content: 'Relish' }, + { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', disabled: true, content: 'Mayonnaise' } + ]; + selectOptions = { + liveSearch: true, + showSubtext: true, + showTick: true, + selectedTextFormat: 'count > 3' + }; constructor() { this.post = { - categories: 'News,Javascript', - dateEntered: '2005-05-05 10:00' - } + categories: 'News,Javascript,C#' + }; + this.myDateObject = new Date(2017, 1, 1); + this.camping = 'Flashlight'; + // this.picnic = [{ id: 2}, {id: 4 }]; + // this.condiment = { id: 4 }; + // this.campingValue = 'Sleeping Bag'; + this.picnicValue = [1, 2]; + // this.condimentValue = 3; } pickerChanged() { this.picker.events.onChange = (e) => console.log('onChange'); this.picker.events.onUpdate = (e) => console.log('onUpdate'); - this.picker.methods.daysOfWeekDisabled([0,6]); // disable Sunday & Saturday + this.picker.methods.daysOfWeekDisabled([0, 6]); // disable Sunday & Saturday } - tagChanged() { + selectPicnicChanged() { + this.selectPicnic.events.onChanged = (e) => console.log('onChanged'); + } + + tagChanged() { this.tag.events.onBeforeItemAdd = (e) => console.log('onBeforeItemAdd'); this.tag.events.onBeforeItemRemove = (e) => console.log('onBeforeItemRemove'); this.tag.events.onItemAdded = (e) => console.log('onItemAdded'); @@ -45,6 +90,11 @@ export class Edit { this.myDateObject = new Date(dateStr); } + toggleOptgroupBreads() { + this.isOptgroupBreadDisabled = !this.isOptgroupBreadDisabled; + this.selectPicnic.methods.disableOptgroupByLabel('Breads', this.isOptgroupBreadDisabled); + } + removeAllTag() { setTimeout(() => this.tag.methods.removeAll(), 1000); } @@ -52,4 +102,19 @@ export class Edit { removeTag(tagName) { this.tag.methods.remove(tagName); } + + preSelectFirstOptions() { + // Change selection by item (object/string) + this.camping = 'Tent'; + this.picnic = [ { 'id': 2, 'option': 'Mustard', 'company': 'French\'s' } ]; + this.condiment = { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', icon: 'glyphicon-heart' }; + this.condimentStyled = { id: 4, option: 'Mayonnaise', company: 'Miracle Whip', disabled: true, content: 'Mayonnaise' }; + } + preSelectSecondOptions() { + // Change selection by value (id) + this.campingValue = 'Sleeping Bag'; + this.picnicValue = [1, 3, 4, 12]; + this.condimentValue = 3; + this.condimentStyledValue = 3; + } } diff --git a/client-wp/src/main.js b/client-wp/src/main.js index db6da2610..9a7f0ca0b 100644 --- a/client-wp/src/main.js +++ b/client-wp/src/main.js @@ -2,6 +2,7 @@ import '../styles/styles.css'; import 'font-awesome/css/font-awesome.css'; import 'bootstrap/dist/css/bootstrap.css'; +import 'bootstrap-select/dist/css/bootstrap-select.min.css'; import 'bootstrap-tagsinput/dist/bootstrap-tagsinput.css'; import 'eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css'; import 'bootstrap'; @@ -13,6 +14,7 @@ Bluebird.config({ warnings: false }); export async function configure(aurelia) { aurelia.use .standardConfiguration() + .feature('resources') .plugin('aurelia-bootstrap-datetimepicker', config => { // extra attributes, with config.extra config.extra.iconBase = 'font-awesome'; diff --git a/client-wp/src/resources/behaviors/optional.js b/client-wp/src/resources/behaviors/optional.js new file mode 100644 index 000000000..ad601156b --- /dev/null +++ b/client-wp/src/resources/behaviors/optional.js @@ -0,0 +1,21 @@ +export class OptionalBindingBehavior { + bind(binding, scope, interceptor) { + binding.originalupdateTarget = binding.updateTarget; + binding.originalTargetProperty = binding.targetProperty; + binding.updateTarget = val => { + if (val === undefined || val === null || val === '') { + binding.targetProperty = null; + } else { + binding.targetProperty = binding.originalTargetProperty; + } + binding.originalupdateTarget(val); + }; + } + + unbind(binding, scope) { + binding.updateTarget = binding.originalupdateTarget; + binding.originalupdateTarget = null; + binding.targetProperty = binding.originalTargetProperty; + binding.originalTargetProperty = null; + } +} \ No newline at end of file diff --git a/client-wp/src/resources/elements/bootstrap-select.html b/client-wp/src/resources/elements/bootstrap-select.html new file mode 100644 index 000000000..f94194297 --- /dev/null +++ b/client-wp/src/resources/elements/bootstrap-select.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/client-wp/src/resources/elements/bootstrap-select.js b/client-wp/src/resources/elements/bootstrap-select.js new file mode 100644 index 000000000..3fc93258f --- /dev/null +++ b/client-wp/src/resources/elements/bootstrap-select.js @@ -0,0 +1,81 @@ +import {inject, bindable, bindingMode, observable} from 'aurelia-framework'; +import $ from 'jquery'; +import 'bootstrap-select'; + +@inject(Element) +export class BootstrapSelect { + @bindable items; + @bindable({ defaultBindingMode: bindingMode.twoWay }) value; + @bindable({ defaultBindingMode: bindingMode.twoWay }) selectedItem; + @bindable options = {}; + @observable selectedIndex = 0; + + constructor(element) { + this.element = element; + } + + + selectedIndexChanged(newValue, oldValue) { + console.log("selectedIndexChanged"); + console.log(newValue); + console.log(this.items); + this.value = this.items[this.selectedIndex]; + $(this.element.firstElementChild).selectpicker('refresh'); + console.log(this.value); + } + + valueChanged(newValue, oldValue) { + console.log("valueChanged"); + console.log(newValue); + console.log("oldValue"); + console.log(oldValue); + this.selectedIndex = this.items.indexOf(this.value); + if(this.selectedIndex >= 0) { + this.selectedItem = this.value; + //$(this.element.firstElementChild).selectpicker('val', this.selectedIndex); + } + console.log("value & index"); + console.log(this.value); + console.log(this.selectedIndex); + $(this.element.firstElementChild).selectpicker('refresh'); + } + + optionsChanged(newValue, oldValue) { + console.log("optionsChanged"); + } + + itemsChanged(newValue, oldValue) { + $(this.element.firstElementChild).selectpicker('refresh'); + } + + bind() { + + } + + attached() { + $(this.element.firstElementChild).selectpicker(this.options) + .on('changed.bs.select', event => { + let selectedOptions = event.target.selectedOptions; + this.value = selectedOptions[0].model; + console.log(this.value); + }) + .on('loaded.bs.select', (e) => { + $(this.element.firstElementChild).selectpicker('val', "2"); + }); + + } + + detached() { + $(this.element.firstElementChild).selectpicker('destroy'); + } +} + +export class NumberToStringValueConverter { + toView(value) { + return value.toString(10); + } + + fromView(value) { + return +value; + } +} \ No newline at end of file diff --git a/client-wp/src/resources/elements/bselect.html b/client-wp/src/resources/elements/bselect.html new file mode 100644 index 000000000..918eff241 --- /dev/null +++ b/client-wp/src/resources/elements/bselect.html @@ -0,0 +1,45 @@ + diff --git a/client-wp/src/resources/elements/bselect.js b/client-wp/src/resources/elements/bselect.js new file mode 100644 index 000000000..692845796 --- /dev/null +++ b/client-wp/src/resources/elements/bselect.js @@ -0,0 +1,426 @@ +import {inject, bindable, bindingMode} from 'aurelia-framework'; +import {UtilService} from './util-service'; +import $ from 'jquery'; +import 'bootstrap-select'; +import {globalExtraOptions, globalPickerOptions} from './picker-global-options'; +//import 'bootstrap-select/dist/css/bootstrap-select.min.css'; + +@inject(Element, UtilService) +export class BselectCustomElement { + @bindable({defaultBindingMode: bindingMode.twoWay}) element; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedItem; + @bindable({defaultBindingMode: bindingMode.twoWay}) selectedValue; + @bindable class; + @bindable collection = []; + @bindable dataMappingStructure; + @bindable disabled = false; + @bindable emptyOnNull = false; + @bindable hasOptgroup = false; + @bindable multiple = false; + @bindable objectKey = 'id'; + @bindable pickerOptions; + @bindable placeholder; + @bindable selected; + + // events (from the View) + @bindable onChanged; + @bindable onHide; + @bindable onHidden; + @bindable onLoaded; + @bindable onRendered; + @bindable onRefreshed; + @bindable onShow; + @bindable onShown; + + // variables + _originalSelectedIndexes; + _originalSelectedObjects; + + constructor(elm, utilService) { + this.elm = elm; + this.util = utilService; + } + + attached() { + // reference to the DOM element + this.domElm = $(this.elm).find('.selectpicker'); + + // expose events & methods + let events = this.applyExposeEvents(); + let methods = this.exposeMethods(); + + // finally create the bootstrap-select with all options + let pickerOptions = Object.assign({}, globalPickerOptions, this.pickerOptions || {}); + this.domElm.selectpicker(pickerOptions); + + // expose the element object to the outside + // this will be useful for calling events/methods/options from the outside + this.element = { + events: events, + options: pickerOptions, + methods: methods, + dataMappingStructure: this.dataMappingStructure + }; + + this.watchOnLoadedToRenderPreSelection(); + this.watchOnChangedToUpdateValueAndItemObjects(); + } + + /** + * Keep original value(s) that could be passed by the user ViewModel. + * If nothing was passed, it will default to first option of select + */ + bind() { + this.multiple = this.util.parseBool(this.multiple || this.elm.getAttribute('multiple')); + let originalSelectedObjects = this.selectedItem || this.elm.getAttribute('selectedItem'); + let originalSelectedIndexes = this.selectedValue || this.elm.getAttribute('selectedValue'); + + // make a deep clone copy to avoid object pointer issues + this._originalSelectedObjects = originalSelectedObjects ? JSON.parse(JSON.stringify(originalSelectedObjects)) : null; + this._originalSelectedIndexes = originalSelectedIndexes ? JSON.parse(JSON.stringify(originalSelectedIndexes)) : null; + } + + /** + * Apply/expose selectpicker events + * Each event has 2 ways of triggering an event (from the View as an attribute or from the ViewModel has a function call) + */ + applyExposeEvents() { + let events = {}; + + this.domElm.on('show.bs.select', (e) => { + if (typeof this.onShow === 'function') { + this.onShow(e); + } + if (typeof events.onShow === 'function') { + events.onShow(e); + } + }); + + this.domElm.on('shown.bs.select', (e) => { + if (typeof this.onShown === 'function') { + this.onShown(e); + } + if (typeof events.onShown === 'function') { + events.onShown(e); + } + }); + + this.domElm.on('hide.bs.select', (e) => { + if (typeof this.onHide === 'function') { + this.onHide(e); + } + if (typeof events.onHide === 'function') { + events.onHide(e); + } + }); + + this.domElm.on('hidden.bs.select', (e) => { + if (typeof this.onHidden === 'function') { + this.onHidden(e); + } + if (typeof events.onHidden === 'function') { + events.onHidden(e); + } + }); + + this.domElm.on('loaded.bs.select', (e) => { + if (typeof this.onLoaded === 'function') { + this.onLoaded(e); + } + if (typeof events.onLoaded === 'function') { + events.onLoaded(e); + } + }); + + this.domElm.on('rendered.bs.select', (e) => { + if (typeof this.onRendered === 'function') { + this.onRendered(e); + } + if (typeof events.onRendered === 'function') { + events.onRendered(e); + } + }); + + this.domElm.on('refreshed.bs.select', (e) => { + if (typeof this.onRefreshed === 'function') { + this.onRefreshed(e); + } + if (typeof events.onRefreshed === 'function') { + events.onRefreshed(e); + } + }); + + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + if (typeof this.onChanged === 'function') { + this.onChanged(e); + } + if (typeof events.onChanged === 'function') { + events.onChanged(e); + } + }); + + return events; + } + + /** + * Expose selectpicker methods + */ + exposeMethods() { + let methods = { + deselectAll: () => this.domElm.selectpicker('deselectAll'), + destroy: () => this.domElm.selectpicker('destroy'), + mobile: () => this.domElm.selectpicker('mobile'), + refresh: () => this.domElm.selectpicker('refresh'), + render: () => this.domElm.selectpicker('render'), + val: (value) => this.domElm.selectpicker('val', value), + selectAll: () => this.domElm.selectpicker('selectAll'), + setStyle: (style, isAddingTheClass = true) => { + if (style.includes('btn')) { + let action = isAddingTheClass ? 'add' : 'remove'; + this.domElm.selectpicker('setStyle', style, action); + } else { + this.domElm.addClass(style).selectpicker('setStyle'); + } + } + }; + + return methods; + } + + detached() { + this.domElm.selectpicker('destroy'); + } + + /** + * Get the grouped collection by the mapping 'group' property + * @return {array} groupedCollection + */ + getGroupedCollection() { + // group the array by the mapping group property + // ex input: [{ id: 12, option: 'Steam', group: 'Breads' }, { id: 4, option: 'Mayonnaise', group: 'Condiments' }] + // output: { Breads: [{ id: 12, option: 'Steam', group: 'Breads' }], Condiments: [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }]} + let groupingPropName = this.getMappingProperty('group'); + let collectionGroupedAsObject = this.collection.reduce((groups, y) => { + let key = y[groupingPropName]; + (groups[key] = groups[key] || []).push(y); + + return groups; + }, {}); + + // then recreate an array with previously found subgroup + // output: [ [{ id: 12, option: 'Steam', group: 'Breads' }], [{ id: 4, option: 'Mayonnaise', group: 'Condiments' }] ] + return Object.keys(collectionGroupedAsObject).map(k => collectionGroupedAsObject[k]); + } + + /** + * Get data structure mapping property + * @param {string} type + * @return {string} mappingPropertyName + */ + getMappingProperty(type) { + let dataMappingStructure = this.getMergedMappingStructure(); + return dataMappingStructure[type]; + } + + /** + * From an input array, find the mapping property value by an index provided + * Example: getMappingPropertyValueFromIndex(option, 2, 'group') => 'group' or the user custom group + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValueFromIndex(inputArray, arrayIndex, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray[arrayIndex].hasOwnProperty(propertyName) ? inputArray[arrayIndex][propertyName] : ''; + } + + /** + * From an input array, find the mapping property value + * Example: getMappingPropertyValue(option, 'divider') => 'divider' or the user custom divider + * @param {array} input array + * @param {string} search property name + * @return {any} found item (stringo/object) + */ + getMappingPropertyValue(inputArray, searchPropName) { + let propertyName = this.getMappingProperty(searchPropName); + return inputArray.hasOwnProperty(propertyName) ? inputArray[propertyName] : ''; + } + + /** + * Get merged data structure mapping + * @return {array} mergedDataMappingStructure + */ + getMergedMappingStructure() { + let dataMappingStructure = Object.assign({}, globalExtraOptions.mappingDataStructure, this.dataMappingStructure || {}); + return dataMappingStructure; + } + + /** + * Find item(s) in the collection. + * To support the "multiple" selection attribute and to make our life and code easier, we will use arrays to search and return the found item(s). + * The array advantage is that even if we are not using "multiple", we will still have 1 value at the end (the only difference is that it's inside an array). + * @param {array} collection + * @param {any} newValue + * @param {string} objectKey + * @return {object} found selection, the output is an object with structure of selection = { indexes: [], items: [] }; + */ + findItems(collection, newValue, objectKey) { + let foundItems = []; + let searchingItems = []; + let selection = { + indexes: [], + items: [] + }; + if (newValue === null || newValue === undefined) { + return selection; + } + + if (!Array.isArray(newValue)) { + searchingItems.push(newValue); + } else { + searchingItems = newValue; + } + + for (let searchItem of searchingItems) { + let searchFilter = this.util.isObject(searchItem) ? searchItem[objectKey] : searchItem; + let foundItem = collection.find(item => { + // for comparison, we're using == mostly because indexes are passed as string because of html + return this.util.isObject(item) ? item[objectKey] == searchFilter : item == searchFilter; + }); + if (foundItem) { + selection.indexes.push(this.util.isObject(foundItem) ? foundItem[objectKey] : foundItem); + selection.items.push(foundItem); + foundItems.push(foundItem); + } + } + + return selection; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length === 0 && selection.indexes.length === 0; + } + + /** + * From the selection object provided, we want to know if the selection is empty + * The structure is:: selection = { indexes: [], items: [] }; + * @param {obejct} selection object + */ + isNotEmptySelection(selection) { + if (!selection) { + return true; + } + return selection.items.length > 0 && selection.indexes.length > 0; + } + + /** + * From a selection option (from View), we want to know if the item is selected + * @param {any} option + */ + isSelected(option) { + if (option === this._originalSelectedIndexes || option === this._originalSelectedObjects) { + return true; + } + return false; + } + + /** + * Select the item in the UI element from a selection object passed + * The structure is:: selection = { indexes: [], items: [] }; + * @param {object} selection object + */ + renderSelection(selection) { + if (selection.indexes.length > 0) { + this.domElm.selectpicker('val', selection.indexes); + } else if (this.util.parseBool(this.emptyOnNull) && this.isEmptySelection(selection)) { + this.domElm.selectpicker('val', null); + } + } + + /** + * On selected item (ids) changed, we will update the selectedValue(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedItemChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedIndexes, this.objectKey); + + // get selected indexes (ids), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + if (selection.indexes.length > 0) { + this.selectedValue = selection.indexes; + } else { + // value could be an object, if so we will use the objectKey (object.id by default) + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + } + + this.renderSelection(selection); + } + } + + /** + * On selected value (string/object) changed, we will update the selectedItem(s) unless user chose emptyOnNull on first pass. + * We will also render the selection with new value(s) + * @param {any} newValue + * @param {any} oldValue + */ + selectedValueChanged(newValue, oldValue) { + if (!this.util.isEqual(newValue, oldValue)) { + let selection = this.findItems(this.collection, newValue || this._originalSelectedObjects, this.objectKey); + + // get selected items (string/object), unless user chose to emptyOnNull on first pass + if ((!this.util.parseBool(this.emptyOnNull) && !this.multiple) || this.isNotEmptySelection(selection)) { + this.selectedItem = (selection.items.length > 0) ? selection.items : this.collection[0]; + } + + this.renderSelection(selection); + } + } + + /** + * onLoaded trigger, we will also check if user pre-selected any option(s). + * Pre-selection of options can be done by index and/or by item object. + * If provided, we will make them part of the select index/item and render selection on UI as well + */ + watchOnLoadedToRenderPreSelection() { + this.domElm.on('loaded.bs.select', (e) => { + let newValue = this._originalSelectedIndexes || this._originalSelectedObjects; + let selection = this.findItems(this.collection, newValue, this.objectKey); + if (selection.indexes) { + this.selectedValue = selection.indexes; + } else { + this.selectedValue = (this.util.isObject(this.collection[0]) ? this.collection[0][this.objectKey] : this.collection[0]); + } + this.selectedItem = selection.items ? selection.items : this.collection[0]; + this.renderSelection(selection); + }); + } + + /** + * onChanged trigger, we will update our index(es) and item object(s) + */ + watchOnChangedToUpdateValueAndItemObjects() { + this.domElm.on('changed.bs.select', (e, clickedIndex, newValue, oldValue) => { + this.selectedValue = this.domElm.selectpicker('val'); + let selection = this.findItems(this.collection, this.selectedValue, this.objectKey); + if (selection.indexes) { + this.domElm.selectpicker('val', selection.indexes); + } + + // refresh the bindable value/item + this.selectedValue = selection.indexes; + this.selectedItem = selection.items; + }); + } +} // class > end diff --git a/client-wp/src/resources/elements/picker-global-options.js b/client-wp/src/resources/elements/picker-global-options.js new file mode 100644 index 000000000..d28932ec8 --- /dev/null +++ b/client-wp/src/resources/elements/picker-global-options.js @@ -0,0 +1,30 @@ +/** + * Extra options that can be passed to the Custom Element + */ +export let globalExtraOptions = { + bootstrapVersion: 3, + mappingDataStructure: { + class: 'class', + content: 'content', + disabled: 'disabled', + divider: 'divider', + group: 'group', + groupDisabled: 'disabled', + icon: 'icon', + maxOptions: 'maxOptions', + option: 'option', + subtext: 'subtext', + style: 'style', + title: 'title', + tokens: 'tokens' + } +}; + +/** + * Options that can be passed to the Bootstrap-Select directly + */ +export let globalPickerOptions = { + dropupAuto: true, + showTick: true, + size: 'auto' +}; diff --git a/client-wp/src/resources/elements/util-service.js b/client-wp/src/resources/elements/util-service.js new file mode 100644 index 000000000..e3e981c6d --- /dev/null +++ b/client-wp/src/resources/elements/util-service.js @@ -0,0 +1,79 @@ +export class UtilService { + /** + * Are 2 provided arrays equal + * @param {array} a + * @param {array} b + * @return {bool} is eual + */ + isArrayEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + // If you don't care about the order of the elements inside + // the array, you should sort both arrays here. + + for (let i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + + /** + * Are 2 provided variables equal + * @param {any} a + * @param {any} b + * @return {bool} is eual + */ + isEqual(a, b) { + if (Array.isArray(a)) { + return this.isArrayEqual(a, b); + } + return a === b; + } + + /** + * Find if the input array is an array of objects or not + * @param {array} input array + * @return {bool} result + */ + isObjectArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'object'; + } + + /** + * Find if the input argument is an object or not + * @param {any} argument + * @return {bool} result + */ + isObject(arg) { + return typeof arg === 'object'; + } + + /** + * Find if the input argument is a string or not + * @param {any} argument + * @return {bool} result + */ + isString(arg) { + return typeof arg === 'string' || arg instanceof String; + } + + /** + * Find if the input array is an array of strings or not + * @param {array} input array + * @return {bool} result + */ + isStringArray(inputArrray) { + return Array.isArray(inputArrray) && inputArrray.length > 0 && typeof inputArrray[0] === 'string'; + } + + /** + * Parse the argument to a boolean output + * @param {any} input value + * @return {bool} result + */ + parseBool(value) { + return (/^(true|1)$/i).test(value); + } +} diff --git a/client-wp/src/resources/index.js b/client-wp/src/resources/index.js new file mode 100644 index 000000000..d7a84d78a --- /dev/null +++ b/client-wp/src/resources/index.js @@ -0,0 +1,8 @@ +export function configure(config) { + config.globalResources([ + './elements/bselect', + './elements/bootstrap-select', + //'./behaviors/optional.js', + './value-converters/stringify' + ]); +} \ No newline at end of file diff --git a/client-wp/src/resources/value-converters/stringify.js b/client-wp/src/resources/value-converters/stringify.js new file mode 100644 index 000000000..2db10e6b2 --- /dev/null +++ b/client-wp/src/resources/value-converters/stringify.js @@ -0,0 +1,5 @@ +export class StringifyValueConverter { + toView(value) { + return JSON.stringify(value, null, 4 ); + } +} \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..bbdd436a2 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "amd", + "target": "ES6" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..bbdd436a2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "module": "amd", + "target": "ES6" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file