├── .editorconfig ├── .ember-cli ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── components │ ├── carousel-component.js │ ├── color-picker-cell.js │ ├── color-picker-dropdown.js │ ├── color-picker.js │ ├── debounced-text-component.js │ ├── editable-label-component.js │ ├── modal-component.js │ ├── multi-select-component.js │ ├── popover-component.js │ ├── popover-link-component.js │ ├── popover-modal-base.js │ ├── radio-button-group-component.js │ ├── radio-button.js │ ├── radio-input.js │ ├── render-popover.js │ ├── select-component.js │ └── typeahead-component.js ├── mixins │ ├── body-event-listener.js │ ├── color-picker.js │ ├── keyboard-helper.js │ ├── new-popover-modal-api.js │ ├── popover.js │ ├── style-bindings.js │ └── tabbable-modal.js ├── mysterious-dependency │ └── ember-addepar-mixins │ │ ├── README.md │ │ └── resize_handler.js ├── services │ └── popover.js ├── styles │ └── addon.css ├── utils │ ├── color-picker.js │ └── widget-config.js └── views │ ├── carousel-indicator.js │ ├── carousel-item.js │ ├── multi-select-item.js │ ├── multi-select-option.js │ ├── multi-select-tooltip-item.js │ ├── select-option.js │ ├── select-tooltip-option.js │ └── view-parent-view-content.js ├── app ├── .gitkeep ├── components │ ├── carousel-component.js │ ├── color-picker-cell.js │ ├── color-picker-dropdown.js │ ├── color-picker.js │ ├── debounced-text-component.js │ ├── editable-label-component.js │ ├── modal-component.js │ ├── multi-select-component.js │ ├── popover-component.js │ ├── popover-link-component.js │ ├── radio-button-group-component.js │ ├── radio-button.js │ ├── radio-input.js │ ├── render-popover.js │ ├── select-component.js │ └── typeahead-component.js ├── services │ └── popover.js ├── templates │ ├── accordion-group-layout.hbs │ ├── carousel.hbs │ ├── color-picker-button-partial.hbs │ ├── color-picker-cell.hbs │ ├── color-picker-dropdown.hbs │ ├── color-picker.hbs │ ├── component-default-content.hbs │ ├── components │ │ └── render-popover.hbs │ ├── editable-label.hbs │ ├── modal-content.hbs │ ├── modal-footer.hbs │ ├── modal-header.hbs │ ├── modal.hbs │ ├── multi-select-item.hbs │ ├── multi-select.hbs │ ├── popover-link-popover.hbs │ ├── popover.hbs │ ├── radio-button-layout.hbs │ ├── select-item-layout.hbs │ ├── select-item.hbs │ ├── select-list-view-partial.hbs │ ├── select.hbs │ ├── typeahead-list-view-partial.hbs │ ├── typeahead.hbs │ └── view-parent-view-content.hbs └── views │ ├── carousel-item.js │ ├── multi-select-option.js │ ├── select-option.js │ └── select-tooltip-option.js ├── bower.json ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── public └── img │ ├── invalid_color.svg │ ├── no_color.svg │ ├── select2.png │ └── spinner.gif ├── testem.js ├── tests ├── .eslintrc.js ├── .jshintrc ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── ember-widgets │ │ │ │ └── popover.js │ │ ├── data │ │ │ └── countries.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ ├── .gitkeep │ │ │ ├── ember-widgets │ │ │ │ ├── index.js │ │ │ │ ├── modal.js │ │ │ │ ├── overview.js │ │ │ │ ├── popover.js │ │ │ │ └── select.js │ │ │ ├── index.js │ │ │ ├── overview.js │ │ │ └── select-box.js │ │ ├── styles │ │ │ └── app.css │ │ ├── templates │ │ │ ├── _footer.hbs │ │ │ ├── _navigation.hbs │ │ │ ├── application.hbs │ │ │ ├── custom-modal-content.hbs │ │ │ ├── custom-popover-content.hbs │ │ │ ├── ember-widgets │ │ │ │ ├── -sub-navigation.hbs │ │ │ │ ├── -test-popover-content.hbs │ │ │ │ ├── carousel.hbs │ │ │ │ ├── color-picker.hbs │ │ │ │ ├── documentation.hbs │ │ │ │ ├── modal.hbs │ │ │ │ ├── overview.hbs │ │ │ │ ├── popover.hbs │ │ │ │ ├── radio-button.hbs │ │ │ │ ├── select.hbs │ │ │ │ └── typahead.hbs │ │ │ ├── ember_widgets.hbs │ │ │ ├── license.hbs │ │ │ ├── test-modal-content.hbs │ │ │ ├── test-modal-footer.hbs │ │ │ └── test-modal-header.hbs │ │ └── views │ │ │ ├── custom-modal-content.js │ │ │ └── custom-popover-content.js │ ├── config │ │ ├── deprecation-workflow.js │ │ ├── environment.js │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ ├── css │ │ ├── font-awesome.css │ │ └── jquery-ui.css │ │ ├── fonts │ │ └── sourceCodePro │ │ │ ├── SourceCodePro-Black.ttf │ │ │ ├── SourceCodePro-Black.woff │ │ │ ├── SourceCodePro-Bold.ttf │ │ │ ├── SourceCodePro-Bold.woff │ │ │ ├── SourceCodePro-ExtraLight.ttf │ │ │ ├── SourceCodePro-ExtraLight.woff │ │ │ ├── SourceCodePro-Light.ttf │ │ │ ├── SourceCodePro-Light.woff │ │ │ ├── SourceCodePro-Medium.ttf │ │ │ ├── SourceCodePro-Medium.woff │ │ │ ├── SourceCodePro-Regular.ttf │ │ │ ├── SourceCodePro-Regular.woff │ │ │ ├── SourceCodePro-Semibold.ttf │ │ │ └── SourceCodePro-Semibold.woff │ │ ├── img │ │ ├── invalid_color.svg │ │ ├── no_color.svg │ │ ├── select2.png │ │ └── spinner.gif │ │ ├── robots.txt │ │ └── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.svg │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 ├── helpers │ ├── assertions.js │ ├── color-picker.js │ ├── destroy-app.js │ ├── keyboard.js │ ├── module-for-acceptance.js │ ├── mouse.js │ ├── resolver.js │ ├── select.js │ └── start-app.js ├── index.html ├── integration │ ├── carousel-component-test.js │ ├── color-picker-test.js │ ├── components │ │ ├── render-popover-new-api-test.js │ │ ├── render-popover-old-api-test.js │ │ └── select-component-test.js │ ├── debounced-text-component-test.js │ ├── modal-component-test.js │ ├── multi-select-component-test.js │ ├── popover-link-component-test.js │ ├── select-component-render-test.js │ └── shared │ │ └── -popover-tests.js ├── test-helper.js └── unit │ ├── .gitkeep │ └── components │ └── select-component-test.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.hbs text eol=lf 2 | *.handlebars text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | gh_pages/ 3 | node_modules/ 4 | vendor/ 5 | .idea/ 6 | .DS_Store 7 | .stage 8 | *.swo 9 | *.swp 10 | 11 | # See http://help.github.com/ignore-files/ for more about ignoring files. 12 | 13 | # compiled output 14 | /dist 15 | /tmp 16 | 17 | # dependencies 18 | /node_modules 19 | /bower_components 20 | 21 | # misc 22 | /.sass-cache 23 | /connect.lock 24 | /coverage/* 25 | /libpeerconnection.log 26 | npm-debug.log 27 | testem.log 28 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise", 6 | "$", 7 | ], 8 | "browser": true, 9 | "boss": true, 10 | "curly": true, 11 | "debug": false, 12 | "devel": true, 13 | "eqeqeq": true, 14 | "evil": true, 15 | "forin": false, 16 | "immed": false, 17 | "laxbreak": false, 18 | "newcap": true, 19 | "noarg": true, 20 | "noempty": false, 21 | "nonew": false, 22 | "nomen": false, 23 | "onevar": false, 24 | "plusplus": false, 25 | "regexp": false, 26 | "undef": true, 27 | "sub": true, 28 | "strict": false, 29 | "white": false, 30 | "eqnull": true, 31 | "esnext": true, 32 | "unused": true 33 | } 34 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /bower_components 2 | /config/ember-try.js 3 | /dist 4 | /tests 5 | /tmp 6 | **/.gitkeep 7 | .bowerrc 8 | .editorconfig 9 | .ember-cli 10 | .gitignore 11 | .eslintrc.js 12 | .watchmanconfig 13 | .travis.yml 14 | bower.json 15 | ember-cli-build.js 16 | testem.js 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "10" 7 | 8 | sudo: true 9 | dist: trusty 10 | 11 | addons: 12 | chrome: stable 13 | 14 | cache: 15 | yarn: true 16 | 17 | env: 18 | global: 19 | # See https://git.io/vdao3 for details. 20 | - JOBS=1 21 | matrix: 22 | # we recommend new addons test the current and previous LTS 23 | # as well as latest stable release (bonus points to beta/canary) 24 | # - EMBER_TRY_SCENARIO=ember-2.0 25 | # - EMBER_TRY_SCENARIO=ember-lts-2.8 26 | # - EMBER_TRY_SCENARIO=ember-lts-2.12 27 | # - EMBER_TRY_SCENARIO=ember-release 28 | # - EMBER_TRY_SCENARIO=ember-beta 29 | # - EMBER_TRY_SCENARIO=ember-canary 30 | - EMBER_TRY_SCENARIO=ember-1.13 31 | - EMBER_TRY_SCENARIO=ember-default 32 | 33 | matrix: 34 | fast_finish: true 35 | 36 | before_install: 37 | - curl -o- -L https://yarnpkg.com/install.sh | bash 38 | - export PATH=$HOME/.yarn/bin:$PATH 39 | - yarn global add bower 40 | 41 | install: 42 | - yarn install --no-lockfile --non-interactive 43 | - bower install 44 | 45 | script: 46 | # Usually, it's ok to finish the test scenario without reverting 47 | # to the addon's original dependency state, skipping "cleanup". 48 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO --skip-cleanup 49 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2012 Addepar, Inc. All Rights Reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. "Addepar" or "Addepar, Inc." may not be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY ADDEPAR, INC. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember Widgets by Addepar [![Build Status](https://secure.travis-ci.org/Addepar/ember-widgets.svg?branch=master)](https://travis-ci.org/Addepar/ember-widgets) 2 | 3 | **THIS LIBRARY IS DEPRECATED** 4 | 5 | We will be releasing versions which may introduce breaking changes as we transition to a more modern 6 | set of Ember components. 7 | 8 | A collection of small widgets, easy to drop into place as Ember Components. 9 | 10 | ## Demo and Documentation 11 | https://opensource.addepar.com/ember-widgets/ 12 | 13 | ## Installation 14 | 15 | * `git clone` this repository 16 | * `npm install` 17 | * `bower install` 18 | 19 | ## Running 20 | 21 | * `ember server` 22 | * Visit your app at http://localhost:4200. 23 | 24 | ## Running Tests 25 | 26 | * `ember test` 27 | * `ember test --server` 28 | 29 | ## Building 30 | 31 | * `ember build` 32 | 33 | For more information on using ember-cli, visit [https://www.ember-cli.com/](https://www.ember-cli.com/). 34 | 35 | ## Publishing 36 | 37 | This library is not published to NPM. 38 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/addon/.gitkeep -------------------------------------------------------------------------------- /addon/components/carousel-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import CarouselIndicatorView from '../views/carousel-indicator'; 3 | 4 | // A lot of the javascript came from bootstrap.js 5 | // The bootstrap behavior was limited in how you could treat the slide list as a 6 | // circular ring. I.e., bootstrap would only allow you to return to first slide 7 | // by going forwards if you were in the last slide. We want the ability to seem 8 | // like the carousel is moving forwards even if we are on an earlier slide and 9 | // then want to go to the first slide. For example, imagine you are in the middle 10 | // of a wizard, you want to click finish, the results of that wizard bring up the 11 | // same wizard it should seem like it is the next step, not that you are going 12 | // backwards. 13 | 14 | export default Ember.Component.extend({ 15 | layoutName: 'carousel', 16 | classNames: ['carousel', 'slide'], 17 | classNameBindings: Ember.A(['sliding']), 18 | activeIndex: 0, 19 | collectionViewClass: Ember.CollectionView, 20 | carouselIndicatorClass: CarouselIndicatorView, 21 | $nextItem: null, 22 | didInsertElement: function() { 23 | this._super(); 24 | // suppose a content array is not specified in use case 1, we use jquery to 25 | // figure out how many carousel items are there. This allows us to generate 26 | // the correct number of carousel indicator 27 | if (!this.get('content')) { 28 | this.set('content', Ember.A(new Array(this.$('.item').length))); 29 | } 30 | }, 31 | willDestroyElement: function() { 32 | var ref; 33 | if ((ref = this.$nextItem) != null) { 34 | ref.off($.support.transition.end); 35 | } 36 | return this._super(); 37 | }, 38 | actions: { 39 | prev: function() { 40 | var activeIndex, contentLength, nextIndex; 41 | if (this.get('sliding')) { 42 | return; 43 | } 44 | activeIndex = this.get('activeIndex'); 45 | contentLength = this.get('content.length'); 46 | nextIndex = activeIndex - 1; 47 | nextIndex = nextIndex < 0 ? contentLength - 1 : nextIndex; 48 | return this.slide('prev', nextIndex); 49 | }, 50 | next: function() { 51 | var activeIndex, contentLength, nextIndex; 52 | if (this.get('sliding')) { 53 | return; 54 | } 55 | activeIndex = this.get('activeIndex'); 56 | contentLength = this.get('content.length'); 57 | nextIndex = activeIndex + 1; 58 | nextIndex = nextIndex >= contentLength ? 0 : nextIndex; 59 | return this.slide('next', nextIndex); 60 | } 61 | }, 62 | to: function(pos) { 63 | var direction; 64 | if (this.get('sliding')) { 65 | return; 66 | } 67 | if (!((0 <= pos && pos < this.get('content.length')))) { 68 | return; 69 | } 70 | direction = pos > this.get('activeIndex') ? 'next' : 'prev'; 71 | return this.slide(direction, pos); 72 | }, 73 | 74 | // TODO(Peter): Further emberized this by keeping the turth out of the DOM 75 | // We can use slide to transition to any slide with any animation direction. 76 | // E.g., by specifiying type = 'next' and next = first_slide_index, we can 77 | // transition to the first slide by moving to the right. 78 | 79 | // type: next | prev 80 | // next: is the index of the next slide 81 | slide: function(type, nextIndex) { 82 | var $active, direction, ref, 83 | _this = this; 84 | if (this.get('activeIndex') === nextIndex) { 85 | return; 86 | } 87 | direction = type === 'next' ? 'left' : 'right'; 88 | $active = $(this.$('.item').get(this.get('activeIndex'))); 89 | if ((ref = this.$nextItem) != null) { 90 | ref.off($.support.transition.end); 91 | } 92 | this.$nextItem = $(this.$('.item').get(nextIndex)); 93 | if (!window.EMBER_WIDGETS_DISABLE_ANIMATIONS) { 94 | this.set('sliding', true); 95 | this.$nextItem.addClass(type); 96 | // force reflow 97 | this.$nextItem[0].offsetWidth; // jshint ignore:line 98 | $active.addClass(direction); 99 | this.$nextItem.addClass(direction); 100 | } 101 | // Bootstrap has this method for listening on end of transition 102 | return this._onTransitionEnd(this.$nextItem, () => { 103 | this.$nextItem.off($.support.transition.end); 104 | // This code is async and ember-testing requires us to wrap any code with 105 | // asynchronous side-effects in an Ember.run 106 | Ember.run(() => { 107 | this.set('activeIndex', nextIndex); 108 | this.$nextItem.removeClass([type, direction].join(' ')).addClass('active'); 109 | $active.removeClass(['active', direction].join(' ')); 110 | this.set('sliding', false); 111 | this.send('transitionEnded'); 112 | 113 | Ember.run.schedule('afterRender', () => { 114 | // Have any vertical-collection based select 115 | // refresh its contents now that the list is visible 116 | window.dispatchEvent(new Event('resize')); 117 | }); 118 | }); 119 | this.$nextItem = null; 120 | }); 121 | }, 122 | _onTransitionEnd: function($el, callback) { 123 | if (window.EMBER_WIDGETS_DISABLE_ANIMATIONS) { 124 | return callback(); 125 | } else { 126 | return $el.one($.support.transition.end, callback); 127 | } 128 | } 129 | }); 130 | -------------------------------------------------------------------------------- /addon/components/color-picker-cell.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import StyleBindingsMixin from '../mixins/style-bindings'; 3 | import ColorPickerMixin from '../mixins/color-picker'; 4 | 5 | /** 6 | * ColorPickerCell 7 | * 8 | * This is an internal-use view to control the logic of a color cell in 9 | * the color picker. 10 | * @class 11 | * @augments {Ember.View, Ember.Widgets.StyleBindingMixin, 12 | * Ember.Widgets.ColorPickerMixin} 13 | */ 14 | 15 | export default Ember.Component.extend(StyleBindingsMixin, ColorPickerMixin, { 16 | layoutName: 'color-picker-cell', 17 | classNames: ['pull-left', 'color-picker-cell'], 18 | classNameBindings: 'isActive:active:inactive', 19 | styleBindings: 'color:background-color', 20 | /** 21 | * The color name of the cell, e.g. 'yellow' 22 | * @see Ember.Widgets.ColorPickerMixin 23 | * @type { string } 24 | */ 25 | 26 | color: null, 27 | /** 28 | * Determines whether the state of the cell is active if the picker selected 29 | * color matches this cell's color 30 | * @type {Boolean} 31 | */ 32 | 33 | isActive: Ember.computed(function() { 34 | return this.get('selectedColor') === this.get('color'); 35 | }).property('selectedColor', 'color'), 36 | click: function() { 37 | return this.sendAction('setColor', this.get('color')); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /addon/components/color-picker-dropdown.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import BodyEventListenerMixin from '../mixins/body-event-listener'; 3 | import ColorPickerMixin from '../mixins/color-picker'; 4 | 5 | /** 6 | * ColorPickerDropdownComponent 7 | * 8 | * This is an internal-use component to control the logic of the dropdown of 9 | * the color picker. It contains the color palette and the custom color input 10 | * @class 11 | * @augments {Ember.Component, Ember.Widgets.BodyEventListener, 12 | * Ember.Widgets.ColorPickerMixin} 13 | */ 14 | 15 | var __indexOf = [].indexOf || function(item) { 16 | for (var i = 0, l = this.length; i < l; i++) { 17 | if (i in this && this[i] === item) { 18 | return i; 19 | } 20 | } 21 | 22 | return -1; 23 | }; 24 | 25 | export default Ember.Component.extend(BodyEventListenerMixin, ColorPickerMixin, { 26 | layoutName: 'color-picker-dropdown', 27 | dropdownClass: null, 28 | selectedColor: '', 29 | customColor: '', 30 | /** 31 | * The color palette preset. It is passed in from the ColorPickerComponent. 32 | * @type {array} 33 | */ 34 | 35 | colorRows: Ember.computed(function() { 36 | return Ember.A(); 37 | }), 38 | setCustomColorObserver: Ember.on('init', Ember.observer('selectedColor', 'colorRows', function() { 39 | var selectedColor; 40 | selectedColor = this.get('selectedColor'); 41 | selectedColor = this.colorToHex(selectedColor); 42 | if (this.get('colorRows').find(function(row) { 43 | return __indexOf.call(row.invoke('toLowerCase'), selectedColor) >= 0; 44 | })) { 45 | this.set('customColor', ''); 46 | } 47 | this.set('customColor', selectedColor); 48 | })), 49 | /** 50 | * This is the formatted string of the input color, for which a hashtag "#" 51 | * is automatically added if it is not present. 52 | * @type {string} 53 | */ 54 | 55 | formattedCustomColor: Ember.computed(function() { 56 | var customColor; 57 | customColor = this.get('customColor').trim(); 58 | if (customColor.charAt(0) !== '#') { 59 | customColor = '#' + customColor; 60 | } 61 | return customColor; 62 | }).property('customColor'), 63 | isCustomColorValid: Ember.computed(function() { 64 | return /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(this.get('formattedCustomColor')); 65 | }).property('formattedCustomColor'), 66 | customColorCSS: Ember.computed(function() { 67 | return Ember.String.htmlSafe("background-color: " + (this.get('formattedCustomColor'))); 68 | }).property('formattedCustomColor'), 69 | userDidSelect: function(selection) { 70 | return this.sendAction('userSelected', selection); 71 | }, 72 | /** 73 | * Handle the body click event, i.e. click outside of the component. Here, 74 | * an action is sent up to inform the color picker component to close the 75 | * dropdown. 76 | * @override 77 | * @function 78 | */ 79 | 80 | bodyClick: function() { 81 | return this.sendAction('hideDropdown'); 82 | }, 83 | actions: { 84 | setColor: function(color) { 85 | this.set('customColor', ''); 86 | this.set('selectedColor', color); 87 | this.sendAction('setSelectedColor', color, false); 88 | return this.userDidSelect(color); 89 | }, 90 | setCustomColor: function() { 91 | var color; 92 | if (this.get('isCustomColorValid')) { 93 | color = this.get('formattedCustomColor'); 94 | this.sendAction('setSelectedColor', color, true); 95 | return this.userDidSelect(color); 96 | } 97 | } 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /addon/components/color-picker.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import ColorPickerMixin from '../mixins/color-picker'; 3 | 4 | export default Ember.Component.extend(ColorPickerMixin, { 5 | layoutName: 'color-picker', 6 | classNames: ['color-picker-button'], 7 | colorPickerPlacement: 'right', 8 | dropdownClass: null, 9 | /** 10 | * The color palette preset. It is passed in from the ColorPickerComponent. 11 | * @type {array} an array of two arrays of hex color strings. The two arrays 12 | * corresponding to the two color palettes displayed in the dropdown. 13 | */ 14 | 15 | colorRows: Ember.computed(function() { 16 | return Ember.A([Ember.A(['#000000', '#111111', '#434343', '#666666', '#999999', '#AAAAAA', '#B7B7B7', '#CCCCCC', '#D9D9D9', '#EFEFEF', '#F3F3F3', '#FFFFFF']), Ember.A(['#001F3F', '#0074D9', '#7FDBFF', '#39CCCC', '#2ECC40', '#01FF70', '#FFDC00', '#FF851B', '#FF4136', '#85144B', '#B10DC9', 'transparent'])]); 17 | }), 18 | /** 19 | * This is the partial template for the colorPicker button. 20 | * It allows developers to override/style this component differently 21 | * @type {string} 22 | */ 23 | 24 | colorPickerButtonPartial: 'color-picker-button-partial', 25 | /** 26 | * This is a boolean to control if we should render the colorPicker dropdown 27 | * or not. Instead of hiding it using CSS, we use this flag to control the 28 | * rendering. 29 | * @type {boolean} 30 | */ 31 | 32 | isDropdownOpen: false, 33 | selectedColor: '#0074D9', 34 | customColor: '', 35 | /** 36 | * The property indicates that we have a custom color selected or a color 37 | * from color palette selected. 38 | * @type {boolean} 39 | */ 40 | 41 | isCustomColor: Ember.computed.notEmpty('customColor'), 42 | /** 43 | * Determines whether the color is transparent so the cell renders the 44 | * transparent style properly 45 | * @type {Boolean} 46 | */ 47 | 48 | isColorTransparent: Ember.computed.equal('selectedColorRGB', 'transparent'), 49 | selectedColorRGB: Ember.computed(function() { 50 | var selectedColor; 51 | selectedColor = this.get('selectedColor'); 52 | return this.colorToHex(selectedColor); 53 | }).property('selectedColor'), 54 | actions: { 55 | /** 56 | * This action is bound to the colorPicker button to hide/show the dropdown 57 | * when users click on it. 58 | */ 59 | 60 | toggleDropdown: function() { 61 | return this.toggleProperty('isDropdownOpen'); 62 | }, 63 | /** 64 | * Send an action outside of the component to inform that a new 65 | * color is select and also to hide the dropdown. 66 | * @param {String} selection the selected color hex string 67 | */ 68 | 69 | userSelected: function(selection) { 70 | this.sendAction('userSelected', selection); 71 | this.set('isDropdownOpen', false); 72 | }, 73 | /** 74 | * Hide the color picker dropdown 75 | */ 76 | 77 | hideDropdown: function() { 78 | this.set('isDropdownOpen', false); 79 | }, 80 | /** 81 | * Set the selected color and update the custom color accordingly 82 | * @param {string} color the selected color to be updated 83 | * @param {boolean} isCustomColor the flag to indicate if it is a custom 84 | * color 85 | */ 86 | 87 | setSelectedColor: function(color, isCustomColor) { 88 | this.set('selectedColor', color); 89 | if (isCustomColor) { 90 | this.set('customColor', color); 91 | } else { 92 | this.set('customColor', ''); 93 | } 94 | } 95 | } 96 | }); 97 | -------------------------------------------------------------------------------- /addon/components/debounced-text-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | /** 4 | A custom component that allows delay sending text to parent component by some amount of time. 5 | This is useful in case there is some request sent to server when value in this component changes. 6 | As we dont want to send too many request to server, we should only send request after user stops 7 | typing. 8 | 9 | @class 10 | @augments {Ember.TextField} 11 | */ 12 | export default Ember.TextField.extend({ 13 | /** 14 | Ember debounced object that delays execution of the inner function. 15 | @type {Object} 16 | */ 17 | textDebounce: null, 18 | 19 | /** 20 | Delay period between when an input change is triggered and when the change propagated to parent 21 | component. Parent controller can override this value. 22 | @type {number} 23 | */ 24 | delayTime: 250, 25 | 26 | /** 27 | @override 28 | */ 29 | didInsertElement: function() { 30 | this._super.apply(this, arguments); 31 | 32 | this.$().focus(); 33 | }, 34 | 35 | /** 36 | @override 37 | */ 38 | willDestroy: function() { 39 | if (!Ember.isNone(this.textDebounce)) { 40 | Ember.run.cancel(this.textDebounce); 41 | } 42 | 43 | this._super.apply(this, arguments); 44 | }, 45 | 46 | /** 47 | Propagates new text to parent component. 48 | */ 49 | propagateNewText: function(newText) { 50 | this.sendAction('valueChanged', newText); 51 | }, 52 | 53 | onValueChanged: function(newText) { 54 | if (!Ember.isNone(this.textDebounce)) { 55 | Ember.run.cancel(this.textDebounce); 56 | } 57 | 58 | this.textDebounce = Ember.run.debounce(this, this.propagateNewText, newText, 59 | this.get('delayTime')); 60 | }, 61 | 62 | /** 63 | This is called when a browser event which changes the element's value, such as keyUp or paste, 64 | is triggered. It sets the component's value to the underlying input element's value. Override 65 | the parent class behavior to send the valueChanged action if the value has changed. 66 | 67 | @override 68 | */ 69 | _elementValueDidChange: function() { 70 | var previousValue = this.get('value'); 71 | this._super.apply(this, arguments); 72 | var newValue = this.get('value'); 73 | if (previousValue !== newValue) { 74 | this.onValueChanged(newValue); 75 | } 76 | } 77 | }); 78 | -------------------------------------------------------------------------------- /addon/components/editable-label-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Component.extend({ 4 | layoutName: 'editable-label', 5 | classNames: ['editable-label'], 6 | placeholder: '', 7 | isEditing: false, 8 | value: null, 9 | displayName: Ember.computed(function() { 10 | if (Ember.isNone(this.get('value')) || this.get('value') === '') { 11 | return this.get('placeholder'); 12 | } else { 13 | return this.get('value'); 14 | } 15 | }).property('value', 'placeholder'), 16 | innerTextField: Ember.TextField.extend({ 17 | valueBinding: Ember.Binding.oneWay('parentView.value'), 18 | didInsertElement: function() { 19 | return this.$().focus(); 20 | }, 21 | blur: function() { 22 | this.set('parentView.isEditing', false); 23 | this.set('parentView.value', this.get('value')); 24 | } 25 | }), 26 | editLabel: function() { 27 | this.set('isEditing', true); 28 | } 29 | }); 30 | -------------------------------------------------------------------------------- /addon/components/multi-select-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import SelectComponent from './select-component'; 3 | import MultiSelectOptionView from '../views/multi-select-option'; 4 | import MultiSelectItemView from '../views/multi-select-item'; 5 | import MultiSelectTooltipItemView from '../views/multi-select-tooltip-item'; 6 | 7 | /** 8 | * @augments SelectGroup 9 | * @augments Ember.Component 10 | */ 11 | export default SelectComponent.extend({ 12 | layoutName: 'multi-select', 13 | selections: void 0, 14 | choicesFieldClass: '', 15 | placeholder: void 0, 16 | persistentPlaceholder: '', 17 | resetQueryOnSelect: true, 18 | showTooltip: true, 19 | tooltipItemViewClass: MultiSelectTooltipItemView, 20 | originalItemViewClass: MultiSelectItemView, 21 | // disable tabindex of the component container to set focus directly to 22 | // the input field, which is always visible. This helps reducing one tab 23 | // step to navigate back to the previous component 24 | tabindex: -1, 25 | values: Ember.computed('selections.[]', { 26 | set(key, value) { 27 | var selections, valuePath; 28 | if (!value) { 29 | return; 30 | } 31 | valuePath = this.get('optionValuePath'); 32 | this.set('selections', Ember.A(this.get('content').filter(function(item) { 33 | return value.contains(Ember.get(item, valuePath)); 34 | }))); 35 | return value; 36 | }, 37 | get() { 38 | var selections, valuePath; 39 | valuePath = this.get('optionValuePath'); 40 | selections = this.get('selections'); 41 | if (valuePath) { 42 | return selections.getEach(valuePath); 43 | } else { 44 | return selections; 45 | } 46 | } 47 | }), 48 | selectionItemView: MultiSelectOptionView, 49 | 50 | // Invisible span used to make sure there is a good amount of room for either 51 | // the placeholder values, or for the query the user has entered. 52 | invisiblePlaceholderText: Ember.computed(function() { 53 | if (this.get('query')) { 54 | return this.get('query'); 55 | } 56 | if (this.get('selections.length')) { 57 | return this.get('persistentPlaceholder'); 58 | } 59 | return this.get('placeholder') || this.get('persistentPlaceholder'); 60 | }).property('query', 'placeholder', 'persistentPlaceholder', 'selections.length'), 61 | 62 | searchView: Ember.TextField.extend({ 63 | "class": 'ember-select-input', 64 | valueBinding: 'selectComponent.query', 65 | placeholder: Ember.computed(function() { 66 | if (this.get('selectComponent.selections.length')) { 67 | return this.get('selectComponent.persistentPlaceholder'); 68 | } 69 | return this.get('selectComponent.placeholder') || this.get('selectComponent.persistentPlaceholder'); 70 | }).property('selectComponent.placeholder', 'selectComponent.persistentPlaceholder', 'selectComponent.selections.length'), 71 | click: function() { 72 | this.set('selectComponent.showDropdown', true); 73 | } 74 | }), 75 | 76 | // the list of content that is filtered down based on the query entered 77 | // in the textbox 78 | // Other than observing the changes on each elements, we need to observe the 79 | // `filteredContent`, and `sortedFilteredContent` because when the `content` 80 | // is overridden by a DS.PromiseArray, somehow it never triggers this function 81 | preparedContent: Ember.computed(function() { 82 | var content, selections, basedArray; 83 | content = this.get('content'); 84 | selections = this.get('selections'); 85 | if (!(content && selections)) { 86 | return Ember.A([]); 87 | } 88 | // excludes items that are already selected 89 | var emberArray = Ember.A(); 90 | var sortFn = function(item) { 91 | return !emberArray.contains.call(selections, item); 92 | }; 93 | if (this.get('sortLabels')) { 94 | basedArray = this.get('sortedFilteredContent'); 95 | } else { 96 | basedArray = this.get('filteredContent'); 97 | } 98 | return emberArray.filter.call(basedArray, sortFn); 99 | }).property('content.[]', 'filteredContent.[]', 'sortedFilteredContent.[]', 'selections.[]', 'sortLabels', 'filteredContent', 'sortedFilteredContent'), 100 | 101 | // uses single select's "selection" value - adds it to selections and 102 | // then clears the selection value so that it can be re-selected 103 | selectionDidChange: Ember.observer('selection', 'selections.[]', function() { 104 | var selection, selections; 105 | selections = this.get('selections'); 106 | selection = this.get('selection'); 107 | if (this.get('resetQueryOnSelect')) { 108 | this.set('query', ''); 109 | } 110 | this.set('selection', null); 111 | if (!Ember.isEmpty(selection) && !selections.contains(selection)) { 112 | return selections.pushObject(selection); 113 | } 114 | }), 115 | focusTextField: function() { 116 | var ref; 117 | return (ref = this.$('.ember-text-field')) != null ? ref.focus() : void 0; 118 | }, 119 | didInsertElement: function() { 120 | // We want to initialize selections to []. This SHOULD NOT be done through 121 | // computed properties, because we would run into the following situation. 122 | // If the user do selectionsBinding and whatever we are binded to is 123 | // undefined then, selections is initialized as undefined. We could change 124 | // the value to [] if its value is undefined but the bindings would not have 125 | // realized a change and fail to fire. 126 | this._super(); 127 | if (!this.get('selections')) { 128 | this.set('selections', Ember.A([])); 129 | } 130 | if (!this.get('values')) { 131 | this.set('values', Ember.A([])); 132 | } 133 | }, 134 | deletePressed: function(event) { 135 | if (event.target.selectionStart === 0 && event.target.selectionEnd === 0) { 136 | this.removeSelectItem(this.get('selections.lastObject')); 137 | return event.preventDefault(); 138 | } 139 | }, 140 | removeSelectItem: function(item) { 141 | // set the focus back to the searchView because this item will be removed 142 | var dropdownIsShowing; 143 | dropdownIsShowing = this.get('showDropdown'); 144 | this.focusTextField(); 145 | if (!dropdownIsShowing) { 146 | this.send('hideDropdown'); 147 | } 148 | return this.get('selections').removeObject(item); 149 | }, 150 | escapePressed: function(event) { 151 | if (this.get('showDropdown')) { 152 | this.focusTextField(); 153 | this.send('hideDropdown'); 154 | return event.preventDefault(); 155 | } 156 | }, 157 | enterPressed: function(event) { 158 | this._super(event); 159 | return this.focusTextField(); 160 | }, 161 | actions: { 162 | removeSelectItem: function(item) { 163 | return this.removeSelectItem(item); 164 | } 165 | } 166 | }); 167 | -------------------------------------------------------------------------------- /addon/components/popover-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | import PopoverMixin from '../mixins/popover'; 4 | import NewPopoverModalAPIMixin from '../mixins/new-popover-modal-api'; 5 | 6 | var PopoverComponent = Ember.Component.extend(PopoverMixin, NewPopoverModalAPIMixin); 7 | 8 | PopoverComponent.reopenClass({ 9 | rootElement: '.ember-application', 10 | hideAll() { 11 | return $(document).trigger('popover:hide'); 12 | }, 13 | /** 14 | * Shows a popup with options used for PopoverComponent. 15 | * @param {Object} options All options used for PopoverComponent. 16 | * @param {boolean} [hideOthers=true] Indicates if other popover should be hidden when a new one 17 | * is shown. By default, it's set to true. 18 | */ 19 | 20 | popup(options, hideOthers) { 21 | if (hideOthers == null) { 22 | hideOthers = true; 23 | } 24 | if (hideOthers) { 25 | this.hideAll(); 26 | } 27 | 28 | let rootElement = options.rootElement || this.rootElement; 29 | let { container } = options; 30 | 31 | if (!container) { 32 | throw new Error(`.popup() expects an option of {container}`); 33 | } 34 | 35 | let destinationElement = document.querySelector(rootElement); 36 | 37 | if (!destinationElement) { 38 | throw new Error( 39 | '.popup() expected the selector provided as {rootElement} to return a node currently on the page' 40 | ); 41 | } 42 | 43 | let popoverService = container.lookup('service:popover'); 44 | 45 | return popoverService.open(destinationElement, this, options); 46 | } 47 | }); 48 | 49 | export default PopoverComponent; 50 | -------------------------------------------------------------------------------- /addon/components/popover-link-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { getOwner, setOwner } from '@ember/application'; 3 | import PopoverMixin from '../mixins/popover'; 4 | import PopoverComponent from './popover-component'; 5 | 6 | export default Ember.Component.extend({ 7 | classNames: ['popover-link'], 8 | classNameBindings: ['disabled'], 9 | placement: 'top', 10 | content: null, 11 | title: null, 12 | contentViewClass: null, 13 | disabled: false, 14 | popoverClassNames: [], 15 | rootElement: '.ember-application', 16 | fade: true, 17 | openOnLeftClick: true, 18 | openOnRightClick: false, 19 | hideOthers: false, 20 | _popover: null, 21 | willDestroyElement: function() { 22 | var _ref; 23 | if ((_ref = this.get('_popover')) != null) { 24 | _ref.destroy(); 25 | } 26 | return this._super(); 27 | }, 28 | _contentViewClass: Ember.computed(function() { 29 | var contentViewClass; 30 | contentViewClass = this.get('contentViewClass'); 31 | if (typeof contentViewClass === 'string') { 32 | return Ember.get(contentViewClass); 33 | } 34 | return contentViewClass; 35 | }).property('contentViewClass'), 36 | _openPopover: function() { 37 | var popover, popoverView; 38 | if (this.get('disabled')) { 39 | return; 40 | } 41 | popover = this.get('_popover'); 42 | if (((popover != null ? popover.get('_state') : void 0) || (popover != null ? popover.get('state') : void 0)) === 'inDOM') { 43 | return popover.hide(); 44 | } else { 45 | if (this.get('hideOthers')) { 46 | PopoverComponent.hideAll(); 47 | } 48 | popoverView = Ember.View.extend(PopoverMixin, { 49 | layoutName: 'popover-link-popover', 50 | classNames: this.get('popoverClassNames'), 51 | controller: this, 52 | targetElement: this.get('element'), 53 | placement: Ember.computed.alias('controller.placement'), 54 | title: Ember.computed.alias('controller.title'), 55 | contentViewClass: this.get('_contentViewClass'), 56 | fade: this.get('fade') 57 | }); 58 | popover = popoverView.create(); 59 | setOwner(popover, getOwner(this)); 60 | this.set('_popover', popover); 61 | return popover.appendTo(this.get('rootElement')); 62 | } 63 | }, 64 | click: function() { 65 | if (!this.get('openOnLeftClick')) { 66 | return true; 67 | } 68 | this._openPopover(); 69 | return false; 70 | }, 71 | contextMenu: function() { 72 | if (!this.get('openOnRightClick')) { 73 | return true; 74 | } 75 | this._openPopover(); 76 | return false; 77 | } 78 | }); 79 | -------------------------------------------------------------------------------- /addon/components/popover-modal-base.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import NewPopoverModalAPIMixin from '../mixins/new-popover-modal-api'; 3 | 4 | /* 5 | * This base class should be used for components using `buildPopoverSpec()` 6 | * and `openPopoverSpec()` on the popover service. It is useful for any 7 | * kind of popup/alert/modal which does not share the behaviors of the 8 | * popover and modal classes, but which still benefits from the popover 9 | * infrastructure and rendering system. 10 | */ 11 | export default Ember.Component.extend(NewPopoverModalAPIMixin); 12 | -------------------------------------------------------------------------------- /addon/components/radio-button-group-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Component.extend({ 4 | classNames: ['ember-radio-button-group'], 5 | // Bound to the value of the selected radio button in this group 6 | selectedValue: null, 7 | disabled: false, 8 | selectedValueChanged: Ember.on('init', Ember.observer(function() { 9 | return this.sendAction('action', this.get('selectedValue')); 10 | }, 'selectedValue')) 11 | }); 12 | -------------------------------------------------------------------------------- /addon/components/radio-button.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | // Formerly RadioButtonWrapper component, was renamed so that it matched its hbs 4 | // helper, {{radio-button}} 5 | 6 | // Clickable wrapper around the actual radio button which allows the text near the 7 | // button to be clickable too. 8 | export default Ember.Component.extend({ 9 | layoutName: 'radio-button-layout', 10 | value: null, 11 | disabled: false, 12 | selectedValue: Ember.computed.alias('parentView.selectedValue'), 13 | classNames: ['radio-button'], 14 | 15 | // Sets the checked property on the element. 16 | checked: false, 17 | 18 | // Sets the disabled property on the element. 19 | _disabled: Ember.computed.or('parentView.disabled', 'disabled'), 20 | selectedValueChanged: Ember.on('init', Ember.observer(function() { 21 | var selectedValue; 22 | selectedValue = this.get('selectedValue'); 23 | if (!Ember.isEmpty(selectedValue) && this.get('value') === selectedValue) { 24 | this.set('checked', true); 25 | } else { 26 | this.set('checked', false); 27 | } 28 | }, 'selectedValue')), 29 | click: function() { 30 | if (this.get('_disabled')) { 31 | return; 32 | } 33 | const value = this.get('value'); 34 | const selectedValue = this.get('selectedValue'); 35 | 36 | if (selectedValue !== value) { 37 | Ember.run(() => { 38 | this.set('checked', true); 39 | this.set('selectedValue', value); 40 | }); 41 | this.sendAction('onchange', value); 42 | } 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /addon/components/radio-input.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Component.extend({ 4 | attributeBindings: ['type', 'checked', 'disabled'], 5 | classNames: ['radio-input'], 6 | tagName: 'input', 7 | type: 'radio', 8 | checked: Ember.computed.alias('parentView.checked'), 9 | disabled: Ember.computed.alias('parentView._disabled') 10 | }); 11 | -------------------------------------------------------------------------------- /addon/components/render-popover.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Component.extend({ 4 | popover: Ember.inject.service(), 5 | 6 | init() { 7 | this._super(...arguments); 8 | 9 | this.get('popover').registerPopoverRendered(); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /addon/components/typeahead-component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import SelectComponent from './select-component'; 3 | 4 | export default SelectComponent.extend({ 5 | layoutName: 'typeahead', 6 | searchFieldClass: 'form-control', 7 | disabled: false, 8 | listViewPartial: 'typeahead-list-view-partial', 9 | 10 | searchView: Ember.TextField.extend({ 11 | class: 'ember-select-input', 12 | placeholderBinding: 'selectComponent.placeholder', 13 | valueBinding: 'selectComponent.query', 14 | focusIn() { 15 | this.set('selectComponent.showDropdown', true); 16 | } 17 | }), 18 | 19 | /** 20 | * An optional view to be displayed in the typeahead dropdown below the list 21 | * of options 22 | * @type {String} 23 | */ 24 | footerView: null, 25 | 26 | userDidSelect(selection) { 27 | this._super(selection); 28 | this.set('query', selection); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /addon/mixins/body-event-listener.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Mixin.create({ 4 | bodyElementSelector: 'html', 5 | bodyClick: Ember.K, 6 | didInsertElement: function() { 7 | this._super(); 8 | 9 | // It is important to setup document handlers in the next run loop. 10 | // Otherwise we run in to situation whenre the click that causes a popover 11 | // to appears will be handled right away when we attach a click handler. 12 | // This very same click will trigger the bodyClick to fire and thus 13 | // causing us to hide the popover right away 14 | return Ember.run.next(this, this._setupDocumentHandlers); 15 | }, 16 | willDestroyElement: function() { 17 | this._removeDocumentHandlers(); 18 | this._super(); 19 | }, 20 | _setupDocumentHandlers: function() { 21 | var _this = this; 22 | if (this._clickHandler || this.isDestroying) { 23 | return; 24 | } 25 | 26 | this._clickHandler = function(event) { 27 | return Ember.run(function() { 28 | if ((_this.get('_state') || _this.get('state')) === 'inDOM' && Ember.isEmpty(_this.$().has($(event.target)))) { 29 | // check if event.target still exists in DOM 30 | var checkContain = $.contains(document.body, event.target); 31 | var isBodyElement = event.target === document.body; 32 | if (checkContain || isBodyElement) { 33 | // bodyClick starts taking parameter "event" to make room to control 34 | // some special cases where there is a component added to the body 35 | // instead of the app (such as bootstrap date-picker). 36 | // If it is the case, we can check for the event target to prevent 37 | // the popover from being closed. 38 | return _this.bodyClick(event); 39 | } 40 | } 41 | }); 42 | }; 43 | 44 | return $(this.get('bodyElementSelector')).on("click", this._clickHandler); 45 | }, 46 | _removeDocumentHandlers: function() { 47 | if (this._clickHandler) { 48 | $(this.get('bodyElementSelector')).off("click", this._clickHandler); 49 | } 50 | return this._clickHandler = null; 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /addon/mixins/color-picker.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Mixin.create({ 4 | colorNameToHexMap: { 5 | aliceblue: "#f0f8ff", 6 | antiquewhite: "#faebd7", 7 | aqua: "#00ffff", 8 | aquamarine: "#7fffd4", 9 | azure: "#f0ffff", 10 | beige: "#f5f5dc", 11 | bisque: "#ffe4c4", 12 | black: "#000000", 13 | blanchedalmond: "#ffebcd", 14 | blue: "#0000ff", 15 | blueviolet: "#8a2be2", 16 | brown: "#a52a2a", 17 | burlywood: "#deb887", 18 | cadetblue: "#5f9ea0", 19 | chartreuse: "#7fff00", 20 | chocolate: "#d2691e", 21 | coral: "#ff7f50", 22 | cornflowerblue: "#6495ed", 23 | cornsilk: "#fff8dc", 24 | crimson: "#dc143c", 25 | cyan: "#00ffff", 26 | darkblue: "#00008b", 27 | darkcyan: "#008b8b", 28 | darkgoldenrod: "#b8860b", 29 | darkgray: "#a9a9a9", 30 | darkgreen: "#006400", 31 | darkkhaki: "#bdb76b", 32 | darkmagenta: "#8b008b", 33 | darkolivegreen: "#556b2f", 34 | darkorange: "#ff8c00", 35 | darkorchid: "#9932cc", 36 | darkred: "#8b0000", 37 | darksalmon: "#e9967a", 38 | darkseagreen: "#8fbc8f", 39 | darkslateblue: "#483d8b", 40 | darkslategray: "#2f4f4f", 41 | darkturquoise: "#00ced1", 42 | darkviolet: "#9400d3", 43 | deeppink: "#ff1493", 44 | deepskyblue: "#00bfff", 45 | dimgray: "#696969", 46 | dodgerblue: "#1e90ff", 47 | firebrick: "#b22222", 48 | floralwhite: "#fffaf0", 49 | forestgreen: "#228b22", 50 | fuchsia: "#ff00ff", 51 | gainsboro: "#dcdcdc", 52 | ghostwhite: "#f8f8ff", 53 | gold: "#ffd700", 54 | goldenrod: "#daa520", 55 | gray: "#808080", 56 | green: "#008000", 57 | greenyellow: "#adff2f", 58 | honeydew: "#f0fff0", 59 | hotpink: "#ff69b4", 60 | "indianred ": "#cd5c5c", 61 | indigo: "#4b0082", 62 | ivory: "#fffff0", 63 | khaki: "#f0e68c", 64 | lavender: "#e6e6fa", 65 | lavenderblush: "#fff0f5", 66 | lawngreen: "#7cfc00", 67 | lemonchiffon: "#fffacd", 68 | lightblue: "#add8e6", 69 | lightcoral: "#f08080", 70 | lightcyan: "#e0ffff", 71 | lightgoldenrodyellow: "#fafad2", 72 | lightgrey: "#d3d3d3", 73 | lightgreen: "#90ee90", 74 | lightpink: "#ffb6c1", 75 | lightsalmon: "#ffa07a", 76 | lightseagreen: "#20b2aa", 77 | lightskyblue: "#87cefa", 78 | lightslategray: "#778899", 79 | lightsteelblue: "#b0c4de", 80 | lightyellow: "#ffffe0", 81 | lime: "#00ff00", 82 | limegreen: "#32cd32", 83 | linen: "#faf0e6", 84 | magenta: "#ff00ff", 85 | maroon: "#800000", 86 | mediumaquamarine: "#66cdaa", 87 | mediumblue: "#0000cd", 88 | mediumorchid: "#ba55d3", 89 | mediumpurple: "#9370d8", 90 | mediumseagreen: "#3cb371", 91 | mediumslateblue: "#7b68ee", 92 | mediumspringgreen: "#00fa9a", 93 | mediumturquoise: "#48d1cc", 94 | mediumvioletred: "#c71585", 95 | midnightblue: "#191970", 96 | mintcream: "#f5fffa", 97 | mistyrose: "#ffe4e1", 98 | moccasin: "#ffe4b5", 99 | navajowhite: "#ffdead", 100 | navy: "#000080", 101 | oldlace: "#fdf5e6", 102 | olive: "#808000", 103 | olivedrab: "#6b8e23", 104 | orange: "#ffa500", 105 | orangered: "#ff4500", 106 | orchid: "#da70d6", 107 | palegoldenrod: "#eee8aa", 108 | palegreen: "#98fb98", 109 | paleturquoise: "#afeeee", 110 | palevioletred: "#d87093", 111 | papayawhip: "#ffefd5", 112 | peachpuff: "#ffdab9", 113 | peru: "#cd853f", 114 | pink: "#ffc0cb", 115 | plum: "#dda0dd", 116 | powderblue: "#b0e0e6", 117 | purple: "#800080", 118 | red: "#ff0000", 119 | rosybrown: "#bc8f8f", 120 | royalblue: "#4169e1", 121 | saddlebrown: "#8b4513", 122 | salmon: "#fa8072", 123 | sandybrown: "#f4a460", 124 | seagreen: "#2e8b57", 125 | seashell: "#fff5ee", 126 | sienna: "#a0522d", 127 | silver: "#c0c0c0", 128 | skyblue: "#87ceeb", 129 | slateblue: "#6a5acd", 130 | slategray: "#708090", 131 | snow: "#fffafa", 132 | springgreen: "#00ff7f", 133 | steelblue: "#4682b4", 134 | tan: "#d2b48c", 135 | teal: "#008080", 136 | thistle: "#d8bfd8", 137 | tomato: "#ff6347", 138 | turquoise: "#40e0d0", 139 | violet: "#ee82ee", 140 | wheat: "#f5deb3", 141 | white: "#ffffff", 142 | whitesmoke: "#f5f5f5", 143 | yellow: "#ffff00", 144 | yellowgreen: "#9acd32" 145 | }, 146 | /** 147 | * Convert RGB color to Hex string 148 | * @param {number} r Red channel, integer value range [0..255] 149 | * @param {number} g Green channel, integer value range [0..255] 150 | * @param {number} b Blue channel, integer value range [0..255] 151 | * @return {string} 152 | */ 153 | 154 | rgbToHex: function(r, g, b) { 155 | return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); 156 | }, 157 | /** 158 | * Function to convert from color name to hex string value 159 | * @param {string} color the name of a color 160 | * @return {string} the hex value of the input color name 161 | */ 162 | 163 | colorToHex: function(color) { 164 | var blue, colorNameToHexMap, digits, green, opacity, red; 165 | if (!color) { 166 | return color; 167 | } 168 | if (color.substr(0, 1) === "#" || color === "transparent") { 169 | return color.toLowerCase(); 170 | } 171 | colorNameToHexMap = this.get('colorNameToHexMap'); 172 | if (color in colorNameToHexMap) { 173 | return colorNameToHexMap[color.toLowerCase()]; 174 | } 175 | digits = /(.*?)rgb(a)?\((\d+), (\d+), (\d+)(, (\d+))?\)/.exec(color); 176 | if ((digits != null ? digits.length : void 0) === 8) { 177 | red = parseInt(digits[3]); 178 | green = parseInt(digits[4]); 179 | blue = parseInt(digits[5]); 180 | opacity = parseInt(digits[7]); 181 | if (opacity === 0) { 182 | return "transparent"; 183 | } 184 | return this.rgbToHex(red, green, blue); 185 | } 186 | return null; 187 | } 188 | }); 189 | -------------------------------------------------------------------------------- /addon/mixins/keyboard-helper.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Mixin.create({ 4 | KEY_CODES: { 5 | BACKSPACE: 8, 6 | DELETE: 46, 7 | DOWN: 40, 8 | ENTER: 13, 9 | LEFT: 37, 10 | RIGHT: 39, 11 | SPACEBAR: 32, 12 | TAB: 9, 13 | UP: 38, 14 | ESCAPE: 27 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /addon/mixins/new-popover-modal-api.js: -------------------------------------------------------------------------------- 1 | const BINDING_RE = /Binding$/; 2 | const CONCAT_PROPS = ['classNames']; 3 | 4 | export default Ember.Mixin.create({ 5 | init(...args) { 6 | this._super(...args); 7 | let opts = this.get('_propertyOptions'); 8 | Ember.assert(`[ember-widgets] popover or modal must be initialized with _propertyOptions`, 9 | !this.get('__renderPopoverNewAPI') || !!opts); 10 | this._setupProperties(opts); 11 | }, 12 | _setupProperties(props={}) { 13 | let boundPropKeys = []; 14 | let bareProps = {}; 15 | let concatPropKeys = []; 16 | Object.keys(props).forEach(key => { 17 | if (BINDING_RE.test(key)) { 18 | boundPropKeys.push(key); 19 | } else if (CONCAT_PROPS.includes(key)) { 20 | concatPropKeys.push(key); 21 | } else { 22 | bareProps[key] = props[key]; 23 | } 24 | }); 25 | boundPropKeys.forEach(boundKey => { 26 | let barePropName = boundKey.replace(BINDING_RE, ''); 27 | 28 | // "install" binding 29 | this[barePropName] = Ember.computed.alias(props[boundKey]); 30 | }); 31 | 32 | // set non-bound props 33 | this.setProperties(bareProps); 34 | 35 | for (let key of concatPropKeys) { 36 | let originalProps = this.get(key) || []; 37 | let newProps = props[key] || []; 38 | this.set(key, [...originalProps, ...newProps]); 39 | } 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /addon/mixins/popover.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | import StyleBindingsMixin from './style-bindings'; 4 | import BodyEventListener from './body-event-listener'; 5 | 6 | export default Ember.Mixin.create(StyleBindingsMixin, BodyEventListener, { 7 | layoutName: 'popover', 8 | classNames: ['popover'], 9 | classNameBindings: ['isShowing:in', 'fadeEnabled:fade', 'placement'], 10 | styleBindings: ['left', 'top', 'display', 'visibility'], 11 | targetElement: null, 12 | contentViewClass: null, 13 | fade: true, 14 | escToCancel: true, 15 | placement: 'top', 16 | display: 'block', 17 | visibility: 'hidden', 18 | debounceTime: 100, 19 | fadeEnabled: Ember.computed(function() { 20 | if (window.EMBER_WIDGETS_DISABLE_ANIMATIONS) { 21 | return false; 22 | } 23 | return this.get('fade'); 24 | }).property('fade'), 25 | left: 0, 26 | top: 0, 27 | marginTop: 23, 28 | marginLeft: 10, 29 | isShowing: false, 30 | inserted: false, 31 | title: '', 32 | content: '', 33 | _resizeHandler: null, 34 | _scrollHandler: null, 35 | _contentViewClass: Ember.computed(function() { 36 | if (this.get('contentViewClass')) { 37 | return this.get('contentViewClass'); 38 | } 39 | return Ember.View.extend({ 40 | templateName: 'view-parent-view-content' 41 | }); 42 | }).property('contentViewClass'), 43 | 44 | /** 45 | Called when the popover has been initially rendered and properly positioned. 46 | */ 47 | didRenderPopover: () => {}, 48 | 49 | didInsertElement: function() { 50 | this._super(); 51 | Ember.run.schedule('afterRender', () => { 52 | this.snapToPosition(); 53 | this.set('visibility', 'visible'); 54 | this.set('isShowing', true); 55 | 56 | Ember.run.schedule('afterRender', () => { 57 | if (this.isDestroying) { 58 | return; 59 | } 60 | this.didRenderPopover(); 61 | }); 62 | }); 63 | }, 64 | willDestroyElement: function() { 65 | this.$().off($.support.transition.end); 66 | return this._super(); 67 | }, 68 | bodyClick: function() { 69 | return this.hide(); 70 | }, 71 | hide: function() { 72 | if (this.isDestroyed) { 73 | return; 74 | } 75 | 76 | Ember.run.join(() => { 77 | this.set('isShowing', false); 78 | 79 | if (this.get('fadeEnabled')) { 80 | this.$().one($.support.transition.end, () => { 81 | Ember.run(() => { 82 | if (this.isDestroyed) { 83 | return; 84 | } 85 | this.closePopover ? this.closePopover() : this.destroy(); 86 | }); 87 | }); 88 | } else { 89 | this.closePopover ? this.closePopover() : this.destroy(); 90 | } 91 | }); 92 | }, 93 | /* 94 | Calculate the offset of the given iframe relative to the top window. 95 | - Walks up the iframe chain, checking the offset of each one till it reaches top 96 | - Only works with friendly iframes. 97 | - Takes into account scrolling, but comes up with a result relative to 98 | top iframe, regardless of being visibile withing intervening frames. 99 | 100 | @param window win the iframe we're interested in (e.g. window) 101 | @param object pos an object containing the offset so far: 102 | { left: [x], top: [y] } 103 | (optional - initializes with 0,0 if undefined) 104 | @return pos object above 105 | 106 | via http://stackoverflow.com/a/9676655 107 | */ 108 | 109 | computeFrameOffset: function(win, pos) { 110 | var found, frame, frames, rect, _i, _len; 111 | if (pos == null) { 112 | pos = { 113 | top: 0, 114 | left: 0 115 | }; 116 | } 117 | frames = win.parent.document.getElementsByTagName('iframe'); 118 | found = false; 119 | for (_i = 0, _len = frames.length; _i < _len; _i++) { 120 | frame = frames[_i]; 121 | if (frame.contentWindow === win) { 122 | found = true; 123 | break; 124 | } 125 | } 126 | if (found) { 127 | rect = frame.getBoundingClientRect(); 128 | pos.left += rect.left; 129 | pos.top += rect.top; 130 | if (win !== top) { 131 | this.computeFrameOffset(win.parent, pos); 132 | } 133 | } 134 | return pos; 135 | }, 136 | getOffset: function($target) { 137 | var doc, pos, win; 138 | pos = $target.offset(); 139 | doc = $target[0].ownerDocument; 140 | win = doc.defaultView; 141 | return this.computeFrameOffset(win, pos); 142 | }, 143 | snapToPosition: function() { 144 | var $target, actualHeight, actualWidth, pos; 145 | $target = $(this.get('targetElement')); 146 | if ((this.get('_state') || this.get('state')) !== 'inDOM') { 147 | return; 148 | } 149 | actualWidth = this.$()[0].offsetWidth; 150 | actualHeight = this.$()[0].offsetHeight; 151 | if (Ember.isEmpty($target)) { 152 | pos = { 153 | top: this.get('top'), 154 | left: this.get('left'), 155 | width: 0, 156 | height: 0 157 | }; 158 | } else { 159 | pos = this.getOffset($target); 160 | pos.width = $target[0].offsetWidth; 161 | pos.height = $target[0].offsetHeight; 162 | } 163 | switch (this.get('placement')) { 164 | case 'bottom': 165 | this.set('top', pos.top + pos.height); 166 | this.set('left', pos.left + pos.width / 2 - actualWidth / 2); 167 | break; 168 | case 'top': 169 | this.set('top', pos.top - actualHeight); 170 | this.set('left', pos.left + pos.width / 2 - actualWidth / 2); 171 | break; 172 | case 'top-right': 173 | this.set('top', pos.top); 174 | this.set('left', pos.left + pos.width); 175 | break; 176 | case 'top-left': 177 | this.set('top', pos.top); 178 | this.set('left', pos.left - actualWidth); 179 | break; 180 | case 'bottom-right': 181 | this.set('top', pos.top + pos.height); 182 | this.set('left', pos.left + pos.width - actualWidth); 183 | break; 184 | case 'bottom-left': 185 | this.set('top', pos.top + pos.height); 186 | this.set('left', pos.left); 187 | break; 188 | case 'left': 189 | this.set('top', pos.top - this.get('marginTop')); 190 | this.set('left', pos.left - actualWidth); 191 | break; 192 | case 'right': 193 | this.set('top', pos.top - this.get('marginTop')); 194 | this.set('left', pos.left + pos.width); 195 | break; 196 | } 197 | this.correctIfOffscreen(); 198 | if (!Ember.isEmpty($target)) { 199 | this.positionArrow(); 200 | } 201 | }, 202 | positionArrow: function() { 203 | var $target, arrowSize, left, pos, top, arrowStyle; 204 | $target = $(this.get('targetElement')); 205 | pos = this.getOffset($target); 206 | pos.width = $target[0].offsetWidth; 207 | pos.height = $target[0].offsetHeight; 208 | arrowSize = 22; 209 | switch (this.get('placement')) { 210 | case 'left': 211 | case 'right': 212 | top = pos.top + pos.height / 2 - this.get('top') - arrowSize / 2; 213 | arrowStyle = 'margin-top:' + top + 'px;'; 214 | case 'top': 215 | case 'bottom': 216 | left = pos.left + pos.width / 2 - this.get('left') - arrowSize / 2; 217 | arrowStyle = 'margin-left:' + left + 'px;'; 218 | } 219 | this.set('arrowStyle', Ember.String.htmlSafe(arrowStyle)); 220 | }, 221 | correctIfOffscreen: function() { 222 | var actualHeight, actualWidth, bodyHeight, bodyWidth; 223 | bodyWidth = $('body').width(); 224 | bodyHeight = $('body').height(); 225 | actualWidth = this.$()[0].offsetWidth; 226 | actualHeight = this.$()[0].offsetHeight; 227 | if (this.get('left') + actualWidth > bodyWidth) { 228 | this.set('left', bodyWidth - actualWidth - this.get('marginLeft')); 229 | } 230 | if (this.get('left') < 0) { 231 | this.set('left', this.get('marginLeft')); 232 | } 233 | if (this.get('top') + actualHeight > bodyHeight) { 234 | this.set('top', bodyHeight - actualHeight - this.get('marginTop')); 235 | } 236 | if (this.get('top') < 0) { 237 | this.set('top', this.get('marginTop')); 238 | } 239 | }, 240 | keyHandler: Ember.computed(function() { 241 | var _this = this; 242 | return function(event) { 243 | if (event.keyCode === 27 && _this.get('escToCancel')) { 244 | _this.hide(); 245 | return false; 246 | } 247 | }; 248 | }), 249 | debounceSnapToPosition: Ember.computed(function() { 250 | var _this = this; 251 | return function() { 252 | return Ember.run.debounce( 253 | _this, 254 | _this.snapToPosition, 255 | _this.get('debounceTime') 256 | ); 257 | }; 258 | }), 259 | _setupDocumentHandlers: function() { 260 | var _this = this; 261 | this._super(); 262 | if (!this._hideHandler) { 263 | this._hideHandler = () => { 264 | this.hide(); 265 | }; 266 | $(document).on('popover:hide', this._hideHandler); 267 | } 268 | if (!this._resizeHandler) { 269 | this._resizeHandler = this.get('debounceSnapToPosition'); 270 | $(document).on('resize', this._resizeHandler); 271 | } 272 | if (!this._scrollHandler) { 273 | this._scrollHandler = this.get('debounceSnapToPosition'); 274 | $(document).on('scroll', this._scrollHandler); 275 | } 276 | return $(document).on('keyup', this.get('keyHandler')); 277 | }, 278 | _removeDocumentHandlers: function() { 279 | this._super(); 280 | $(document).off('popover:hide', this._hideHandler); 281 | this._hideHandler = null; 282 | $(document).off('resize', this._resizeHandler); 283 | this._resizeHandler = null; 284 | $(document).off('scroll', this._scrollHandler); 285 | this._scrollHandler = null; 286 | return $(document).off('keyup', this.get('keyHandler')); 287 | } 288 | }); 289 | -------------------------------------------------------------------------------- /addon/mixins/style-bindings.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { keys, values } from 'lodash-es'; 3 | 4 | export default Ember.Mixin.create({ 5 | concatenatedProperties: ['styleBindings'], 6 | attributeBindings: ['style'], 7 | unitType: 'px', 8 | createStyleString: function(styleName, property) { 9 | var value; 10 | value = this.get(property); 11 | if (value === void 0) { 12 | return; 13 | } 14 | if (Ember.typeOf(value) === 'number') { 15 | value = value + this.get('unitType'); 16 | } 17 | return styleName + ":" + value + ";"; 18 | }, 19 | applyStyleBindings: function() { 20 | var lookup, properties, styleBindings, styleComputed, styles; 21 | styleBindings = this.styleBindings; 22 | if (!styleBindings) { 23 | return; 24 | } 25 | // get properties from bindings e.g. ['width', 'top'] 26 | lookup = {}; 27 | styleBindings.forEach(function(binding) { 28 | var property, ref, style; 29 | ref = binding.split(':'); 30 | property = ref[0]; 31 | style = ref[1]; 32 | return lookup[style || property] = property; 33 | }); 34 | styles = keys(lookup); 35 | properties = values(lookup); 36 | // create computed property 37 | styleComputed = Ember.computed((function(_this) { 38 | return function() { 39 | var styleString, styleTokens; 40 | styleTokens = styles.map(function(style) { 41 | return _this.createStyleString(style, lookup[style]); 42 | }); 43 | styleString = styleTokens.join(''); 44 | if (styleString.length !== 0) { 45 | return Ember.String.htmlSafe(styleString); 46 | } 47 | }; 48 | })(this)); 49 | // add dependents to computed property 50 | styleComputed.property.apply(styleComputed, properties); 51 | // define style computed properties 52 | return Ember.defineProperty(this, 'style', styleComputed); 53 | }, 54 | init: function() { 55 | this.applyStyleBindings(); 56 | return this._super(); 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /addon/mixins/tabbable-modal.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import KeyboardHelper from './keyboard-helper'; 3 | 4 | export default Ember.Mixin.create(KeyboardHelper, { 5 | // these are variables from the modal component (modal.coffee) to enforce 6 | // the modality when clicking outside of the modal and to allow pressing ESC 7 | // to cancel the modal. Please check if this mixin is going to be used 8 | // outside of modal.coffee 9 | enforceModality: false, 10 | escToCancel: true, 11 | currentFocus: null, 12 | _focusTabbable: function() { 13 | // Set focus to the first match: 14 | // 1. The saved focused element 15 | // 2. First element inside the dialog matching [autofocus] 16 | // 3. Tabbable element inside the content element 17 | // 4. The close button (has class "close") 18 | var focusElement, hasFocus; 19 | hasFocus = [this.get('currentFocus')]; 20 | if (!this.$().has(hasFocus[0]).length) { 21 | hasFocus = this.$('[autofocus]'); 22 | } 23 | // if we have more than two tabbable objects, we do not want to tab 24 | // to close button 25 | // while if we do not have any choice, the close button is chosen 26 | if (hasFocus.length === 0) { 27 | hasFocus = this.$(':tabbable'); 28 | } 29 | if (focusElement = hasFocus[0]) { 30 | if (focusElement.className.indexOf('close') > -1) { 31 | if (hasFocus.length > 1) { 32 | focusElement = hasFocus[1]; 33 | } 34 | } 35 | focusElement.focus(); 36 | this.set('currentFocus', focusElement); 37 | } 38 | }, 39 | _checkContainingElement: function(containers, element) { 40 | var containerItem, i, len; 41 | for (i = 0, len = containers.length; i < len; i++) { 42 | containerItem = containers[i]; 43 | if (containerItem === element || $.contains(containerItem, element)) { 44 | return true; 45 | } 46 | } 47 | return false; 48 | }, 49 | mouseDown: function(event) { 50 | this._super(event); 51 | if (this._checkContainingElement(this.$(':tabbable'), event.target)) { 52 | this.set('currentFocus', event.target); 53 | } else { 54 | // if we click on a non-tabbable element, we should just set the focus back 55 | // to the modal and reset the tab loop 56 | this.set('currentFocus', null); 57 | return this.$().focus(); 58 | } 59 | }, 60 | // capture the TAB key and make a cycle tab loop among the tabbable elements 61 | // inside the modal. Remove the close button from the loop 62 | keyDown: function(event) { 63 | var _currentFocus, _index, first, last, tabbableObjects; 64 | this._super(event); 65 | if (event.isDefaultPrevented()) { 66 | return; 67 | } 68 | if (event.keyCode === this.KEY_CODES.ESCAPE && this.get('escToCancel')) { 69 | this.send('sendCancel'); 70 | event.preventDefault(); 71 | } else if (event.keyCode === this.KEY_CODES.ENTER && this.get('enterToConfirm')) { 72 | if (!this.get('isDisabled')) { 73 | this.send('sendConfirm'); 74 | event.preventDefault(); 75 | } 76 | } else if (event.keyCode === this.KEY_CODES.TAB) { 77 | // tabbable objects list without close button 78 | tabbableObjects = this.$(":tabbable").not('.close'); 79 | _currentFocus = document.activeElement; 80 | _index = tabbableObjects.index(_currentFocus); 81 | // if the current target does not belong to tabbable objects 82 | // we need to guide the focus back to a tabbable element 83 | if (_index === -1) { 84 | this._focusTabbable(); 85 | return false; 86 | } 87 | // process the tab loop by checking two ends to construct the loop 88 | if (tabbableObjects.length > 0) { 89 | first = tabbableObjects[0]; 90 | last = tabbableObjects[tabbableObjects.length - 1]; 91 | // check the two ends 92 | if (event.target === last && !event.shiftKey) { 93 | first.focus(); 94 | this.set('currentFocus', first); 95 | event.preventDefault(); 96 | } else if (event.target === first && event.shiftKey) { 97 | this.set('currentFocus', last); 98 | last.focus(); 99 | event.preventDefault(); 100 | } else { 101 | this.set('currentFocus', tabbableObjects[_index + 1]); 102 | } 103 | } 104 | } 105 | return true; 106 | } 107 | }); 108 | -------------------------------------------------------------------------------- /addon/mysterious-dependency/ember-addepar-mixins/README.md: -------------------------------------------------------------------------------- 1 | ember-addepar-mixins 2 | ==================== 3 | 4 | Extend Ember with some useful mixins 5 | -------------------------------------------------------------------------------- /addon/mysterious-dependency/ember-addepar-mixins/resize_handler.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import jQuery from 'jquery'; 3 | 4 | var AddeparMixins; 5 | 6 | AddeparMixins = Ember.AddeparMixins || Ember.Namespace.create(); 7 | 8 | AddeparMixins.ResizeHandlerMixin = Ember.Mixin.create({ 9 | resizeEndDelay: 200, 10 | resizing: false, 11 | onResizeStart: Ember.K, 12 | onResizeEnd: Ember.K, 13 | onResize: Ember.K, 14 | endResize: Ember.computed(function() { 15 | return function(event) { 16 | if (this.isDestroyed) { 17 | return; 18 | } 19 | this.set('resizing', false); 20 | return typeof this.onResizeEnd === "function" ? this.onResizeEnd(event) : void 0; 21 | }; 22 | }), 23 | handleWindowResize: function(event) { 24 | // Ignore bubbled "resize" events. These can come from other jquery 25 | // libraries, eg jquery-ui's "resizable" widget 26 | if (event.target !== window) { 27 | return; 28 | } 29 | if (!this.get('resizing')) { 30 | this.set('resizing', true); 31 | if (typeof this.onResizeStart === "function") { 32 | this.onResizeStart(event); 33 | } 34 | } 35 | if (typeof this.onResize === "function") { 36 | this.onResize(event); 37 | } 38 | return Ember.run.debounce(this, this.get('endResize'), event, this.get('resizeEndDelay')); 39 | }, 40 | didInsertElement: function() { 41 | this._super(); 42 | return this._setupResizeDocumentHandlers(); 43 | }, 44 | willDestroyElement: function() { 45 | this._removeResizeDocumentHandlers(); 46 | return this._super(); 47 | }, 48 | _setupResizeDocumentHandlers: function() { 49 | if (this._resizeHandler) { 50 | return; 51 | } 52 | this._resizeHandler = jQuery.proxy(this.get('handleWindowResize'), this); 53 | return jQuery(window).on("resize." + this.elementId, this._resizeHandler); 54 | }, 55 | _removeResizeDocumentHandlers: function() { 56 | jQuery(window).off("resize." + this.elementId, this._resizeHandler); 57 | return this._resizeHandler = null; 58 | } 59 | }); 60 | 61 | export default AddeparMixins; 62 | -------------------------------------------------------------------------------- /addon/services/popover.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { getOwner } from '@ember/application'; 3 | import { assert } from '@ember/debug'; 4 | import ModalComponent from 'ember-widgets/components/modal-component'; 5 | import PopoverComponent from 'ember-widgets/components/popover-component'; 6 | import $ from 'jquery'; 7 | 8 | function classInheritsFrom(childKlass, parentKlass) { 9 | return parentKlass.prototype.isPrototypeOf(childKlass.prototype); 10 | } 11 | 12 | export default Ember.Service.extend({ 13 | init() { 14 | this._super(...arguments); 15 | 16 | // Array of specifications for modals and/or popovers 17 | this.popovers = Ember.A([]); 18 | }, 19 | 20 | openPopover(componentName, options={}, hideAll=true) { 21 | assert(`[ember-widgets#openPopover] {{render-popover}} must be included in a template for popovers to open`, !!this._popoverDidRender); 22 | 23 | let ComponentClass = getOwner(this).factoryFor(`component:${componentName}`).class; 24 | assert(`[ember-widget#openPopover] cannot find component class for name "${componentName}"`, 25 | !!ComponentClass,); 26 | assert(`[ember-widgets#PopoverComponent] The Component Class for "${componentName}" does not extend PopoverComponent`, 27 | classInheritsFrom(ComponentClass, PopoverComponent)); 28 | 29 | let rootElementSelector = options.rootElement; 30 | delete options.rootElement; 31 | let destinationElement = this._getDestinationElement(ComponentClass, rootElementSelector); 32 | 33 | let { popoverSpec, close } = this.buildPopoverSpec(componentName, destinationElement, options); 34 | 35 | if (hideAll) { 36 | this._hideAllPopovers(); 37 | } 38 | this.openPopoverSpec(popoverSpec); 39 | return close; 40 | }, 41 | 42 | openModal(componentName, options={}) { 43 | assert(`[ember-widgets#openModal] {{render-popover}} must be included in a template for popovers to open`, !!this._popoverDidRender); 44 | 45 | let ComponentClass = getOwner(this).factoryFor(`component:${componentName}`).class; 46 | assert(`[ember-widget#openModal] cannot find component class for name "${componentName}"`, 47 | !!ComponentClass,); 48 | assert(`[ember-widgets#openModal] The Component Class for "${componentName}" does not extend ModalComponent`, 49 | classInheritsFrom(ComponentClass, ModalComponent)); 50 | 51 | let rootElementSelector = options.rootElement; 52 | delete options.rootElement; 53 | let destinationElement = this._getDestinationElement(ComponentClass, rootElementSelector); 54 | 55 | let { popoverSpec, close } = this.buildPopoverSpec(componentName, destinationElement, options); 56 | 57 | this._hideAllModals(); 58 | this.openPopoverSpec(popoverSpec); 59 | return close; 60 | }, 61 | 62 | /* 63 | * A popoverSpec is an object describing (specifying) a component to be 64 | * rendered by the `{{render-popover}}` component. In 65 | * `app/templates/components/render-popover.hbs` these objects are iterated 66 | * and rendered using the component helper. 67 | * 68 | * A popoverSpec also has a `closePopover` method added to the `propertyOptions` 69 | * for that spec. This function allows a popover/modal/etc to close itself. 70 | */ 71 | buildPopoverSpec(componentName, destinationElement, options) { 72 | let close = () => { 73 | this.popovers.removeObject(popoverSpec); 74 | }; 75 | let popoverSpec = { 76 | componentName, 77 | destinationElement, 78 | propertyOptions: { 79 | ...options, closePopover: close 80 | } 81 | }; 82 | 83 | return { popoverSpec, close }; 84 | }, 85 | 86 | /* 87 | * It is likely you should reach for `openPopover` or `openModal` before 88 | * using this method. Those methods perform special additional behaviors 89 | * around hiding currently open items of their type. 90 | * 91 | * This is a lower level method which permits developers to open any popover 92 | * spec. You might use this if you want to use the popover infrastructure but 93 | * also want to avoid the behaviors of the `openPopover` and `openModal` 94 | * systems. 95 | */ 96 | openPopoverSpec(popoverSpec) { 97 | assert(`[ember-widgets#openPopoverSpec] {{render-popover}} must be included in a template for popovers to open`, !!this._popoverDidRender); 98 | this.popovers.pushObject(popoverSpec); 99 | }, 100 | 101 | _hideAllModals() { 102 | $(document).trigger('modal:hide'); 103 | }, 104 | 105 | _hideAllPopovers() { 106 | $(document).trigger('popover:hide'); 107 | }, 108 | 109 | _getDestinationElement(ComponentClass, rootElement) { 110 | let selector = ComponentClass.rootElement || rootElement; 111 | assert(`[ember-widgets] No selector found when attempting to openModal`, !!selector); 112 | let el = document.querySelector(selector); 113 | assert(`[ember-widgets] No rootElement found for selector "${selector}`, !!el); 114 | return el; 115 | }, 116 | 117 | // TODO: This should be deprecated 118 | open(destinationElement, ComponentClass, options={}) { 119 | if (!this._popoverDidRender) { 120 | throw new Error( 121 | '{{render-popover}} must be included in a template for popovers to open. ' + 122 | "Unit-integration tests don't include application.hbs; you must add {{render-popover}} to this.render()." 123 | ); 124 | } 125 | 126 | let popoverSpec = { 127 | destinationElement 128 | }; 129 | 130 | let closePopover = () => { 131 | this.popovers.removeObject(popoverSpec); 132 | }; 133 | 134 | popoverSpec.childViews = [ 135 | ComponentClass.extend({ 136 | ...options, 137 | closePopover 138 | }) 139 | ]; 140 | 141 | this.popovers.pushObject(popoverSpec); 142 | 143 | return closePopover; 144 | }, 145 | 146 | registerPopoverRendered() { 147 | this._popoverDidRender = true; 148 | } 149 | }); 150 | -------------------------------------------------------------------------------- /addon/utils/color-picker.js: -------------------------------------------------------------------------------- 1 | var colorNameToHexMap, colorToHex, expandHexColor, rgbToHex; 2 | 3 | rgbToHex = function(r, g, b) { 4 | return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); 5 | }; 6 | 7 | expandHexColor = function(color) { 8 | // expand 3-digit hex codes to 6-digit, otherwise don't modify 9 | return color.replace(/^#([0-9A-F])([0-9A-F])([0-9A-F])$/i, '#$1$1$2$2$3$3'); 10 | }; 11 | 12 | colorNameToHexMap = { 13 | aliceblue: "#f0f8ff", 14 | antiquewhite: "#faebd7", 15 | aqua: "#00ffff", 16 | aquamarine: "#7fffd4", 17 | azure: "#f0ffff", 18 | beige: "#f5f5dc", 19 | bisque: "#ffe4c4", 20 | black: "#000000", 21 | blanchedalmond: "#ffebcd", 22 | blue: "#0000ff", 23 | blueviolet: "#8a2be2", 24 | brown: "#a52a2a", 25 | burlywood: "#deb887", 26 | cadetblue: "#5f9ea0", 27 | chartreuse: "#7fff00", 28 | chocolate: "#d2691e", 29 | coral: "#ff7f50", 30 | cornflowerblue: "#6495ed", 31 | cornsilk: "#fff8dc", 32 | crimson: "#dc143c", 33 | cyan: "#00ffff", 34 | darkblue: "#00008b", 35 | darkcyan: "#008b8b", 36 | darkgoldenrod: "#b8860b", 37 | darkgray: "#a9a9a9", 38 | darkgreen: "#006400", 39 | darkkhaki: "#bdb76b", 40 | darkmagenta: "#8b008b", 41 | darkolivegreen: "#556b2f", 42 | darkorange: "#ff8c00", 43 | darkorchid: "#9932cc", 44 | darkred: "#8b0000", 45 | darksalmon: "#e9967a", 46 | darkseagreen: "#8fbc8f", 47 | darkslateblue: "#483d8b", 48 | darkslategray: "#2f4f4f", 49 | darkturquoise: "#00ced1", 50 | darkviolet: "#9400d3", 51 | deeppink: "#ff1493", 52 | deepskyblue: "#00bfff", 53 | dimgray: "#696969", 54 | dodgerblue: "#1e90ff", 55 | firebrick: "#b22222", 56 | floralwhite: "#fffaf0", 57 | forestgreen: "#228b22", 58 | fuchsia: "#ff00ff", 59 | gainsboro: "#dcdcdc", 60 | ghostwhite: "#f8f8ff", 61 | gold: "#ffd700", 62 | goldenrod: "#daa520", 63 | gray: "#808080", 64 | green: "#008000", 65 | greenyellow: "#adff2f", 66 | honeydew: "#f0fff0", 67 | hotpink: "#ff69b4", 68 | "indianred ": "#cd5c5c", 69 | indigo: "#4b0082", 70 | ivory: "#fffff0", 71 | khaki: "#f0e68c", 72 | lavender: "#e6e6fa", 73 | lavenderblush: "#fff0f5", 74 | lawngreen: "#7cfc00", 75 | lemonchiffon: "#fffacd", 76 | lightblue: "#add8e6", 77 | lightcoral: "#f08080", 78 | lightcyan: "#e0ffff", 79 | lightgoldenrodyellow: "#fafad2", 80 | lightgrey: "#d3d3d3", 81 | lightgreen: "#90ee90", 82 | lightpink: "#ffb6c1", 83 | lightsalmon: "#ffa07a", 84 | lightseagreen: "#20b2aa", 85 | lightskyblue: "#87cefa", 86 | lightslategray: "#778899", 87 | lightsteelblue: "#b0c4de", 88 | lightyellow: "#ffffe0", 89 | lime: "#00ff00", 90 | limegreen: "#32cd32", 91 | linen: "#faf0e6", 92 | magenta: "#ff00ff", 93 | maroon: "#800000", 94 | mediumaquamarine: "#66cdaa", 95 | mediumblue: "#0000cd", 96 | mediumorchid: "#ba55d3", 97 | mediumpurple: "#9370d8", 98 | mediumseagreen: "#3cb371", 99 | mediumslateblue: "#7b68ee", 100 | mediumspringgreen: "#00fa9a", 101 | mediumturquoise: "#48d1cc", 102 | mediumvioletred: "#c71585", 103 | midnightblue: "#191970", 104 | mintcream: "#f5fffa", 105 | mistyrose: "#ffe4e1", 106 | moccasin: "#ffe4b5", 107 | navajowhite: "#ffdead", 108 | navy: "#000080", 109 | oldlace: "#fdf5e6", 110 | olive: "#808000", 111 | olivedrab: "#6b8e23", 112 | orange: "#ffa500", 113 | orangered: "#ff4500", 114 | orchid: "#da70d6", 115 | palegoldenrod: "#eee8aa", 116 | palegreen: "#98fb98", 117 | paleturquoise: "#afeeee", 118 | palevioletred: "#d87093", 119 | papayawhip: "#ffefd5", 120 | peachpuff: "#ffdab9", 121 | peru: "#cd853f", 122 | pink: "#ffc0cb", 123 | plum: "#dda0dd", 124 | powderblue: "#b0e0e6", 125 | purple: "#800080", 126 | red: "#ff0000", 127 | rosybrown: "#bc8f8f", 128 | royalblue: "#4169e1", 129 | saddlebrown: "#8b4513", 130 | salmon: "#fa8072", 131 | sandybrown: "#f4a460", 132 | seagreen: "#2e8b57", 133 | seashell: "#fff5ee", 134 | sienna: "#a0522d", 135 | silver: "#c0c0c0", 136 | skyblue: "#87ceeb", 137 | slateblue: "#6a5acd", 138 | slategray: "#708090", 139 | snow: "#fffafa", 140 | springgreen: "#00ff7f", 141 | steelblue: "#4682b4", 142 | tan: "#d2b48c", 143 | teal: "#008080", 144 | thistle: "#d8bfd8", 145 | tomato: "#ff6347", 146 | turquoise: "#40e0d0", 147 | violet: "#ee82ee", 148 | wheat: "#f5deb3", 149 | white: "#ffffff", 150 | whitesmoke: "#f5f5f5", 151 | yellow: "#ffff00", 152 | yellowgreen: "#9acd32" 153 | }; 154 | 155 | colorToHex = function(color) { 156 | var blue, digits, green, opacity, red; 157 | if (!color) { 158 | return color; 159 | } 160 | if (color.substr(0, 1) === "#" || color === "transparent") { 161 | return color.toLowerCase(); 162 | } 163 | if (color in colorNameToHexMap) { 164 | return colorNameToHexMap[color.toLowerCase()]; 165 | } 166 | digits = /(.*?)rgb(a)?\((\d+), (\d+), (\d+)(, (\d+))?\)/.exec(color); 167 | if ((digits != null ? digits.length : void 0) === 8) { 168 | red = parseInt(digits[3]); 169 | green = parseInt(digits[4]); 170 | blue = parseInt(digits[5]); 171 | opacity = parseInt(digits[7]); 172 | if (opacity === 0) { 173 | return "transparent"; 174 | } 175 | return rgbToHex(red, green, blue); 176 | } 177 | return void 0; 178 | }; 179 | 180 | export default { 181 | colorNameToHexMap: colorNameToHexMap, 182 | colorToHex: colorToHex, 183 | expandHexColor: expandHexColor, 184 | rgbToHex: rgbToHex 185 | }; 186 | -------------------------------------------------------------------------------- /addon/utils/widget-config.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { getOwner } from '@ember/application'; 3 | 4 | var Config = Ember.Object.extend({ 5 | disableAnimations: Ember.computed(function(){ 6 | return getOwner(this).lookup('config:environment'); 7 | }) 8 | }); 9 | 10 | export default Config.create(); 11 | -------------------------------------------------------------------------------- /addon/views/carousel-indicator.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View.extend({ 4 | classNameBindings: 'isActive:active', 5 | isActive: Ember.computed(function() { 6 | return this.get('contentIndex') === this.get('controller.activeIndex'); 7 | }).property('contentIndex', 'controller.activeIndex'), 8 | click: function() { 9 | return this.get('controller').to(this.get('contentIndex')); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /addon/views/carousel-item.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View.extend({ 4 | classNames: 'item' 5 | }); 6 | -------------------------------------------------------------------------------- /addon/views/multi-select-item.js: -------------------------------------------------------------------------------- 1 | import SelectOption from './select-option'; 2 | 3 | export default SelectOption.extend({ 4 | processDropDownShown: function() { 5 | this._super(); 6 | return this.get('selectComponent').focusTextField(); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /addon/views/multi-select-option.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View.extend({ 4 | tagName: 'li', 5 | templateName: 'multi-select-item', 6 | classNames: 'ember-select-search-choice', 7 | didInsertElement: function() { 8 | this._super(); 9 | this.labelPathDidChange(); 10 | }, 11 | labelPathDidChange: Ember.observer('content', 'labelPath', function() { 12 | var labelPath, path; 13 | labelPath = this.get('labelPath'); 14 | path = labelPath ? "content." + labelPath : 'content'; 15 | Ember.defineProperty(this, 'label', Ember.computed.alias(path)); 16 | this.notifyPropertyChange('label'); 17 | }) 18 | }); 19 | -------------------------------------------------------------------------------- /addon/views/multi-select-tooltip-item.js: -------------------------------------------------------------------------------- 1 | import SelectTooltipOption from './select-tooltip-option'; 2 | 3 | export default SelectTooltipOption.extend({ 4 | processDropDownShown: function() { 5 | this._super(); 6 | return this.get('selectComponent').focusTextField(); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /addon/views/select-option.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { get } = Ember; 4 | 5 | // The view for each item in the select. 6 | export default Ember.View.extend({ 7 | tagName: 'li', 8 | templateName: 'select-item', 9 | layoutName: 'select-item-layout', 10 | classNames: 'ember-select-result-item', 11 | classNameBindings: Ember.A(['content.isGroupOption:ember-select-group', 'isHighlighted:highlighted']), 12 | collapsedGroupHeaderIcon: 'fas fa-caret-right', 13 | expandedGroupHeaderIcon: 'fas fa-caret-down', 14 | isHighlighted: Ember.computed(function() { 15 | return this.get('highlighted') === this.get('content'); 16 | }).property('highlighted', 'content'), 17 | labelPathDidChange: Ember.on('init', Ember.observer('content', 'labelPath', function() { 18 | var labelPath, path; 19 | labelPath = this.get('labelPath'); 20 | 21 | // if it is a raw string, the path is just the context 22 | // if labelPath is specified, the path should be context.labelPath 23 | path = labelPath ? "content." + labelPath : 'content'; 24 | 25 | // We are creating a computed property called label that is an alias of 26 | // 'context.#{labelPath}' 27 | Ember.defineProperty(this, 'label', Ember.computed.alias(path)); 28 | return this.notifyPropertyChange('label'); 29 | })), 30 | processDropDownShown: function() { 31 | return this.get('selectComponent').send('hideDropdown'); 32 | }, 33 | didInsertElement: function() { 34 | this._super(); 35 | return this.labelPathDidChange(); 36 | }, 37 | 38 | click: function() { 39 | let selection = this.get('content'); 40 | if (get(selection, 'isGroupOption')) { 41 | return; 42 | } 43 | this.set('selectComponent.selection', selection); 44 | this.get('selectComponent').userDidSelect(selection); 45 | // if there's a selection and the dropdown is unexpanded, we want to 46 | // propagate the click event 47 | // if the dropdown is expanded and we select something, don't propagate 48 | if (this.get('showDropdown')) { 49 | this.processDropDownShown(); 50 | return false; 51 | } 52 | }, 53 | mouseEnter: function() { 54 | if (this.get('content.isGroupOption')) { 55 | return; 56 | } 57 | this.set('highlighted', this.get('content')); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /addon/views/select-tooltip-option.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import SelectOption from './select-option'; 3 | 4 | export default SelectOption.extend({ 5 | attributeBindings: ['contentLabel:title'], 6 | contentLabel: Ember.computed(function() { 7 | var labelPath; 8 | labelPath = this.get('labelPath'); 9 | return this.get("content." + labelPath); 10 | }).property('content', 'labelPath') 11 | }); 12 | -------------------------------------------------------------------------------- /addon/views/view-parent-view-content.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View; 4 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/app/.gitkeep -------------------------------------------------------------------------------- /app/components/carousel-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/carousel-component'; 2 | -------------------------------------------------------------------------------- /app/components/color-picker-cell.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/color-picker-cell'; 2 | -------------------------------------------------------------------------------- /app/components/color-picker-dropdown.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/color-picker-dropdown'; 2 | -------------------------------------------------------------------------------- /app/components/color-picker.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/color-picker'; -------------------------------------------------------------------------------- /app/components/debounced-text-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/debounced-text-component'; 2 | -------------------------------------------------------------------------------- /app/components/editable-label-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/editable-label-component'; 2 | -------------------------------------------------------------------------------- /app/components/modal-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/modal-component'; 2 | -------------------------------------------------------------------------------- /app/components/multi-select-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/multi-select-component'; 2 | -------------------------------------------------------------------------------- /app/components/popover-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/popover-component'; 2 | -------------------------------------------------------------------------------- /app/components/popover-link-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/popover-link-component'; 2 | -------------------------------------------------------------------------------- /app/components/radio-button-group-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/radio-button-group-component'; 2 | -------------------------------------------------------------------------------- /app/components/radio-button.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/radio-button'; -------------------------------------------------------------------------------- /app/components/radio-input.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/radio-input'; 2 | -------------------------------------------------------------------------------- /app/components/render-popover.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/render-popover'; -------------------------------------------------------------------------------- /app/components/select-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/select-component'; 2 | -------------------------------------------------------------------------------- /app/components/typeahead-component.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/components/typeahead-component'; 2 | -------------------------------------------------------------------------------- /app/services/popover.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/services/popover'; 2 | -------------------------------------------------------------------------------- /app/templates/accordion-group-layout.hbs: -------------------------------------------------------------------------------- 1 |
2 |

3 | 6 |

7 |
8 |
9 |
{{yield}}
10 |
11 | -------------------------------------------------------------------------------- /app/templates/carousel.hbs: -------------------------------------------------------------------------------- 1 | {{view collectionViewClass tagName="ol" class="carousel-indicators" 2 | content=view.content itemViewClass=carouselIndicatorClass 3 | }} 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/templates/color-picker-button-partial.hbs: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/templates/color-picker-cell.hbs: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /app/templates/color-picker-dropdown.hbs: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /app/templates/color-picker.hbs: -------------------------------------------------------------------------------- 1 | {{partial colorPickerButtonPartial}} 2 | {{#if isDropdownOpen}} 3 | {{color-picker-dropdown 4 | dropdownClass=dropdownClass 5 | colorRows=colorRows 6 | userSelected="userSelected" 7 | hideDropdown="hideDropdown" 8 | setSelectedColor="setSelectedColor" 9 | customColor=customColor 10 | selectedColor=selectedColor 11 | }} 12 | {{/if}} 13 | -------------------------------------------------------------------------------- /app/templates/component-default-content.hbs: -------------------------------------------------------------------------------- 1 |

{{content}}

2 | -------------------------------------------------------------------------------- /app/templates/components/render-popover.hbs: -------------------------------------------------------------------------------- 1 | {{#each popover.popovers as |p|}} 2 | {{#ember-wormhole destinationElement=p.destinationElement}} 3 |
{{! Need a stable element in the wormhole to ensure rendered components are removed in Ember 1.12}} 4 | {{#if p.componentName}} 5 | {{component p.componentName _propertyOptions=p.propertyOptions __renderPopoverNewAPI=true}} 6 | {{else}} 7 | {{#each p.childViews as |childView|}} 8 | {{view childView}} 9 | {{/each}} 10 | {{/if}} 11 |
12 | {{/ember-wormhole}} 13 | {{/each}} 14 | -------------------------------------------------------------------------------- /app/templates/editable-label.hbs: -------------------------------------------------------------------------------- 1 | {{#if isEditing}} 2 | {{view innerTextField}} 3 | {{else}} 4 | {{displayName}} 5 | {{/if}} 6 | -------------------------------------------------------------------------------- /app/templates/modal-content.hbs: -------------------------------------------------------------------------------- 1 |

{{content}}

2 | -------------------------------------------------------------------------------- /app/templates/modal-footer.hbs: -------------------------------------------------------------------------------- 1 | {{#if confirmText}} 2 | 6 | {{/if}} 7 | 8 | {{#if cancelText}} 9 | 12 | {{/if}} 13 | 14 | {{#if closeText}} 15 | 18 | {{/if}} 19 | -------------------------------------------------------------------------------- /app/templates/modal-header.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/templates/modal.hbs: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/templates/multi-select-item.hbs: -------------------------------------------------------------------------------- 1 |
{{view.label}}
2 | × 4 | 5 | -------------------------------------------------------------------------------- /app/templates/multi-select.hbs: -------------------------------------------------------------------------------- 1 | 18 | 19 | 50 | -------------------------------------------------------------------------------- /app/templates/popover-link-popover.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if title}} 3 |

{{title}}

4 | {{/if}} 5 |
6 | {{view view._contentViewClass}} 7 |
8 | -------------------------------------------------------------------------------- /app/templates/popover.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if title}} 3 |

{{title}}

4 | {{/if}} 5 |
6 | {{view _contentViewClass 7 | content=content 8 | popoverComponent=this}} 9 |
10 | -------------------------------------------------------------------------------- /app/templates/radio-button-layout.hbs: -------------------------------------------------------------------------------- 1 | {{radio-input}} 2 | {{yield}} 3 | -------------------------------------------------------------------------------- /app/templates/select-item-layout.hbs: -------------------------------------------------------------------------------- 1 | {{#if view.content.isGroupOption}} 2 |
3 | {{view.content.name}} 4 | {{#if view.selectComponent.componentNameForGroupTooltip}} 5 | {{component view.selectComponent.componentNameForGroupTooltip groupItem=view.content}} 6 | {{/if}} 7 |
8 | {{else}} 9 | {{#if view.titleOnOptions}} 10 | 11 | {{view.label}} 12 | 13 | {{else}} 14 | {{view.label}} 15 | {{/if}} 16 | {{/if}} 17 | -------------------------------------------------------------------------------- /app/templates/select-item.hbs: -------------------------------------------------------------------------------- 1 | {{#if view.titleOnOptions}} 2 | 3 | {{view.label}} 4 | 5 | {{else}} 6 | {{view.label}} 7 | {{/if}} 8 | -------------------------------------------------------------------------------- /app/templates/select-list-view-partial.hbs: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /app/templates/select.hbs: -------------------------------------------------------------------------------- 1 | 13 | 40 | -------------------------------------------------------------------------------- /app/templates/typeahead-list-view-partial.hbs: -------------------------------------------------------------------------------- 1 | {{#unless hasNoResults}} 2 | 19 | {{#if footerView}} 20 | {{view footerView selectComponent=this}} 21 | {{/if}} 22 | {{/unless}} 23 | -------------------------------------------------------------------------------- /app/templates/typeahead.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{view searchView 3 | classBinding="searchFieldClass" 4 | disabled=disabled 5 | selectComponent=this}} 6 |
7 | 12 | -------------------------------------------------------------------------------- /app/templates/view-parent-view-content.hbs: -------------------------------------------------------------------------------- 1 | {{content}} 2 | -------------------------------------------------------------------------------- /app/views/carousel-item.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/views/carousel-item'; 2 | -------------------------------------------------------------------------------- /app/views/multi-select-option.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/views/multi-select-option'; 2 | -------------------------------------------------------------------------------- /app/views/select-option.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/views/select-option'; 2 | -------------------------------------------------------------------------------- /app/views/select-tooltip-option.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-widgets/views/select-tooltip-option'; 2 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-widgets", 3 | "dependencies": { 4 | "ember": "~1.13.0", 5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3", 6 | "ember-resolver": "~0.1.15", 7 | "handlebars": "1.3.0", 8 | "jquery": "^1.11.1", 9 | "loader.js": "ember-cli/loader.js#3.2.0", 10 | "qunit": "~1.17.1", 11 | "bootstrap": "~3.2.0", 12 | "lodash": "~4.17.10", 13 | "jquery-ui": "~1.10.1" 14 | }, 15 | "devDependencies": { 16 | "jquery-browser": "jquery.browser#~0.0.4", 17 | "google-code-prettify": "~1.0.4", 18 | "holder": "holderjs#1.9", 19 | "sinonjs": "~1.14.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | useYarn: true, 4 | scenarios: [ 5 | { 6 | name: 'ember-1.13', 7 | bower: { 8 | dependencies: { 9 | 'ember': '1.13.13' 10 | } 11 | } 12 | }, 13 | { 14 | name: 'ember-2.4', 15 | bower: { 16 | dependencies: { 17 | 'ember': '2.4.6' 18 | } 19 | } 20 | }, 21 | /* 22 | { 23 | name: 'ember-2.0', 24 | bower: { 25 | dependencies: { 26 | 'ember': '2.0.0' 27 | } 28 | }, 29 | npm: { 30 | devDependencies: { 31 | 'ember-source': null 32 | } 33 | } 34 | }, 35 | { 36 | name: 'ember-lts-2.8', 37 | bower: { 38 | dependencies: { 39 | 'ember': 'components/ember#lts-2-8' 40 | }, 41 | resolutions: { 42 | 'ember': 'lts-2-8' 43 | } 44 | }, 45 | npm: { 46 | devDependencies: { 47 | 'ember-source': null 48 | } 49 | } 50 | }, 51 | { 52 | name: 'ember-lts-2.12', 53 | npm: { 54 | devDependencies: { 55 | 'ember-source': '~2.12.0' 56 | } 57 | } 58 | }, 59 | */ 60 | { 61 | name: 'ember-default', 62 | npm: { 63 | devDependencies: {} 64 | } 65 | } 66 | ] 67 | }; 68 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | module.exports = function(/* environment, appConfig */) { 5 | return { 6 | EmberENV: { 7 | _ENABLE_LEGACY_VIEW_SUPPORT: true 8 | } 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | 'use strict'; 3 | 4 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 5 | 6 | module.exports = function (defaults) { 7 | let app = new EmberAddon(defaults, { 8 | // Add options here 9 | }); 10 | 11 | app.import(app.bowerDirectory + '/ember/ember-template-compiler.js'); 12 | app.import(app.bowerDirectory + '/google-code-prettify/bin/prettify.min.css'); 13 | app.import(app.bowerDirectory + '/google-code-prettify/bin/prettify.min.js'); 14 | app.import(app.bowerDirectory + '/jquery-ui/ui/jquery-ui.custom.js'); 15 | app.import(app.bowerDirectory + '/bootstrap/dist/js/bootstrap.js'); 16 | app.import(app.bowerDirectory + '/bootstrap/dist/css/bootstrap.min.css'); 17 | 18 | app.import(app.bowerDirectory + '/sinonjs/sinon.js', { type: 'test' }); 19 | 20 | /* 21 | This build file specifies the options for the dummy test app of this 22 | addon, located in `/tests/dummy` 23 | This build file does *not* influence how the addon or the app using it 24 | behave. You most likely want to be modifying `./index.js` or app's build file 25 | */ 26 | 27 | return app.toTree(); 28 | }; 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | name: 'ember-widgets', 6 | options: { 7 | babel: { 8 | plugins: ['transform-object-rest-spread'] 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-widgets", 3 | "version": "0.5.0", 4 | "description": "The default blueprint for ember-cli addons.", 5 | "directories": { 6 | "doc": "doc", 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "build": "ember build", 11 | "start": "ember server", 12 | "test": "ember try:each" 13 | }, 14 | "repository": "", 15 | "engines": { 16 | "node": ">= 10.*" 17 | }, 18 | "author": "", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "broccoli-asset-rev": "^2.4.5", 22 | "ember-cli": "~2.16.2", 23 | "ember-cli-dependency-checker": "^2.0.0", 24 | "ember-cli-deprecation-workflow": "^1.0.1", 25 | "ember-cli-htmlbars": "~2.0.3", 26 | "ember-cli-htmlbars-inline-precompile": "~1.0.2", 27 | "ember-cli-inject-live-reload": "^1.4.1", 28 | "ember-cli-qunit": "^4.0.0", 29 | "ember-cli-uglify": "^2.0.0", 30 | "ember-disable-prototype-extensions": "^1.1.2", 31 | "ember-disable-proxy-controllers": "^1.0.0", 32 | "ember-load-initializers": "^1.0.0", 33 | "ember-resolver": "^4.0.0", 34 | "loader.js": "^4.2.3" 35 | }, 36 | "keywords": [ 37 | "ember-addon" 38 | ], 39 | "dependencies": { 40 | "@html-next/vertical-collection": "1.0.0-beta.14", 41 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 42 | "ember-auto-import": "^1.5.2", 43 | "ember-cli-babel": "^6.6.0", 44 | "ember-getowner-polyfill": "^3.0.2", 45 | "ember-wormhole": "0.5.4", 46 | "lodash-es": "^4.17.15" 47 | }, 48 | "ember-addon": { 49 | "configPath": "tests/dummy/config" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/img/invalid_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/img/no_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/img/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/public/img/select2.png -------------------------------------------------------------------------------- /public/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/public/img/spinner.gif -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | test_page: 'tests/index.html?hidepassed', 4 | disable_watching: true, 5 | launch_in_ci: [ 6 | 'Chrome' 7 | ], 8 | launch_in_dev: [ 9 | 'Chrome' 10 | ], 11 | browser_args: { 12 | Chrome: { 13 | mode: 'ci', 14 | args: [ 15 | '--disable-gpu', 16 | '--headless', 17 | '--remote-debugging-port=9222', 18 | '--window-size=1440,900' 19 | ] 20 | }, 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | embertest: true 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "find", 12 | "visit", 13 | "exists", 14 | "fillIn", 15 | "click", 16 | "keyEvent", 17 | "triggerEvent", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName", 25 | "expect", 26 | "equal", 27 | "deepEqual", 28 | "ok", 29 | "sinon" 30 | ], 31 | "node": false, 32 | "browser": false, 33 | "boss": true, 34 | "curly": true, 35 | "debug": false, 36 | "devel": false, 37 | "eqeqeq": true, 38 | "evil": true, 39 | "forin": false, 40 | "immed": false, 41 | "laxbreak": false, 42 | "newcap": true, 43 | "noarg": true, 44 | "noempty": false, 45 | "nonew": false, 46 | "nomen": false, 47 | "onevar": false, 48 | "plusplus": false, 49 | "regexp": false, 50 | "undef": true, 51 | "sub": true, 52 | "strict": false, 53 | "white": false, 54 | "eqnull": true, 55 | "esnext": true, 56 | "unused": true 57 | } 58 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | var App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver: Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/ember-widgets/popover.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import CustomPopoverContentView from '../../views/custom-popover-content'; 3 | 4 | export default Ember.Controller.extend({ 5 | customPopoverContentView: CustomPopoverContentView 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | 19 | 20 | {{content-for "body"}} 21 | 22 | 23 | 24 | 25 | {{content-for "body-footer"}} 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | var Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | this.route('license'); 10 | this.route('emberWidgets', { 11 | path: '/ember-widgets' 12 | }, function() { 13 | this.route('overview'); 14 | this.route('documentation'); 15 | this.route('carousel'); 16 | this.route('modal'); 17 | this.route('popover'); 18 | this.route('select'); 19 | this.route('colorPicker'); 20 | this.route('radioButton'); 21 | }); 22 | }); 23 | 24 | export default Router; 25 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/routes/ember-widgets/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | beforeModel: function() { 5 | return this.transitionTo('emberWidgets.overview'); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/ember-widgets/modal.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import CustomModalContentView from 'dummy/views/custom-modal-content'; 3 | import ModalComponent from 'ember-widgets/components/modal-component'; 4 | 5 | export default Ember.Route.extend({ 6 | actions: { 7 | showModal() { 8 | ModalComponent.popup({ 9 | targetObject: this, 10 | confirm: "modalConfirm", 11 | cancel: "modalCancel", 12 | content: "Isn't this one fine day?" 13 | }); 14 | }, 15 | showSmallModal() { 16 | ModalComponent.popup({ 17 | targetObject: this, 18 | confirm: "modalConfirm", 19 | cancel: "modalCancel", 20 | size: 'small', 21 | content: "This is quite small isn't it? You can also use 'large'." 22 | }); 23 | }, 24 | showModalWithCustomContent() { 25 | ModalComponent.popup({ 26 | targetObject: this, 27 | confirm: "modalConfirm", 28 | cancel: "modalCancel", 29 | content: { 30 | name: "Louis" 31 | }, 32 | contentViewClass: CustomModalContentView 33 | }); 34 | }, 35 | modalConfirm() { 36 | console.log("Modal Confirm!"); 37 | }, 38 | modalCancel() { 39 | console.log("Modal Cancel!"); 40 | } 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/ember-widgets/overview.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | activate: function() { 5 | var controller; 6 | controller = this.controllerFor('emberWidgets'); 7 | controller.set('showLargeHero', true); 8 | }, 9 | deactivate: function() { 10 | var controller; 11 | controller = this.controllerFor('emberWidgets'); 12 | controller.set('showLargeHero', false); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/ember-widgets/popover.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | model: function() { 5 | return { 6 | name: "Louis" 7 | }; 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/ember-widgets/select.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import countryList from '../../data/countries'; 3 | 4 | export default Ember.Route.extend({ 5 | model: function() { 6 | return countryList; 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/index.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | beforeModel: function() { 5 | return this.transitionTo('emberWidgets.overview'); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/overview.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | setupController(controller, model){ 5 | this._super(controller, model); 6 | controller.set('showLargeHero', false); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/select-box.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.Route.extend({ 4 | redirect: function() { 5 | this.transitionTo('overview'); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/_footer.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Footer --}} 2 | 56 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/_navigation.hbs: -------------------------------------------------------------------------------- 1 | {{!-- addepar-navbar --}} 2 | 23 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Main navigation --}} 2 | {{partial "navigation"}} 3 | 4 | {{outlet}} 5 | 6 | {{partial "footer"}} 7 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/custom-modal-content.hbs: -------------------------------------------------------------------------------- 1 |

Isn't this one fine day {{content.name}}?

2 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/custom-popover-content.hbs: -------------------------------------------------------------------------------- 1 | Isn't this one fine day {{content.name}}? 2 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/-sub-navigation.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Sub-Navigation --}} 2 | 30 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/-test-popover-content.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/carousel.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Carousel Ember.Widgets.Carousel

5 |

The slideshow below shows a generic plugin and component for cycling through elements like a carousel.

6 | 7 |
8 | {{#carousel-component}} 9 | {{#view 'carousel-item' class="active"}} 10 | 11 | {{/view}} 12 | {{#view 'carousel-item'}} 13 | 14 | {{/view}} 15 | {{#view 'carousel-item'}} 16 | 17 | {{/view}} 18 | {{/carousel-component}} 19 |
20 |
21 | 22 |
23 |

Application.hbs

24 |
25 |
{{#carousel-group}}
{{#view "carousel-item" class="active"}}
<img src="/gh_pages/img/bootstrap-mdo-sfmoma-01.jpg" alt="">
{{/view}}
{{#view "carousel-item"}}
<img src="/gh_pages/img/bootstrap-mdo-sfmoma-02.jpg" alt="">
{{/view}}
{{#view "carousel-item"}}
<img src="/gh_pages/img/bootstrap-mdo-sfmoma-03.jpg" alt="">
{{/view}}
{{/carousel-group}}
26 |
27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/color-picker.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Color Picker Ember Widgets ColorPicker

5 |

A simple component to select color from a given palette and the option to define a custom HEX.

6 |
7 | 8 |
9 | {{color-picker class="btn btn-default"}} 10 | 11 |
12 | 13 |
14 | {{color-picker class="btn btn-default" dropdownClass="pull-right"}} 15 | 16 |
17 | 18 |
19 |
20 |
{{color-picker}}
21 | 
22 |
23 |
24 | 25 |
26 | {{color-picker class="btn btn-default btn-expand"}} 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/modal.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Modal Ember.Widgets.Modal

5 |

The modal is an absolutely positioned dialog 6 | component which overlays the page. Typically, modals are pre-rendered 7 | into the page, even before they are needed. Our approach appends the 8 | modal when it is required and removes it once it is no longer visible. 9 | We designed it such that actions are sent to the application the same way components 11 | send them. One caveat is that we need to pass in the current 12 | controller as the target object to receive actions and to inherit the 13 | container.

14 |
15 |
16 |

Modal with content property

17 |
18 | 21 |
22 | 23 |

Application_controller.coffee

24 |
25 |
App.ApplicationController = Ember.Controller.extend
 26 |   actions:
 27 |     showModal: ->
 28 |       ModalComponent.popup
 29 |         targetObject: this
 30 |         confirm: "modalConfirm"
 31 |         cancel: "modalCancel"
 32 |         content: "Isn't this one fine day?"
 33 | 
 34 |     modalConfirm: -> console.log("Modal Confirm!")
 35 | 
 36 |     modalCancel: -> console.log("Modal Cancel!")
 37 | 
38 |
39 |
40 | 41 |
42 |

Modal with contentViewClass

43 |
44 | 47 |
48 | 49 |

Application_controller.coffee

50 |
51 |
App.ApplicationController = Ember.Controller.extend
 52 |   actions:
 53 |     showModal: ->
 54 |       ModalComponent.popup
 55 |         targetObject: this
 56 |         confirm: "modalConfirm"
 57 |         cancel: "modalCancel"
 58 |         content: { name: "Louis" }
 59 |         contentViewClass: App.CustomModalContentView
 60 | 
 61 |     modalConfirm: -> console.log("Modal Confirm!")
 62 | 
 63 |     modalCancel: -> console.log("Modal Cancel!")
 64 | 
65 |
66 | 67 |

Custom_modal_content_view.coffee

68 |
69 |
App.CustomModalContentView = Ember.View.extend
 70 |   templateName: 'custom-modal-content'
 71 | 
72 |
73 | 74 |

Custom-modal-content.hbs

75 |
76 |
Isn't this one fine day {{content.name}}?
77 |
78 |
79 |
80 |

Modal with custom size

81 |
82 | 85 |
86 | 87 |

Application_controller.coffee

88 |
89 |
App.ApplicationController = Ember.Controller.extend
 90 |   actions:
 91 |     showModal: ->
 92 |       ModalComponent.popup
 93 |         targetObject: this
 94 |         confirm: "modalConfirm"
 95 |         cancel: "modalCancel"
 96 |         size: 'small'
 97 |         content: "This is quite small isn't it?"
 98 | 
 99 |     modalConfirm: -> console.log("Modal Confirm!")
100 | 
101 |     modalCancel: -> console.log("Modal Cancel!")
102 | 
103 |
104 |
105 | 106 |
107 |
108 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/overview.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Content Column --}} 2 |
3 |
4 |
5 |

Ember Widgets

6 | This library is no longer actively maintained. 7 |

A component library built with the Ember.js 9 | framework. Includes easy to extend components such as select, 10 | popover, modal, carousel, and accordion. This library is built on 11 | bootstrap 3.0 CSS and represents our ideas about component best 12 | practices.

13 |
14 |
15 | 16 |
17 |
18 |

Features

19 |
    20 |
  • {{#link-to 'emberWidgets.carousel'}}Carousel{{/link-to}}
  • 21 |
  • {{#link-to 'emberWidgets.modal'}}Modal{{/link-to}}
  • 22 |
  • {{#link-to 'emberWidgets.popover'}}Popover{{/link-to}}
  • 23 |
  • {{#link-to 'emberWidgets.select'}}Select & Multi-Select{{/link-to}}
  • 24 |
  • {{#link-to 'emberWidgets.colorPicker'}}Color Picker{{/link-to}}
  • 25 |
  • {{#link-to 'emberWidgets.radioButton'}}Radio Button{{/link-to}}
  • 26 |
27 |
28 |
29 |

Dependencies

30 | 37 |
38 |
39 | 40 |
41 |
42 |
43 |

Widgets

44 |

The examples below demonstrate how you can extend and customize the widgets.

45 |
46 | {{#link-to 'emberWidgets.carousel'}} 47 |
48 |

Carousel

49 | 50 |
51 | {{/link-to}} 52 | {{#link-to 'emberWidgets.modal'}} 53 |
54 |

Modal

55 | 56 |
57 | {{/link-to}} 58 | {{#link-to 'emberWidgets.popover'}} 59 |
60 |

Popover

61 | 62 |
63 | {{/link-to}} 64 | {{#link-to 'emberWidgets.select'}} 65 |
66 |

Select & Multi-Select

67 | 68 |
69 | {{/link-to}} 70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 |

Getting Started

78 |

You will need node installed as a development dependency.

79 |

Clone it from Github or download the ZIP repo

80 |
81 |
$ npm install -g grunt-cli
 82 | $ npm install
 83 | $ grunt
 84 | $ node examples.js
85 |

Go to your browser and navigate to localhost:8000/gh_pages

86 |
87 |
88 |
89 |
90 |

Contributing

91 |

You can contribute to this project in one of two ways:

92 |
    93 |
  • Browse the ember-widgets issues and report bugs
  • 94 |
  • Clone the ember-widgets repo, make some changes according to our development guidelines and issue a pull-request with your changes.
  • 95 |
96 |

We keep the ember-widgets.js code to the minimum necessary, giving users as much control as possible.

97 |
98 |
99 | 100 |
101 |
102 |
103 |

Changelog

104 |

The current version is 0.2.0.

105 |

For the full list of changes, please see CHANGELOG.md.

106 |
107 |
108 |
109 |

Acknowledgements

110 |

List of Contributors on Github

111 |

With lots of help from the Ember.js team

112 |

wycats, tomdale, ebryn

113 |
114 |
115 |
116 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/popover.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Popover Ember.Widgets.Popover

5 |

Add small overlays of content to any element 6 | for displaying additional information. We created the popover link as a 7 | component which can either hold a content string specified in the handlebar 8 | helper or point to a custom view class for more sophisticated look and 9 | feel.

10 |
11 | 12 |
13 |

Simple Popover

14 |
15 | {{#popover-link-component tagName="button" class="btn btn-primary" 16 | title="Hello" content="Hello World!"}} 17 | Show Popover Top 18 | {{/popover-link-component}} 19 |
20 | 21 |

Application.hbs

22 |
23 |
{{#popover-link-component tagName="button"
 24 |   class="btn btn-primary" title="Hello"
 25 |   content="Hello World"}}
 26 |   Show Popover Button-Left
 27 | {{/popover-link-component}}
28 |
29 | 30 |
31 | 32 |

Popover with contentViewClass

33 |
34 | {{#popover-link-component tagName="button" class="btn btn-primary" 35 | placement="top-right" 36 | content="model" 37 | title="Derp" 38 | contentViewClass=customPopoverContentView}} 39 | Show Popover Top-Right 40 | {{/popover-link-component}} 41 |
42 | 43 |
44 | 45 |

Popover with contentViewClass

46 |
47 | {{#popover-link-component tagName="button" class="btn btn-primary" 48 | placement="right" 49 | content=model 50 | title="Derp" 51 | contentViewClass=customPopoverContentView}} 52 | Show Popover Right 53 | {{/popover-link-component}} 54 |
55 | 56 |
57 | 58 | LOOK HERE 59 | {{randomVariableName}} 60 | 61 |

Popover with contentViewClass

62 |
63 | {{#popover-link-component tagName="button" class="btn btn-primary" 64 | placement="bottom-right" 65 | content=model 66 | title="Derp" 67 | contentViewClass=customPopoverContentView}} 68 | Show Popover Bottom-Right 69 | {{/popover-link-component}} 70 |
71 | 72 |
73 | 74 |

Popover with contentViewClass

75 |
76 | {{#popover-link-component tagName="button" class="btn btn-primary" 77 | placement="bottom" 78 | content=model 79 | title="Derp" 80 | contentViewClass=customPopoverContentView}} 81 | Show Popover Bottom 82 | {{/popover-link-component}} 83 |
84 | 85 |
86 | 87 |

Popover with contentViewClass

88 |
89 | {{#popover-link-component tagName="button" class="btn btn-primary" 90 | placement="bottom-left" 91 | content=model 92 | title="Derp" 93 | contentViewClass=customPopoverContentView}} 94 | Show Popover Bottom-Left 95 | {{/popover-link-component}} 96 |
97 | 98 |
99 | 100 |

Popover with contentViewClass

101 |
102 | {{#popover-link-component tagName="button" class="btn btn-primary" 103 | placement="left" 104 | content=model 105 | title="Derp" 106 | contentViewClass=customPopoverContentView}} 107 | Show Popover Left 108 | {{/popover-link-component}} 109 |
110 |
111 | 112 |
113 |

Popover with contentViewClass

114 |
115 | {{#popover-link-component tagName="button" class="btn btn-primary" 116 | placement="top-left" 117 | content=model 118 | title="Derp" 119 | contentViewClass=customPopoverContentView}} 120 | Show Popover Top-Left 121 | {{/popover-link-component}} 122 |
123 | 124 |

Application.hbs

125 |
126 |
{{#popover-link-component tagName="button"
127 |   class="btn btn-primary" placement="bottom-left"
128 |   contentViewClass="App.CustomPopoverContentView"}}
129 |   Show Popover Button-Left
130 | {{/popover-link-component}}
131 |
132 | 133 |

Custom-popover-content.hbs

134 |
135 |
Isn't this one fine day {{content.name}}?
136 |
137 | 138 |

Custom_popover_content_view.coffee

139 |
140 |
App.CustomPopoverContentView = Ember.View.extend
141 |   templateName: 'custom-popover-content'
142 |
143 |
144 | 145 |
146 |
147 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/radio-button.hbs: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Radio Button Ember.Widgets.RadioButton

5 |

Allows selection of one choice from multiple possibilities.

6 |
7 | {{#radio-button-group}} 8 |
9 | {{#radio-button-wrapper value=true}} 10 | True 11 | {{/radio-button-wrapper}} 12 |
13 |
14 | {{#radio-button-wrapper value=false}} 15 | False 16 | {{/radio-button-wrapper}} 17 |
18 | {{/radio-button-group}} 19 |
20 |
21 |
22 |

Application.hbs

23 |
24 |
{{#radio-button-group}}
25 |   <div class="radio-row">
26 |     {{#radio-button-wrapper value=true}}
27 |       <span class="form-label radio-label">True</span>
28 |     {{/radio-button-wrapper}}
29 |   </div>
30 |   <div class="radio-row">
31 |     {{#radio-button-wrapper value=false}}
32 |       <span class="form-label radio-label">False</span>
33 |     {{/radio-button-wrapper}}
34 |   </div>
35 | {{/radio-button-groupt}}
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/select.hbs: -------------------------------------------------------------------------------- 1 |
2 |

Select Ember.Widgets.Select

3 |

Ember select is a Ember.js based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results. This component's design was inspired by the excellent Chosen jquery plugin and Select2. Uses the vertical-collection for lazily rendering large arrays of content.

4 | 5 |
6 |
7 |

Select with Selection Binding

8 |
9 | {{select-component 10 | content=model 11 | prompt="Select a Country" 12 | optionLabelPath="name" 13 | optionValuePath="name" 14 | selection=selection 15 | placeholder="Search" 16 | }} 17 |
18 | 19 |

Application.hbs

20 |
21 |
{{select-component
 22 |   content=model
 23 |   prompt="Select a Country"
 24 |   optionLabelPath="name"
 25 |   optionValuePath="name"
 26 |   selection=selection
 27 |   placeholder="Search"
 28 | }}
29 |
30 |
31 | 32 |
33 |

Multi-Select

34 |
35 | {{multi-select-component 36 | content=model 37 | optionLabelPath="name" 38 | optionValuePath="code" 39 | classNames="multi-select-example" 40 | placeholder="Select a country..." 41 | }} 42 |
43 | 44 |

Application.hbs

45 |
46 |
{{multi-select-component
 47 |   content=model
 48 |   optionLabelPath="name"
 49 |   optionValuePath="code"
 50 |   classNames="multi-select-example"
 51 |   placeholder="Select a country..."
 52 | }}
53 |
54 |
55 |
56 |
57 |
58 |

Select with Grouped Options

59 |
60 | {{select-component 61 | content=model 62 | prompt="Select a Country" 63 | optionLabelPath="name" 64 | optionValuePath="code" 65 | optionGroupPath="continent" 66 | selection=selection 67 | placeholder="Search" 68 | }} 69 |
70 | 71 |

Application.hbs

72 |
73 |
{{select-component
 74 |   content=model
 75 |   prompt="Select a Country"
 76 |   optionLabelPath="name"
 77 |   optionValuePath="code"
 78 |   optionGroupPath="continent"
 79 |   selection=selection
 80 |   placeholder="Search"
 81 | }}
82 |
83 |
84 |
85 |

Multi-Select Small

86 |
87 | {{multi-select-component 88 | content=model 89 | optionLabelPath="name" 90 | optionValuePath="code" 91 | classNames="multi-select-example" 92 | choicesFieldClass="input-xs" 93 | }} 94 |
95 | 96 |

Application.hbs

97 |
98 |
{{multi-select-component
 99 |   content=model
100 |   optionLabelPath="name"
101 |   optionValuePath="code"
102 |   classNames="multi-select-example"
103 |   choicesFieldClass="input-xs"
104 | }}
105 |
106 |
107 |
108 |
109 |
110 |

Select with Collapsible Group Headers

111 |
112 | {{select-component 113 | content=model 114 | prompt="Select a Country" 115 | optionLabelPath="name" 116 | optionValuePath="code" 117 | optionGroupPath="continent" 118 | isGroupHeaderCollapsible=true 119 | selection=selection 120 | placeholder="Search" 121 | }} 122 |
123 | 124 |

Application.hbs

125 |
126 |
{{select-component
127 |   content=model
128 |   prompt="Select a Country"
129 |   optionLabelPath="name"
130 |   optionValuePath="code"
131 |   optionGroupPath="continent"
132 |   isGroupHeaderCollapsible=true
133 |   selection=selection
134 |   placeholder="Search"
135 | }}
136 |
137 |
138 |
139 |

Multi-Select with Collapsible Group Headers

140 |
141 | {{multi-select-component 142 | content=model 143 | optionLabelPath="name" 144 | optionValuePath="code" 145 | optionGroupPath="continent" 146 | isGroupHeaderCollapsible=true 147 | classNames="multi-select-example" 148 | placeholder="Select a country..." 149 | }} 150 |
151 | 152 |

Application.hbs

153 |
154 |
{{multi-select-component
155 |   content=model
156 |   optionLabelPath="name"
157 |   optionValuePath="code"
158 |   optionGroupPath="continent"
159 |   isGroupHeaderCollapsible=true
160 |   classNames="multi-select-example"
161 |   placeholder="Select a country..."
162 | }}
163 |
164 |
165 |
166 |
167 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember-widgets/typahead.hbs: -------------------------------------------------------------------------------- 1 |
2 |

Typeahead Ember.Widgets.Typeahead

3 |

TBD

4 | 5 |
6 |
7 |

Soon...

8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/ember_widgets.hbs: -------------------------------------------------------------------------------- 1 | {{#if showLargeHero}} 2 | {{!-- Hero --}} 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |

Ember Widgets

11 |

12 | A powerful and easy to use component library for Ember.js. 13 |

14 | 17 | Download Ember-Widgets 18 | 19 |

20 |
21 |
22 |
23 |
24 | {{else}} 25 | {{!-- Small Hero --}} 26 |
27 |
28 |
29 |
30 |
31 | {{/if}} 32 | 33 | {{!-- Main Container --}} 34 |
35 |
36 | {{partial "ember-widgets/sub_navigation"}} 37 | {{outlet}} 38 |
39 |
40 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/license.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Main navigation --}} 2 | {{partial "navigation"}} 3 | 4 | {{!-- License Hero --}} 5 |
6 |
7 |
8 |
9 |
10 | 11 | {{!-- Legal --}} 12 |
13 |
14 |
15 |
16 |

Code & Documentation Licensing

17 |
18 |
19 |

The majority of open source software exclusively developed by Addepar is licensed under the liberal terms of the Apache License, Version 2.0. The documentation is generally available under the Creative Commons Attribution 3.0 Unported License. In the end, you are free to use, modify and distribute any documentation, source code or examples within our open source projects as long as you adhere to the licensing conditions present within the projects.

20 |

Also note that our engineers like to hack on their own open source projects in their free time. For code provided by our engineers outside of our official repositories on GitHub, Addepar does not grant any type of license, whether express or implied, to such code.

21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/test-modal-content.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/test-modal-footer.hbs: -------------------------------------------------------------------------------- 1 |
2 | {{#if footerText}} 3 | {{! This is present to test an edge case when using the old popover API 4 | that causes an issue where the target of actions in this template are incorrectly 5 | retargeted to the enclosing ember-wormhole component when this template is re-rendered (e.g., 6 | by changing a property that causes this footerText to rerender). 7 | }} 8 | 9 | Footer text: {{footerText}}. 10 | 11 | {{/if}} 12 | {{#if fooBindingTest}} 13 | {{! This is here to test the case where a modal is opened 14 | with properties named xxxBinding, e.g. 15 | (old api) ModalComponent.popup({fooBinding: 'bar', bar: 'baz'}) 16 | (new api) popoverService.openModal('component-name', {fooBinding: 'bar', 'bar: 'baz'}) 17 | }} 18 | {{foo}} 19 | {{bar}} 20 | {{input value=foo}} 21 | {{input value=bar}} 22 | {{/if}} 23 | 25 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/test-modal-header.hbs: -------------------------------------------------------------------------------- 1 |
2 | 3 |
-------------------------------------------------------------------------------- /tests/dummy/app/views/custom-modal-content.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default Ember.View.extend({ 4 | templateName: 'custom-modal-content' 5 | }); 6 | -------------------------------------------------------------------------------- /tests/dummy/app/views/custom-popover-content.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import layout from '../templates/custom-popover-content'; 3 | 4 | export default Ember.View.extend({ 5 | layout: layout 6 | }); 7 | -------------------------------------------------------------------------------- /tests/dummy/config/deprecation-workflow.js: -------------------------------------------------------------------------------- 1 | self.deprecationWorkflow = self.deprecationWorkflow || {}; 2 | self.deprecationWorkflow.config = { 3 | throwOnUnhandled: true, 4 | workflow: [ 5 | { handler: "silence", matchId: "ember-legacy-views" }, 6 | { handler: "silence", matchId: "ember-views.dispatching-modify-property" }, 7 | { handler: "silence", matchMessage: "Global lookup of Ember from a Handlebars template is deprecated." }, 8 | { handler: "silence", matchMessage: "Using deprecated `template` property on a View." } 9 | ] 10 | }; 11 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | // jQuery 0.12 was released four days ago and ember doesn't know it exists 11 | FORCE_JQUERY: true, 12 | FEATURES: { 13 | // Here you can enable experimental features on an ember canary build 14 | // e.g. 'with-controller': true 15 | }, 16 | EXTEND_PROTOTYPES: { 17 | // Prevent Ember Data from overriding Date.parse. 18 | Date: false 19 | } 20 | }, 21 | 22 | APP: { 23 | // Here you can pass flags/options to your application instance 24 | // when it is created 25 | } 26 | }; 27 | 28 | if (environment === 'development') { 29 | // ENV.APP.LOG_RESOLVER = true; 30 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 31 | // ENV.APP.LOG_TRANSITIONS = true; 32 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 33 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 34 | } 35 | 36 | if (environment === 'test') { 37 | // Testem prefers this... 38 | ENV.locationType = 'none'; 39 | 40 | // keep test console output quieter 41 | ENV.APP.LOG_ACTIVE_GENERATION = false; 42 | ENV.APP.LOG_VIEW_LOOKUPS = false; 43 | 44 | ENV.APP.rootElement = '#ember-testing'; 45 | } 46 | 47 | if (environment === 'production') { 48 | // here you can enable a production-specific feature 49 | } 50 | 51 | return ENV; 52 | }; 53 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | browsers: [ 4 | 'last 2 Chrome versions', 5 | 'last 2 Firefox versions', 6 | 'last 2 Safari versions' 7 | ] 8 | }; 9 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/css/jquery-ui.css: -------------------------------------------------------------------------------- 1 | ../../../../bower_components/jquery-ui/themes/base/jquery-ui.css -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Black.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Black.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Bold.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-ExtraLight.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-ExtraLight.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Light.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Light.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Medium.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Medium.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Regular.woff -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Semibold.ttf -------------------------------------------------------------------------------- /tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/fonts/sourceCodePro/SourceCodePro-Semibold.woff -------------------------------------------------------------------------------- /tests/dummy/public/img/invalid_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/dummy/public/img/no_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/dummy/public/img/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/img/select2.png -------------------------------------------------------------------------------- /tests/dummy/public/img/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/img/spinner.gif -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /tests/dummy/public/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/dummy/public/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /tests/helpers/assertions.js: -------------------------------------------------------------------------------- 1 | export function isPresent(selector, context) { 2 | var $element; 3 | $element = find(selector, context); 4 | return $element.length > 0; 5 | } 6 | 7 | export function isNotPresent(selector, context) { 8 | return !isPresent(selector, context); 9 | } 10 | 11 | export function isVisible(selector, context) { 12 | var $element; 13 | $element = find(selector, context); 14 | return $element !== false && $element.is(':visible'); 15 | } 16 | 17 | export function isFocused(selector, context) { 18 | var $element; 19 | $element = find(selector, context); 20 | return $element !== false && document.activeElement === $element[0]; 21 | } 22 | 23 | export function isHidden(selector, context) { 24 | return !isVisible(selector, context); 25 | } 26 | -------------------------------------------------------------------------------- /tests/helpers/color-picker.js: -------------------------------------------------------------------------------- 1 | export function openColorChooser(element) { 2 | if (element == null) { 3 | element = 'body'; 4 | } 5 | return andThen(function() { 6 | var dropdown; 7 | dropdown = find('.color-picker-button .dropdown', element); 8 | if (!dropdown.hasClass('open')) { 9 | return click('.color-picker-dropdown-button', element); 10 | } 11 | }); 12 | } 13 | 14 | export function getSelectedColor() { 15 | var active = find('.color-picker-dropdown .active'); 16 | if (active.length) { 17 | return active[0].attributes.style.value.match(/#[A-F0-9]+/)[0]; 18 | } else { 19 | return find('.color-picker-dropdown .input-sm').val(); 20 | } 21 | } 22 | 23 | export function selectColor(colorInHex) { 24 | openColorChooser(); 25 | var colorCell; 26 | 27 | andThen(function() { 28 | $(".color-picker-cell").each(function(index, cell) { 29 | if (cell.attributes.style.value.match(colorInHex)) { 30 | colorCell = cell; 31 | } 32 | }); 33 | 34 | return click(colorCell); 35 | }); 36 | } 37 | 38 | export function fillInCustomColor(value) { 39 | return fillIn('.color-picker-dropdown .input-sm', value); 40 | } 41 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import { run } from '@ember/runloop'; 2 | 3 | export default function destroyApp(application) { 4 | run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/helpers/keyboard.js: -------------------------------------------------------------------------------- 1 | export function pressEnter(element) { 2 | return keyEvent(element, 'keydown', 13); 3 | } 4 | 5 | export function pressSpacebar(element) { 6 | return keyEvent(element, 'keydown', 32); 7 | } 8 | 9 | export function pressEsc(element) { 10 | return keyEvent(element, 'keydown', 27); 11 | } 12 | 13 | export function pressUpArrow(element) { 14 | return keyEvent(element, 'keydown', 38); 15 | } 16 | 17 | export function pressDownArrow(element) { 18 | return keyEvent(element, 'keydown', 40); 19 | } 20 | 21 | export function pressBackspace(element) { 22 | return keyEvent(element, 'keydown', 8); 23 | } 24 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import { resolve } from 'rsvp'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | export default function(name, options = {}) { 7 | module(name, { 8 | beforeEach() { 9 | this.application = startApp(); 10 | 11 | if (options.beforeEach) { 12 | return options.beforeEach.apply(this, arguments); 13 | } 14 | }, 15 | 16 | afterEach() { 17 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 18 | return resolve(afterEach).then(() => destroyApp(this.application)); 19 | } 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /tests/helpers/mouse.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export function mouseDown(selector, context) { 4 | var $element; 5 | $element = find(selector, context); 6 | Ember.run(function() { 7 | return $element.mousedown(); 8 | }); 9 | return wait(); 10 | } 11 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | import config from '../../config/environment'; 3 | 4 | var resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/select.js: -------------------------------------------------------------------------------- 1 | import { isHidden } from './assertions'; 2 | import { mouseDown } from './mouse'; 3 | 4 | export function openDropdown(element) { 5 | return click('.dropdown-toggle', element); 6 | } 7 | 8 | export function findInChosen(element, itemText) { 9 | var promise, searchBox, shouldOpenChosen, toggleButton; 10 | toggleButton = find('.dropdown-toggle', element); 11 | searchBox = find('.ember-select-search input', element); 12 | shouldOpenChosen = isHidden(searchBox); 13 | if (shouldOpenChosen) { 14 | promise = click(toggleButton); 15 | } else { 16 | promise = wait(); 17 | } 18 | return promise.then(function() { 19 | return $('li', element).filter(function() { 20 | return $.text([this]).trim() === itemText; 21 | }); 22 | }); 23 | } 24 | 25 | export function findInMultiChosen(element, itemText) { 26 | var searchBox; 27 | searchBox = find('input', element); 28 | click(searchBox); 29 | return fillIn(searchBox, itemText).then(function() { 30 | return $('.js-dropdown-menu li', element).filter(function() { 31 | return $.text([this]).trim() === itemText; 32 | }); 33 | }); 34 | } 35 | 36 | export function selectInMultiChosen(element, itemText) { 37 | return findInMultiChosen(element, itemText).then(function(item) { 38 | return click(item); 39 | }); 40 | } 41 | 42 | export function selectFirstInMultiChosen(element) { 43 | var searchBox; 44 | searchBox = find('input', element); 45 | click(searchBox); 46 | return click($('.ember-select-result-item', element)[0]); 47 | } 48 | 49 | export function selectInChosen(app, element, itemText) { 50 | return findInChosen(element, itemText).then(function(item) { 51 | return click(item); 52 | }); 53 | } 54 | 55 | export function findInSelect(element, itemText) { 56 | var toggleButton; 57 | toggleButton = find('a', element); 58 | return mouseDown(toggleButton).then(function() { 59 | var item; 60 | item = $('li', element).filter(function() { 61 | return $.text([this]).trim() === itemText; 62 | }); 63 | return click(item); 64 | }); 65 | } 66 | 67 | /** 68 | * Returns the selector for an option in the select dropdown 69 | * 70 | * @public 71 | * @param {string} selection - The text displayed in an option in the select dropdown. 72 | * @return {string} the selector for an option in the select dropdown 73 | */ 74 | export function getOptionSelector(selection) { 75 | return `.ember-select-results .ember-select-result-item:contains(${selection})`; 76 | } 77 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | var application; 7 | 8 | var attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(function() { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/carousel-component-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import startApp from '../helpers/start-app'; 4 | 5 | var carousel, app; 6 | 7 | carousel = null; 8 | 9 | moduleForComponent('carousel-component', '[Integration] Carousel component', { 10 | needs: ['template:carousel'], 11 | setup: function() { 12 | app = startApp(); 13 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = true; 14 | }, 15 | teardown: function() { 16 | Ember.run(function() { 17 | return carousel.destroy(); 18 | }); 19 | Ember.run(app, 'destroy'); 20 | carousel = null; 21 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = false; 22 | } 23 | }); 24 | 25 | test('The action transitionEnded is sent after sliding', function(assert) { 26 | assert.expect(1); 27 | Ember.run((function(_this) { 28 | return function() { 29 | carousel = _this.factory().extend({ 30 | actions: { 31 | transitionEnded: function() { 32 | return assert.ok(true, 'The action is sent after sliding'); 33 | } 34 | } 35 | }).create(); 36 | return carousel.appendTo('#ember-testing'); 37 | }; 38 | })(this)); 39 | return Ember.run(function() { 40 | return carousel.slide('next', 1); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/integration/color-picker-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import startApp from '../helpers/start-app'; 4 | 5 | import { 6 | fillInCustomColor, 7 | openColorChooser, 8 | getSelectedColor, 9 | selectColor 10 | } from '../helpers/color-picker'; 11 | 12 | import { 13 | isNotPresent 14 | } from '../helpers/assertions'; 15 | 16 | var COLOR_PICKER, colorPicker, getColorPickerDropdown, getPreviewCellSelector, testHexConversion, app; 17 | 18 | COLOR_PICKER = { 19 | PREVIEW_CELL: '.color-picker-custom-preview', 20 | DROPDOWN: '.color-picker-dropdown' 21 | }; 22 | 23 | getPreviewCellSelector = function() { 24 | return COLOR_PICKER.PREVIEW_CELL; 25 | }; 26 | 27 | getColorPickerDropdown = function() { 28 | return COLOR_PICKER.DROPDOWN; 29 | }; 30 | 31 | moduleForComponent('color-picker', '[Integration] Color picker unit tests', { 32 | needs: ['template:color-picker', 'template:color-picker-cell', 'template:color-picker-button-partial', 'template:color-picker-dropdown', 'component:color-picker-dropdown', 'component:color-picker-cell'], 33 | 34 | setup: function() { 35 | app = startApp(); 36 | }, 37 | 38 | teardown: function() { 39 | Ember.run(function() { 40 | return colorPicker != null ? colorPicker.destroy() : void 0; 41 | }); 42 | Ember.run(app, 'destroy'); 43 | return colorPicker = null; 44 | } 45 | }); 46 | 47 | testHexConversion = function(assert, colorPicker, color, hex) { 48 | Ember.run(function() { 49 | colorPicker.set('selectedColor', color); 50 | }); 51 | return assert.equal(colorPicker.get('selectedColorRGB'), hex); 52 | }; 53 | 54 | test('Color picker converts color to hex when color is undefined', function(assert) { 55 | assert.expect(3); 56 | colorPicker = this.subject(); 57 | return [void 0, null, 0].forEach(function(color) { 58 | return testHexConversion(assert, colorPicker, color, color); 59 | }); 60 | }); 61 | 62 | test('Color picker converts color to hex when color is capitalized hex', function(assert) { 63 | colorPicker = this.subject(); 64 | return testHexConversion(assert, colorPicker, "#AAAAAA", "#aaaaaa"); 65 | }); 66 | 67 | test('Color picker converts color to hex when color is transparent', function(assert) { 68 | colorPicker = this.subject(); 69 | return testHexConversion(assert, colorPicker, "transparent", "transparent"); 70 | }); 71 | 72 | test('Color picker converts color to hex when color is rgb', function(assert) { 73 | colorPicker = this.subject(); 74 | return testHexConversion(assert, colorPicker, "rgb(0, 0, 0)", "#000000"); 75 | }); 76 | 77 | test('Color picker converts color to hex when color is rgba', function(assert) { 78 | colorPicker = this.subject(); 79 | return testHexConversion(assert, colorPicker, "rgb(3, 2, 1, 1)", "#030201"); 80 | }); 81 | 82 | test('Color picker converts color to hex when color is rgba and transparent', function(assert) { 83 | colorPicker = this.subject(); 84 | testHexConversion(assert, colorPicker, "rgb(3, 2, 1, 0)", "transparent"); 85 | return testHexConversion(assert, colorPicker, "rgb(0, 0, 0, 0)", "transparent"); 86 | }); 87 | 88 | test('Color picker converts color to hex when color is color name', function(assert) { 89 | colorPicker = this.subject(); 90 | return testHexConversion(assert, colorPicker, "aliceblue", "#f0f8ff"); 91 | }); 92 | 93 | test('Color picker converts color to hex when color is invalid', function(assert) { 94 | colorPicker = this.subject(); 95 | testHexConversion(assert, colorPicker, "foo", void 0); 96 | testHexConversion(assert, colorPicker, "rgb(a, b, c)", void 0); 97 | return testHexConversion(assert, colorPicker, "rgb(1,2,3)", void 0); 98 | }); 99 | 100 | test('Custom color is set as selected color', function(assert) { 101 | var customColor; 102 | colorPicker = this.subject(); 103 | customColor = '#addec0'; 104 | this.render(); 105 | openColorChooser(); 106 | fillInCustomColor(customColor); 107 | click(getPreviewCellSelector()); 108 | openColorChooser(); 109 | return andThen(function() { 110 | assert.equal(getSelectedColor(), customColor, 'Custom color in dropdown should remain after reopening'); 111 | assert.equal(colorPicker.get('selectedColor'), customColor, 'Custom color is set correctly when clicking on preview cell'); 112 | return assert.ok(colorPicker.get('isCustomColor'), 'Custom color cell is highlighted'); 113 | }); 114 | }); 115 | 116 | test('Test accepting custom color without hashtag', function(assert) { 117 | var customColor, formattedCustomColor; 118 | colorPicker = this.subject(); 119 | customColor = 'addec0'; 120 | formattedCustomColor = '#addec0'; 121 | this.render(); 122 | openColorChooser(); 123 | fillInCustomColor(customColor); 124 | click(getPreviewCellSelector()); 125 | openColorChooser(); 126 | return andThen(function() { 127 | return assert.equal(colorPicker.get('selectedColor'), formattedCustomColor, 'Custom color can be entered without hashtag'); 128 | }); 129 | }); 130 | 131 | test('Selecting a color should send an action', function(assert) { 132 | var color, customColor, spy; 133 | colorPicker = this.subject(); 134 | customColor = '#addec0'; 135 | color = '#01FF70'; 136 | spy = sinon.spy(colorPicker, 'sendAction'); 137 | this.render(); 138 | selectColor(color); 139 | andThen(function() { 140 | return assert.ok(spy.calledWithExactly('userSelected', color), 'Clicking color picker cell sends action'); 141 | }); 142 | openColorChooser(); 143 | fillInCustomColor(customColor); 144 | click(getPreviewCellSelector()); 145 | return andThen(function() { 146 | return assert.ok(spy.calledWithExactly('userSelected', customColor), 'Clicking custom color sends action'); 147 | }); 148 | }); 149 | 150 | test('Click outside of the component should close the dropdown', function(assert) { 151 | colorPicker = this.subject(); 152 | this.render(); 153 | openColorChooser(); 154 | return andThen(function() { 155 | $('body').trigger('click'); 156 | return assert.ok(isNotPresent(getColorPickerDropdown()), 'The dropdown should disappear when clicking outside'); 157 | }); 158 | }); 159 | 160 | test('Submitting custom color form updates color and does not reload page', function(assert) { 161 | var customColor; 162 | colorPicker = this.subject(); 163 | customColor = "#abc123"; 164 | this.render(); 165 | openColorChooser(); 166 | fillInCustomColor(customColor); 167 | return andThen(function() { 168 | $(".color-picker-custom-form").submit(); 169 | return assert.equal(colorPicker.get('selectedColor'), customColor, "Custom color gets set correctly and page doesn't refresh"); 170 | }); 171 | }); 172 | 173 | test('Transparent color is set in preview cell', function(assert) { 174 | var color; 175 | colorPicker = this.subject(); 176 | color = 'transparent'; 177 | this.render(); 178 | selectColor(color); 179 | return andThen(function() { 180 | return assert.ok(colorPicker.get('isColorTransparent'), 'Transparent color correctly identified in preview cell'); 181 | }); 182 | }); 183 | 184 | test('Correct cell is highlighted within color palette', function(assert) { 185 | var color; 186 | colorPicker = this.subject(); 187 | color = '#01FF70'; 188 | this.render(); 189 | selectColor(color); 190 | openColorChooser(); 191 | return andThen(function() { 192 | return assert.equal(getSelectedColor(), color, 'Correct color cell is highlighted'); 193 | }); 194 | }); 195 | -------------------------------------------------------------------------------- /tests/integration/components/render-popover-new-api-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | import runPopoverTests from '../shared/-popover-tests'; 3 | import startApp from '../../helpers/start-app'; 4 | 5 | function makeComponent(context, componentName, BaseClass, options={}) { 6 | context.register(`component:${componentName}`, BaseClass.extend(options)); 7 | let componentSpec = { componentName }; 8 | return componentSpec; 9 | } 10 | 11 | function openPopover(context, componentSpec, options={}, hideAll=true) { 12 | let { componentName } = componentSpec; 13 | return Ember.run(() => context.container.lookup('service:popover').openPopover(componentName, options, hideAll)); 14 | } 15 | 16 | function openModal(context, componentSpec, options={}) { 17 | let { componentName } = componentSpec; 18 | return Ember.run(() => context.container.lookup('service:popover').openModal(componentName, options)); 19 | } 20 | 21 | function openPopoverModal(context, componentName, options={}) { 22 | return Ember.run(() => { 23 | let popoverService = context.container.lookup('service:popover'); 24 | let { popoverSpec, close } = popoverService.buildPopoverSpec(componentName, document.body, options); 25 | popoverService.openPopoverSpec(popoverSpec); 26 | return close; 27 | }); 28 | } 29 | 30 | moduleForComponent( 31 | 'render-popover', 32 | 'Integration | Component | render popover (new API)', 33 | { 34 | integration: true, 35 | setup() { 36 | // Set up application to install app helpers like `fillIn` 37 | this.application = startApp(); 38 | }, 39 | teardown() { 40 | Ember.run(this.application, 'destroy'); 41 | } 42 | } 43 | ); 44 | 45 | runPopoverTests(test, {makeComponent, openPopover, openModal, openPopoverModal}); 46 | -------------------------------------------------------------------------------- /tests/integration/components/render-popover-old-api-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | 3 | import runPopoverTests from '../shared/-popover-tests'; 4 | import startApp from '../../helpers/start-app'; 5 | 6 | function makeComponent(context, componentName, BaseClass, options={}) { 7 | return { 8 | klass: BaseClass.extend(options) 9 | }; 10 | } 11 | 12 | function openPopover(context, componentSpec, options={}, hideAll=true) { 13 | let { klass } = componentSpec; 14 | options.container = context.container; 15 | return Ember.run(() => { 16 | return klass.popup(options, hideAll); 17 | }); 18 | } 19 | 20 | function openModal(context, componentSpec, options={}) { 21 | let { klass } = componentSpec; 22 | options.container = context.container; 23 | return Ember.run(() => { 24 | return klass.popup(options); 25 | }); 26 | } 27 | 28 | moduleForComponent( 29 | 'render-popover', 30 | 'Integration | Component | render popover (deprecated API)', 31 | { 32 | integration: true, 33 | setup() { 34 | // Set up application to install app helpers like `fillIn` 35 | this.application = startApp(); 36 | }, 37 | teardown() { 38 | Ember.run(this.application, 'destroy'); 39 | } 40 | } 41 | ); 42 | 43 | runPopoverTests(test, {makeComponent, openPopover, openModal, skipDeprecatedAPIFailingTests: true}); -------------------------------------------------------------------------------- /tests/integration/components/select-component-test.js: -------------------------------------------------------------------------------- 1 | import hbs from 'htmlbars-inline-precompile'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import { click, fillIn } from '@ember/test-helpers'; 4 | import Ember from 'ember'; 5 | 6 | class SelectPageObject { 7 | constructor(elementLookup) { 8 | this.$ = elementLookup; 9 | } 10 | 11 | get selectResultsElement() { 12 | return this.$('.ember-select-results')[0]; 13 | } 14 | 15 | get selectChoiceElement() { 16 | return this.$('.ember-select-choice')[0]; 17 | } 18 | 19 | get contentLoadingElement() { 20 | return this.$('.ember-select-loading')[0]; 21 | } 22 | 23 | get contentEmptyElement() { 24 | return this.$('.ember-select-empty-content')[0]; 25 | } 26 | 27 | get noResultsElement() { 28 | return this.$('.ember-select-no-results')[0]; 29 | } 30 | 31 | selectOptionElementContaining(content) { 32 | return this.$(`li:contains(${content}) div`)[0]; 33 | } 34 | 35 | get selectOptionElements() { 36 | return this.$('.ember-select-result-item').toArray(); 37 | } 38 | 39 | get selectOptionTextContents() { 40 | return this.selectOptionElements.map(e => e.textContent.trim()); 41 | } 42 | 43 | async inputSearchText(text) { 44 | await fillIn(this.$('.ember-select-search > input')[0], text); 45 | } 46 | } 47 | 48 | moduleForComponent('select-component', '[Integration] Select component', { 49 | integration: true, 50 | 51 | beforeEach() { 52 | this.helpers = new SelectPageObject((...args) => this.$(...args)); 53 | }, 54 | }); 55 | 56 | test('it renders collapsed, but opens to display options', async function(assert) { 57 | this.set('content', [ 58 | 'foo', 'bar', 'bar', 'baz' 59 | ]); 60 | 61 | await this.render(hbs`{{select-component content=content}}`); 62 | 63 | assert.ok( 64 | !this.helpers.selectResultsElement, 65 | 'dropdown is not present on initial render' 66 | ); 67 | 68 | await click(this.helpers.selectChoiceElement); 69 | 70 | assert.ok( 71 | this.helpers.selectResultsElement, 72 | 'dropdown is present after click' 73 | ); 74 | }); 75 | 76 | test('it renders collapsed, but opens to display grouped options', async function(assert) { 77 | this.set('content', [ 78 | {name: 'Sparrow', sound: 'Squawk'}, 79 | {name: 'Crow', sound: 'Squawk'}, 80 | {name: 'Dog', sound: 'bark'}, 81 | {name: 'Wolf', sound: 'Bark'}, 82 | {name: 'Sea Lion', sound: 'Bark'} 83 | ]); 84 | 85 | this.set('collapsedGroupHeaders', Ember.A([ 86 | 'Squawk', 'bark' 87 | ])); 88 | 89 | await this.render(hbs` 90 | {{select-component 91 | content=content 92 | optionLabelPath='name' 93 | optionValuePath='name' 94 | optionGroupPath='sound' 95 | isGroupHeaderCollapsible=true 96 | collapsedGroupHeaders=collapsedGroupHeaders 97 | }}`); 98 | 99 | await click(this.helpers.selectChoiceElement); 100 | 101 | assert.deepEqual( 102 | this.helpers.selectOptionTextContents, 103 | ['Bark', 'Sea Lion', 'Wolf', 'Squawk', 'bark'], 104 | 'rendered content includes groups' 105 | ); 106 | 107 | await click(this.helpers.selectOptionElementContaining('bark')); 108 | 109 | assert.deepEqual( 110 | this.helpers.selectOptionTextContents, 111 | ['Bark', 'Sea Lion', 'Wolf', 'Squawk', 'bark', 'Dog'], 112 | 'rendered content includes expanded bark group' 113 | ); 114 | 115 | await click(this.helpers.selectOptionElementContaining('Bark')); 116 | 117 | assert.deepEqual( 118 | this.helpers.selectOptionTextContents, 119 | ['Bark', 'Squawk', 'bark', 'Dog'], 120 | 'rendered content collapses Bark group' 121 | ); 122 | }); 123 | 124 | test('It displays the specified component when componentNameForGroupTooltip is provided', async function(assert) { 125 | this.register('template:components/some-component', hbs`{{groupItem.name}}`); 126 | 127 | this.set('content', [ 128 | {name: 'Sparrow', sound: 'Squawk'}, 129 | {name: 'Crow', sound: 'Squawk'}, 130 | {name: 'Dog', sound: 'bark'}, 131 | {name: 'Wolf', sound: 'Bark'}, 132 | {name: 'Sea Lion', sound: 'Bark'} 133 | ]); 134 | 135 | await this.render(hbs` 136 | {{select-component 137 | content=content 138 | optionLabelPath='name' 139 | optionValuePath='name' 140 | optionGroupPath='sound' 141 | componentNameForGroupTooltip='some-component' 142 | }}`); 143 | 144 | await click(this.helpers.selectChoiceElement); 145 | 146 | let tooltipElements = this.$('[data-test-some-component]'); 147 | assert.equal(tooltipElements.length, 3, 'the passed tooltip is rendered for each group header'); 148 | assert.deepEqual( 149 | tooltipElements.toArray().map(e => e.textContent), 150 | ['Bark', 'Squawk', 'bark'], 151 | 'the groupItem argument is passed' 152 | ); 153 | }); 154 | 155 | test( 156 | 'It does not display loading text and does show empty content component when content is empty and loaded', 157 | async function(assert) { 158 | this.register('view:some-view', Ember.View.extend({ templateName: 'some-view' })); 159 | this.register('template:some-view', hbs`No results`); 160 | 161 | this.set('content', []); 162 | 163 | await this.render(hbs` 164 | {{select-component 165 | content=content 166 | emptyContentView='some-view' 167 | }}`); 168 | 169 | await click(this.helpers.selectChoiceElement); 170 | 171 | assert.ok(!this.helpers.contentLoadingElement, 'Loading ui is not present'); 172 | assert.ok(!!this.helpers.contentEmptyElement, 'Empty content is present'); 173 | assert.ok( 174 | !!this.helpers.contentEmptyElement.querySelector('[data-test-some-component]'), 175 | 'Empty content view is present' 176 | ); 177 | assert.ok(!this.helpers.noResultsElement, 'No results message is not present'); 178 | } 179 | ); 180 | 181 | test( 182 | 'It does not display loading text and does show no results message when content is non-empty and loaded', 183 | async function(assert) { 184 | this.set('content', ['foo']); 185 | 186 | await this.render(hbs` 187 | {{select-component content=content}}`); 188 | 189 | await click(this.helpers.selectChoiceElement); 190 | await this.helpers.inputSearchText('no match'); 191 | 192 | assert.ok(!this.helpers.contentLoadingElement, 'Loading ui is not present'); 193 | assert.ok(!this.helpers.contentEmptyElement, 'Empty content is not present'); 194 | assert.ok(!!this.helpers.noResultsElement, 'No results message is present'); 195 | } 196 | ); 197 | 198 | test( 199 | 'It does display loading text and does not show empty content component or no results message when loading', 200 | async function(assert) { 201 | await this.render(hbs` 202 | {{select-component 203 | isLoading=true 204 | }}`); 205 | 206 | await click(this.helpers.selectChoiceElement); 207 | 208 | assert.ok(!!this.helpers.contentLoadingElement, 'Loading ui is present'); 209 | assert.ok(!this.helpers.contentEmptyElement, 'Empty content is not present'); 210 | assert.ok(!this.helpers.noResultsElement, 'No results message is not present'); 211 | } 212 | ); 213 | -------------------------------------------------------------------------------- /tests/integration/debounced-text-component-test.js: -------------------------------------------------------------------------------- 1 | import hbs from 'htmlbars-inline-precompile'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import wait from 'ember-test-helpers/wait'; 4 | 5 | moduleForComponent('debounced-text-component', '[Integration] Debounced Text Component', { 6 | integration: true, 7 | }); 8 | 9 | test('Test debounced text', function(assert) { 10 | assert.expect(1); 11 | 12 | this.on('valueChanged', (newText) => { 13 | assert.equal(newText, 'foo', 14 | 'valueChanged action is fired once when value changed'); 15 | }); 16 | 17 | this.render(hbs`{{debounced-text-component valueChanged='valueChanged'}}`); 18 | 19 | this.$('input').val('fo'); 20 | this.$('input').change(); 21 | this.$('input').val('foo'); 22 | this.$('input').change(); 23 | return wait(); 24 | }); 25 | -------------------------------------------------------------------------------- /tests/integration/modal-component-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import startApp from '../helpers/start-app'; 4 | 5 | import { isFocused } from '../helpers/assertions'; 6 | 7 | var modal, app; 8 | 9 | moduleForComponent('modal-component', "[Integration] Modal component", { 10 | needs: ['template:modal', 'template:modal-header', 'template:modal-footer', 'template:modal-content'], 11 | 12 | setup: function() { 13 | app = startApp(); 14 | }, 15 | 16 | teardown: function() { 17 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = false; 18 | Ember.run(function() { 19 | return modal != null ? modal.destroy() : void 0; 20 | }); 21 | Ember.run(app, 'destroy'); 22 | return modal = null; 23 | } 24 | }); 25 | 26 | test('With DISABLE_ANIMATIONS=true, hide can be called several times', function(assert) { 27 | assert.expect(1); 28 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = true; 29 | modal = this.subject(); 30 | this.render(); 31 | return andThen(function() { 32 | modal.hide(); 33 | modal.hide(); 34 | return assert.ok(true, 'hide can be called multiple times when animations are disabled'); 35 | }); 36 | }); 37 | 38 | test('Test tab loop only inside modal', function(assert) { 39 | var buttonCancel, buttonConfirm, modalComponent, validateFocus; 40 | assert.expect(3); 41 | modal = this.subject(); 42 | this.render(); 43 | modalComponent = modal.$(); 44 | modal.set('enforceModality', true); 45 | buttonConfirm = find('.btn-confirm', modalComponent); 46 | buttonCancel = find('.btn-cancel', modalComponent); 47 | validateFocus = function(element, messageFocus) { 48 | return assert.ok(isFocused(element, modalComponent), messageFocus); 49 | }; 50 | validateFocus(buttonConfirm, 'Button Confirm should be focused'); 51 | triggerEvent(buttonConfirm, null, 'keydown', { 52 | keyCode: 9, 53 | which: 9, 54 | shiftKey: true 55 | }); 56 | andThen(function() { 57 | return validateFocus(buttonCancel, 'Button Cancel should be focused'); 58 | }); 59 | keyEvent(buttonCancel, 'keydown', 9); 60 | return andThen(function() { 61 | return validateFocus(buttonConfirm, 'Button Confirm should be focused'); 62 | }); 63 | }); 64 | 65 | test('Test preserving the focus when clicking on non-focusable element', function(assert) { 66 | var buttonCancel, buttonConfirm, modalBody, modalComponent, validateFocus; 67 | assert.expect(1); 68 | modal = this.subject(); 69 | this.render(); 70 | modalComponent = modal.$(); 71 | buttonConfirm = find('.btn-confirm', modalComponent); 72 | buttonCancel = find('.btn-cancel', modalComponent); 73 | modalBody = find('.modal-body', modalComponent); 74 | validateFocus = function(element, messageFocus) { 75 | return assert.ok(isFocused(element, modalComponent), messageFocus); 76 | }; 77 | keyEvent(buttonConfirm, 'keydown', 9).then(function() { 78 | return buttonCancel.focus(); 79 | }); 80 | return click(modalBody).then(function() { 81 | return validateFocus(buttonCancel, 'The focus should stay on the Cancel button'); 82 | }); 83 | }); 84 | 85 | test('Test pressing Enter to confirm', function(assert) { 86 | var modalComponent, spy; 87 | assert.expect(1); 88 | modal = this.subject({ 89 | enterToConfirm: true 90 | }); 91 | spy = sinon.spy(modal, "send"); 92 | this.render(); 93 | modalComponent = modal.$(); 94 | keyEvent(modalComponent, 'keydown', 13); 95 | return andThen(function() { 96 | return assert.ok(spy.calledWith('sendConfirm'), "sendConfirm gets called when hitting enter"); 97 | }); 98 | }); 99 | 100 | test('Test confirm button has default class of btn-primary', function(assert) { 101 | var buttonConfirm, modalComponent; 102 | assert.expect(2); 103 | modal = this.subject(); 104 | this.render(); 105 | modalComponent = modal.$(); 106 | buttonConfirm = find('.btn-confirm', modalComponent); 107 | assert.ok(buttonConfirm.hasClass('btn-primary'), 'By default the confirm button has class of btn-primary'); 108 | assert.ok(!buttonConfirm.hasClass('btn-default'), 'By default the confirm button does not have class of btn-default'); 109 | }); 110 | 111 | test('Test confirm button can be set to not have default class of btn-primary', function(assert) { 112 | var buttonConfirm, modalComponent; 113 | assert.expect(2); 114 | modal = this.subject(); 115 | modal.set('isConfirmPrimaryBtn', false); 116 | this.render(); 117 | modalComponent = modal.$(); 118 | buttonConfirm = find('.btn-confirm', modalComponent); 119 | assert.ok(!buttonConfirm.hasClass('btn-primary'), 'When isConfirmPrimaryBtn is set to false the confirm button does not have class of btn-primary'); 120 | assert.ok(buttonConfirm.hasClass('btn-default'), 'When isConfirmPrimaryBtn is set to false the confirm button has class of btn-default'); 121 | }); 122 | -------------------------------------------------------------------------------- /tests/integration/popover-link-component-test.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import { moduleForComponent, test } from 'ember-qunit'; 3 | import startApp from '../helpers/start-app'; 4 | 5 | import { 6 | isPresent, 7 | isNotPresent 8 | } from '../helpers/assertions'; 9 | 10 | var animationsDisabled, popoverLink, app; 11 | 12 | animationsDisabled = false; 13 | 14 | moduleForComponent('popover-link-component', '[Integration] Popover link tests', { 15 | needs: ['template:popover-link-popover', 'template:view-parent-view-content'], 16 | setup: function() { 17 | app = startApp(); 18 | animationsDisabled = window.EMBER_WIDGETS_DISABLE_ANIMATIONS; 19 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = true; 20 | }, 21 | teardown: function() { 22 | window.EMBER_WIDGETS_DISABLE_ANIMATIONS = animationsDisabled; 23 | Ember.run(function() { 24 | if (popoverLink != null) { 25 | popoverLink.destroy(); 26 | } 27 | }); 28 | Ember.run(app, 'destroy'); 29 | } 30 | }); 31 | 32 | test('Destroying a popover link destroys the associated popover', function(assert) { 33 | assert.expect(2); 34 | popoverLink = this.subject({ 35 | rootElement: "#ember-testing" 36 | }); 37 | this.render(); 38 | click('.popover-link'); 39 | andThen(function() { 40 | var popover; 41 | assert.ok(isPresent('.popover'), "The popover is created"); 42 | popover = popoverLink.get('_popover'); 43 | return Ember.run(function() { 44 | popoverLink.destroy(); 45 | return popoverLink = null; 46 | }); 47 | }); 48 | return andThen(function() { 49 | return assert.ok(isNotPresent('.popover'), "The popover is destroyed"); 50 | }); 51 | }); 52 | 53 | test('Popover links can be configured to be opened with left clicks', function(assert) { 54 | assert.expect(1); 55 | popoverLink = this.subject({ 56 | rootElement: "#ember-testing", 57 | openOnLeftClick: true 58 | }); 59 | this.render(); 60 | click('.popover-link'); 61 | return andThen(function() { 62 | return assert.ok(isPresent('.popover'), "The popover is created"); 63 | }); 64 | }); 65 | 66 | test('Popover links can be configured to not be opened with left clicks', function(assert) { 67 | assert.expect(1); 68 | popoverLink = this.subject({ 69 | rootElement: "#ember-testing", 70 | openOnLeftClick: false 71 | }); 72 | this.render(); 73 | click('.popover-link'); 74 | return andThen(function() { 75 | return assert.ok(isNotPresent('.popover'), "The popover is not created"); 76 | }); 77 | }); 78 | 79 | test('Popover links can be configured to be opened with right clicks', function(assert) { 80 | assert.expect(1); 81 | popoverLink = this.subject({ 82 | rootElement: "#ember-testing", 83 | openOnRightClick: true 84 | }); 85 | this.render(); 86 | triggerEvent('.popover-link', 'contextmenu'); 87 | return andThen(function() { 88 | return assert.ok(isPresent('.popover'), "The popover is created"); 89 | }); 90 | }); 91 | 92 | test('Popover links can be configured to not be opened with right clicks', function(assert) { 93 | assert.expect(1); 94 | popoverLink = this.subject({ 95 | rootElement: "#ember-testing", 96 | openOnRightClick: false 97 | }); 98 | this.render(); 99 | triggerEvent('.popover-link', 'contextmenu'); 100 | return andThen(function() { 101 | return assert.ok(isNotPresent('.popover'), "The popover is not created"); 102 | }); 103 | }); 104 | 105 | test('Popover links do not bubble left click events when configured to open with left clicks', function(assert) { 106 | var bubbled; 107 | popoverLink = this.subject({ 108 | rootElement: "#ember-testing", 109 | openOnLeftClick: true, 110 | _openPopover: Ember.K 111 | }); 112 | this.render(); 113 | bubbled = Ember.run(popoverLink, 'click'); 114 | return assert.ok(!bubbled, 'The event did not bubble'); 115 | }); 116 | 117 | test('Popover links bubble left click events when not configured to open with left clicks', function(assert) { 118 | var bubbled; 119 | popoverLink = this.subject({ 120 | rootElement: "#ember-testing", 121 | openOnLeftClick: false, 122 | _openPopover: Ember.K 123 | }); 124 | this.render(); 125 | bubbled = Ember.run(popoverLink, 'click'); 126 | return assert.ok(bubbled, 'The event bubbled'); 127 | }); 128 | 129 | test('Popover links do not bubble right click events when configured to open with right clicks', function(assert) { 130 | var bubbled; 131 | popoverLink = this.subject({ 132 | rootElement: "#ember-testing", 133 | openOnRightClick: true, 134 | _openPopover: Ember.K 135 | }); 136 | this.render(); 137 | bubbled = Ember.run(popoverLink, 'contextMenu'); 138 | return assert.ok(!bubbled, 'The event did not bubble'); 139 | }); 140 | 141 | test('Popover links bubble right click events when not configured to open with right clicks', function(assert) { 142 | var bubbled; 143 | popoverLink = this.subject({ 144 | rootElement: "#ember-testing", 145 | openOnRightClick: false, 146 | _openPopover: Ember.K 147 | }); 148 | this.render(); 149 | bubbled = Ember.run(popoverLink, 'contextMenu'); 150 | return assert.ok(bubbled, 'The event bubbled'); 151 | }); 152 | 153 | test('Popover links can be configured to hide other popovers when opening', function(assert) { 154 | assert.expect(2); 155 | $(document).on('popover:hide', function() { 156 | return assert.ok(true, 'The popover:hide event was triggered'); 157 | }); 158 | popoverLink = this.subject({ 159 | rootElement: "#ember-testing", 160 | hideOthers: true 161 | }); 162 | this.render(); 163 | click('.popover-link'); 164 | andThen(function() { 165 | return assert.ok(isPresent('.popover'), "The new popover was not hidden"); 166 | }); 167 | andThen(function() { 168 | return $(document).off('popover:hide'); 169 | }); 170 | }); 171 | 172 | test('Popover links can be configured to not hide other popovers when opening', function(assert) { 173 | assert.expect(1); 174 | $(document).on('popover:hide', function() { 175 | return assert.ok(false, 'The popover:hide event was not triggered'); 176 | }); 177 | popoverLink = this.subject({ 178 | rootElement: "#ember-testing", 179 | hideOthers: false 180 | }); 181 | this.render(); 182 | click('.popover-link'); 183 | andThen(function() { 184 | return assert.ok(isPresent('.popover'), "The new popover was not hidden"); 185 | }); 186 | return $(document).off('popover:hide'); 187 | }); 188 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from '@ember/test-helpers'; 5 | import { start } from 'ember-cli-qunit'; 6 | import { default as registerRAFWaiter } from 'ember-raf-scheduler/test-support/register-waiter'; 7 | 8 | registerRAFWaiter(); 9 | setResolver(resolver); 10 | start(); 11 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Addepar/ember-widgets/868016f75d6453b8a8146e0f82595cec77f2fc04/tests/unit/.gitkeep --------------------------------------------------------------------------------