├── app
├── .gitkeep
├── templates
│ └── components
│ │ ├── ps-pane-link.hbs
│ │ ├── ps-pane-menu.hbs
│ │ ├── ps-pane.hbs
│ │ ├── ps-draggable-pane.hbs
│ │ └── ps-panel.hbs
└── components
│ ├── ps-draggable-pane.js
│ ├── ps-pane-link.js
│ ├── ps-pane-menu.js
│ ├── ps-pane.js
│ └── ps-panel.js
├── addon
├── .gitkeep
├── utils
│ ├── animate.js
│ ├── init-pane-controllers.js
│ ├── init-active-panes.js
│ └── find-component-by-name.js
├── controllers
│ ├── panel.js
│ └── pane.js
└── mixins
│ ├── component-registry.js
│ ├── child-component.js
│ └── draggable-panel.js
├── vendor
├── .gitkeep
└── ember-cli-panels.css
├── tests
├── unit
│ └── .gitkeep
├── dummy
│ ├── public
│ │ ├── .gitkeep
│ │ ├── robots.txt
│ │ └── crossdomain.xml
│ ├── app
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ ├── .gitkeep
│ │ │ ├── user.js
│ │ │ └── contact.js
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── views
│ │ │ └── .gitkeep
│ │ ├── components
│ │ │ └── .gitkeep
│ │ ├── controllers
│ │ │ ├── .gitkeep
│ │ │ ├── panes
│ │ │ │ ├── contacts.js
│ │ │ │ └── users.js
│ │ │ └── application.js
│ │ ├── templates
│ │ │ ├── components
│ │ │ │ └── .gitkeep
│ │ │ ├── panes
│ │ │ │ ├── contacts.hbs
│ │ │ │ └── users.hbs
│ │ │ └── application.hbs
│ │ ├── adapters
│ │ │ └── application.js
│ │ ├── router.js
│ │ ├── styles
│ │ │ └── app.css
│ │ ├── app.js
│ │ └── index.html
│ └── config
│ │ └── environment.js
├── helpers
│ ├── resolver.js
│ └── start-app.js
├── test-helper.js
├── index.html
└── .jshintrc
├── server
├── .jshintrc
├── index.js
└── mocks
│ ├── users.js
│ └── contacts.js
├── .bowerrc
├── config
└── environment.js
├── .npmignore
├── testem.json
├── .ember-cli
├── .gitignore
├── index.js
├── .travis.yml
├── blueprints
└── ember-cli-panels
│ └── index.js
├── bower.json
├── .editorconfig
├── .jshintrc
├── Brocfile.js
├── LICENSE.md
├── package.json
└── README.md
/app/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/views/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true
3 | }
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/templates/components/ps-pane-link.hbs:
--------------------------------------------------------------------------------
1 | {{yield}}
2 |
--------------------------------------------------------------------------------
/app/templates/components/ps-pane-menu.hbs:
--------------------------------------------------------------------------------
1 | {{yield}}
2 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/app/templates/components/ps-pane.hbs:
--------------------------------------------------------------------------------
1 | {{partial pane.partialName}}
2 | {{yield}}
3 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/app/templates/components/ps-draggable-pane.hbs:
--------------------------------------------------------------------------------
1 | {{partial pane.partialName}}
2 | {{yield}}
3 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment, appConfig */) {
4 | return { };
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/user.js:
--------------------------------------------------------------------------------
1 | import DS from 'ember-data';
2 |
3 | export default DS.Model.extend({
4 | name: DS.attr('string')
5 | });
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/contact.js:
--------------------------------------------------------------------------------
1 | import DS from 'ember-data';
2 |
3 | export default DS.Model.extend({
4 | name: DS.attr('string')
5 | });
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/adapters/application.js:
--------------------------------------------------------------------------------
1 | import DS from 'ember-data';
2 |
3 | export default DS.RESTAdapter.extend({
4 | namespace: 'api'
5 | });
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | tests/
3 | .bowerrc
4 | .editorconfig
5 | .ember-cli
6 | .travis.yml
7 | .npmignore
8 | **/.gitkeep
9 | bower.json
10 | Brocfile.js
11 | testem.js
12 | server/
13 |
--------------------------------------------------------------------------------
/testem.json:
--------------------------------------------------------------------------------
1 | {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html",
4 | "launch_in_ci": [
5 | "PhantomJS"
6 | ],
7 | "launch_in_dev": [
8 | "PhantomJS",
9 | "Chrome"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/addon/utils/animate.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default function animate($el, opts, ms) {
4 | if (ms !== 0 && !ms) {
5 | ms = 375;
6 | }
7 |
8 | return Ember.$.Velocity($el, opts, ms);
9 | }
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/panes/contacts.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Pane from 'ember-cli-panels/controllers/pane';
3 |
4 | export default Pane.extend({
5 | modelHook: function() {
6 | return this.store.find('contact');
7 | }
8 | });
9 |
--------------------------------------------------------------------------------
/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 | });
10 |
11 | export default Router;
12 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/addon/utils/init-pane-controllers.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default function initPaneControllers(paneNames) {
4 | return Ember.computed(function() {
5 | return paneNames.map(function(paneName) {
6 | return this.container.lookup('controller:' + paneName);
7 | }, this);
8 | });
9 | }
10 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /coverage/*
15 | /libpeerconnection.log
16 | npm-debug.log
17 | testem.log
18 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | 'use strict';
3 |
4 | var path = require('path');
5 |
6 | module.exports = {
7 | name: 'ember-cli-panels',
8 | isDevelopingAddon: function() {
9 | return true;
10 | },
11 |
12 | included: function(app) {
13 | app.import(path.join('vendor', 'ember-cli-panels.css'));
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 20px;
3 | }
4 |
5 | p {
6 | margin-bottom: 5px;
7 | }
8 |
9 | .ps-panel {
10 | position: relative;
11 | height: 150px;
12 | }
13 |
14 | .ps-pane {
15 | position: absolute;
16 | }
17 |
18 | .panel-menu div {
19 | float: left;
20 | padding-right: 10px;
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/panes/contacts.hbs:
--------------------------------------------------------------------------------
1 |
2 | Contacts
3 |
4 |
5 | {{#if pane.loading}}
6 | Loading...
7 | {{/if}}
8 |
9 |
10 | {{#each contact in pane.model}}
11 | -
12 | {{contact.name}}
13 |
14 | {{else}}
15 | -
16 | No Contacts Found
17 |
18 | {{/each}}
19 |
20 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/panes/users.hbs:
--------------------------------------------------------------------------------
1 |
2 | Users
3 |
4 |
5 |
6 | {{#each user in pane.model}}
7 | -
8 | {{user.name}}
9 |
12 |
13 | {{else}}
14 | -
15 | No Users Found
16 |
17 | {{/each}}
18 |
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | - "0.12"
5 |
6 | sudo: false
7 |
8 | cache:
9 | directories:
10 | - node_modules
11 |
12 | before_install:
13 | - "npm config set spin false"
14 | - "npm install -g npm@^2"
15 |
16 | install:
17 | - npm install -g bower
18 | - npm install
19 | - bower install
20 |
21 | script:
22 | - npm test
23 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/panes/users.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Pane from 'ember-cli-panels/controllers/pane';
3 |
4 | export default Pane.extend({
5 | modelHook: function() {
6 | return this.store.find('user');
7 | },
8 |
9 | actions: {
10 | remove: function(user) {
11 | this.get('model').removeObject(user);
12 | }
13 | }
14 | });
15 |
--------------------------------------------------------------------------------
/addon/controllers/panel.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { animate } from 'liquid-fire';
3 |
4 | export default Ember.Controller.extend({
5 | queryParams: ['pane'],
6 |
7 | showAnimation: function() {
8 | return animate(this, {translateX: 0, opacity: 1}, {}, 'show ps-pane');
9 | },
10 |
11 | hideAnimation: function() {
12 | return animate(this, {translateX: -100, opacity: 0}, {}, 'hide ps-pane');
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/addon/mixins/component-registry.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Mixin.create({
4 | // class can override this to give it a different name
5 | childComponentsName: 'children',
6 |
7 | __registerChild: function(child) {
8 | this.get(this.get('childComponentsName')).unshiftObject(child);
9 | },
10 |
11 | __unregisterChild: function(child) {
12 | this.get(this.get('childComponentsName')).removeObject(child);
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/blueprints/ember-cli-panels/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | normalizeEntityName: function() { },
3 |
4 | afterInstall: function() {
5 | var blueprint = this;
6 |
7 | return blueprint.addPackageToProject('liquid-fire', '~0.16.0').then(function() {
8 | return blueprint.addBowerPackageToProject([
9 | { name: 'hammer.js', target: '^2.0.4' },
10 | { name: 'jquery-scrollstop', target: '~1.1.0' }
11 | ]);
12 | });
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/templates/components/ps-panel.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#each paneCtrl in paneControllers}}
3 | {{#if draggable}}
4 | {{ps-draggable-pane
5 | pane=paneCtrl
6 | showAnimation=showAnimation
7 | hideAnimation=hideAnimation}}
8 | {{else}}
9 | {{ps-pane
10 | pane=paneCtrl
11 | showAnimation=showAnimation
12 | hideAnimation=hideAnimation}}
13 | {{/if}}
14 | {{/each}}
15 |
16 |
17 | {{yield}}
18 |
--------------------------------------------------------------------------------
/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 | Ember.MODEL_FACTORY_INJECTIONS = true;
7 |
8 | var App = Ember.Application.extend({
9 | modulePrefix: config.modulePrefix,
10 | podModulePrefix: config.podModulePrefix,
11 | Resolver: Resolver
12 | });
13 |
14 | loadInitializers(App, config.modulePrefix);
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
8 | document.write('');
9 |
10 | QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
11 | var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
12 | document.getElementById('ember-testing-container').style.visibility = containerVisibility;
13 |
--------------------------------------------------------------------------------
/app/components/ps-draggable-pane.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import PsPane from './ps-pane';
3 |
4 | export default PsPane.extend({
5 | _hideAnimation: function() {
6 | return true;
7 | },
8 |
9 | _showAnimation: function() {
10 | this.get('panel').updateContainerWidth(this.width());
11 |
12 | return true;
13 | },
14 |
15 | width: function() {
16 | return this.$().outerWidth();
17 | },
18 |
19 | updateWidth: function(width) {
20 | return this.$().width(width);
21 | }
22 | });
23 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 | Welcome to ember-cli-panels
2 |
3 | {{ps-panel
4 | currentPaneName=pane
5 | paneControllers=paneControllers
6 | showAnimation=showAnimation
7 | hideAnimation=hideAnimation}}
8 |
9 |
21 |
--------------------------------------------------------------------------------
/addon/mixins/child-component.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | // The parent must mixin ParentRegistrar
4 | export default Ember.Mixin.create({
5 | parentName: 'parent', // class can override this to give it a different name
6 |
7 | __registerWithParent: Ember.on('didInsertElement', function() {
8 | this.get(this.get('parentName')).__registerChild(this);
9 | }),
10 |
11 | __deregisterWithParent: Ember.on('willDestroyElement', function() {
12 | this.get(this.get('parentName')).__unregisterChild(this);
13 | }),
14 | });
15 |
--------------------------------------------------------------------------------
/addon/utils/init-active-panes.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default function initActivePanes(currentPane, paneNames) {
4 | return Ember.on('init', function() {
5 | paneNames.forEach(function(paneName) {
6 | var split = paneName.split('/');
7 | var name = split[split.length - 1];
8 |
9 | var propName = Ember.String.camelize(name + 'Active');
10 |
11 | Ember.defineProperty(this, propName,
12 | Ember.computed.equal(currentPane, paneName));
13 | }, this);
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/tests/helpers/start-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Application from '../../app';
3 | import Router from '../../router';
4 | import config from '../../config/environment';
5 |
6 | export default function startApp(attrs) {
7 | var application;
8 |
9 | var attributes = Ember.merge({}, config.APP);
10 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
11 |
12 | Ember.run(function() {
13 | application = Application.create(attributes);
14 | application.setupForTesting();
15 | application.injectTestHelpers();
16 | });
17 |
18 | return application;
19 | }
20 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-panels",
3 | "dependencies": {
4 | "jquery": "^1.11.1",
5 | "ember": "1.10.0",
6 | "ember-data": "1.0.0-beta.16",
7 | "ember-resolver": "~0.1.14",
8 | "loader.js": "ember-cli/loader.js#3.2.0",
9 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
10 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
11 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.0.2",
12 | "ember-qunit": "0.2.8",
13 | "ember-qunit-notifications": "0.0.7",
14 | "qunit": "~1.17.1",
15 | "hammer.js": "~2.0.4",
16 | "jquery-scrollstop": "~1.1.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | indent_style = space
22 | indent_size = 2
23 |
24 | [*.css]
25 | indent_style = space
26 | indent_size = 2
27 |
28 | [*.html]
29 | indent_style = space
30 | indent_size = 2
31 |
32 | [*.md]
33 | trim_trailing_whitespace = false
34 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser": true,
8 | "boss": true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true
32 | }
33 |
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 |
12 |
13 |
14 |
15 | {{content-for 'head-footer'}}
16 |
17 |
18 | {{content-for 'body'}}
19 |
20 |
21 |
22 |
23 | {{content-for 'body-footer'}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/addon/controllers/pane.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default Ember.Controller.extend({
4 | modelHook: function() {
5 | return null;
6 | },
7 |
8 | partialName: Ember.computed.alias('name'),
9 |
10 | name: Ember.computed(function() {
11 | return this.toString().split(':')[1];
12 | }),
13 |
14 | setupModel: Ember.on('init', function() {
15 | var pane = this;
16 | this.set('loading', true);
17 |
18 | Ember.RSVP.resolve(this.modelHook()).then(function(model) {
19 | if (model) {
20 | pane.set('model', model);
21 | }
22 | }, function(err) {
23 | console.log('modelHook error:', err);
24 | }).finally(function() {
25 | pane.set('loading', false);
26 | });
27 | })
28 | });
29 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | // To use it create some files under `routes/`
2 | // e.g. `server/routes/ember-hamsters.js`
3 | //
4 | // module.exports = function(app) {
5 | // app.get('/ember-hamsters', function(req, res) {
6 | // res.send('hello');
7 | // });
8 | // };
9 |
10 | module.exports = function(app) {
11 | var globSync = require('glob').sync;
12 | var mocks = globSync('./mocks/**/*.js', { cwd: __dirname }).map(require);
13 | var proxies = globSync('./proxies/**/*.js', { cwd: __dirname }).map(require);
14 |
15 | // Log proxy requests
16 | var morgan = require('morgan');
17 | app.use(morgan('dev'));
18 |
19 | mocks.forEach(function(route) { route(app); });
20 | proxies.forEach(function(route) { route(app); });
21 |
22 | };
23 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/application.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import initPaneControllers from 'ember-cli-panels/utils/init-pane-controllers';
3 | import { animate } from 'liquid-fire';
4 |
5 | export default Ember.Controller.extend({
6 | queryParams: ['pane'],
7 | pane: 'panes/users',
8 |
9 | paneControllers: initPaneControllers(['panes/users', 'panes/contacts']),
10 |
11 | showAnimation: function() {
12 | return animate(this, {translateX: 0, opacity: 1}, {}, 'show ps-pane');
13 | },
14 |
15 | hideAnimation: function() {
16 | return animate(this, {translateX: -100, opacity: 0}, {}, 'hide ps-pane');
17 | },
18 |
19 | actions: {
20 | switchPane: function(name) {
21 | this.set('pane', name);
22 | }
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/vendor/ember-cli-panels.css:
--------------------------------------------------------------------------------
1 | .ps-panel {
2 | position: relative;
3 | width: 100%;
4 | overflow-x: hidden;
5 | }
6 |
7 | .ps-pane {
8 | overflow-x: hidden;
9 | overflow-y: scroll;
10 | -webkit-overflow-scrolling: touch;
11 | height: 100%;
12 | margin: 0;
13 | padding: 0;
14 | float: left;
15 | display: block;
16 | }
17 |
18 | /* http://cantina.co/thought_leadership/ios-5-native-scrolling-grins-and-gothcas/ */
19 | .ps-pane > * {
20 | -webkit-transform: translate3d(0,0,0);
21 | }
22 |
23 | .ps-pane-menu {
24 | height: 3rem;
25 | overflow: hidden;
26 | text-align: justify;
27 | white-space: nowrap;
28 | }
29 |
30 | .ps-pane-link {
31 | line-height: 1rem;
32 | text-decoration: none;
33 | font-size: 1.0rem;
34 | padding: 1rem 2rem;
35 | display: inline-block;
36 | }
37 |
--------------------------------------------------------------------------------
/Brocfile.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 | /* global require, module */
3 |
4 | var EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
5 |
6 | var app = new EmberAddon();
7 |
8 | // Use `app.import` to add additional libraries to the generated
9 | // output files.
10 | //
11 | // If you need to use different assets in different
12 | // environments, specify an object as the first parameter. That
13 | // object's keys should be the environment name and the values
14 | // should be the asset to use in that environment.
15 | //
16 | // If the library that you are including contains AMD or ES6
17 | // modules that you would like to import into your application
18 | // please specify an object with the list of modules as keys
19 | // along with the exports of each module as its value.
20 |
21 | module.exports = app.toTree();
22 |
--------------------------------------------------------------------------------
/addon/utils/find-component-by-name.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 |
3 | export default function(componentName, errMsg) {
4 | if (!errMsg) {
5 | errMsg = '';
6 | }
7 |
8 | return Ember.computed(function() {
9 | function tryParentContext(parent) {
10 | var parentComponentName = getComponentName(parent);
11 |
12 | if (parentComponentName === componentName) {
13 | return parent;
14 | }
15 |
16 | var parentsParent = parent.get('parentView');
17 |
18 | if (parentsParent) {
19 | return tryParentContext(parentsParent);
20 | }
21 |
22 | throw new Ember.Error('Could not find parent component named ' + componentName + ' to return. ' + errMsg);
23 | }
24 |
25 | return tryParentContext(this.get('parentView'));
26 | });
27 | }
28 |
29 | function getComponentName(component) {
30 | var name = component.toString();
31 |
32 | if (name.indexOf('@component') > -1) {
33 | return name.split(':')[1];
34 | }
35 |
36 | return false;
37 | }
38 |
--------------------------------------------------------------------------------
/server/mocks/users.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | var express = require('express');
3 | var usersRouter = express.Router();
4 |
5 | usersRouter.get('/', function(req, res) {
6 | res.send({
7 | "users": [
8 | {
9 | id: 0,
10 | name: "Jake Craige"
11 | }, {
12 | id: 1,
13 | name: "Matthew Hager"
14 | }, {
15 | id: 2,
16 | name: "Dalia Rihani"
17 | }
18 | ]
19 | });
20 | });
21 |
22 | usersRouter.post('/', function(req, res) {
23 | res.status(201).end();
24 | });
25 |
26 | usersRouter.get('/:id', function(req, res) {
27 | res.send({
28 | "users": {
29 | "id": req.params.id
30 | }
31 | });
32 | });
33 |
34 | usersRouter.put('/:id', function(req, res) {
35 | res.send({
36 | "users": {
37 | "id": req.params.id
38 | }
39 | });
40 | });
41 |
42 | usersRouter.delete('/:id', function(req, res) {
43 | res.status(204).end();
44 | });
45 |
46 | app.use('/api/users', usersRouter);
47 | };
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/server/mocks/contacts.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | var express = require('express');
3 | var contactsRouter = express.Router();
4 |
5 | contactsRouter.get('/', function(req, res) {
6 | // Simulate delay
7 | setTimeout(function() {
8 | res.send({
9 | "contacts": [
10 | {
11 | id: 0,
12 | name: "Bart Kowalski"
13 | }, {
14 | id: 1,
15 | name: "Lauren Schott"
16 | }, {
17 | id: 2,
18 | name: "John Goodman"
19 | }
20 | ]
21 | });
22 | }, 3000);
23 | });
24 |
25 | contactsRouter.post('/', function(req, res) {
26 | res.status(201).end();
27 | });
28 |
29 | contactsRouter.get('/:id', function(req, res) {
30 | res.send({
31 | "contacts": {
32 | "id": req.params.id
33 | }
34 | });
35 | });
36 |
37 | contactsRouter.put('/:id', function(req, res) {
38 | res.send({
39 | "contacts": {
40 | "id": req.params.id
41 | }
42 | });
43 | });
44 |
45 | contactsRouter.delete('/:id', function(req, res) {
46 | res.status(204).end();
47 | });
48 |
49 | app.use('/api/contacts', contactsRouter);
50 | };
51 |
--------------------------------------------------------------------------------
/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 | baseURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | }
14 | },
15 |
16 | APP: {
17 | // Here you can pass flags/options to your application instance
18 | // when it is created
19 | }
20 | };
21 |
22 | if (environment === 'development') {
23 | // ENV.APP.LOG_RESOLVER = true;
24 | ENV.APP.LOG_ACTIVE_GENERATION = true;
25 | // ENV.APP.LOG_TRANSITIONS = true;
26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
27 | ENV.APP.LOG_VIEW_LOOKUPS = true;
28 | }
29 |
30 | if (environment === 'test') {
31 | // Testem prefers this...
32 | ENV.baseURL = '/';
33 | ENV.locationType = 'none';
34 |
35 | // keep test console output quieter
36 | ENV.APP.LOG_ACTIVE_GENERATION = false;
37 | ENV.APP.LOG_VIEW_LOOKUPS = false;
38 |
39 | ENV.APP.rootElement = '#ember-testing';
40 | }
41 |
42 | if (environment === 'production') {
43 |
44 | }
45 |
46 | return ENV;
47 | };
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-panels",
3 | "version": "0.0.6",
4 | "directories": {
5 | "doc": "doc",
6 | "test": "tests"
7 | },
8 | "scripts": {
9 | "start": "ember server",
10 | "build": "ember build",
11 | "test": "ember test"
12 | },
13 | "homepage": "https://github.com/poetic/ember-cli-panels",
14 | "repository": "https://github.com/poetic/ember-cli-panels.git",
15 | "engines": {
16 | "node": ">= 0.10.0"
17 | },
18 | "author": "Jake Craige ",
19 | "license": "MIT",
20 | "devDependencies": {
21 | "broccoli-asset-rev": "^2.0.2",
22 | "ember-cli": "0.2.1",
23 | "ember-cli-app-version": "0.3.3",
24 | "ember-cli-content-security-policy": "0.4.0",
25 | "ember-cli-dependency-checker": "0.0.8",
26 | "ember-cli-htmlbars": "0.7.4",
27 | "ember-cli-ic-ajax": "0.1.1",
28 | "ember-cli-inject-live-reload": "^1.3.0",
29 | "ember-cli-qunit": "0.3.9",
30 | "ember-cli-uglify": "1.0.1",
31 | "ember-data": "1.0.0-beta.16",
32 | "ember-export-application-global": "^1.0.2",
33 | "ember-cli-github-pages": "0.0.2",
34 | "liquid-fire": "^0.15.2"
35 | },
36 | "description": "An ember addon to support immediate switching between templates.",
37 | "keywords": [
38 | "ember-addon"
39 | ],
40 | "dependencies": {
41 | "ember-cli-babel": "^4.0.0"
42 | },
43 | "ember-addon": {
44 | "configPath": "tests/dummy/config"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/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 |
32 |
33 | {{content-for 'head-footer'}}
34 | {{content-for 'test-head-footer'}}
35 |
36 |
37 |
38 | {{content-for 'body'}}
39 | {{content-for 'test-body'}}
40 |
41 |
42 |
43 |
44 |
45 |
46 | {{content-for 'body-footer'}}
47 | {{content-for 'test-body-footer'}}
48 |
49 |
50 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "$",
8 | "-Promise",
9 | "QUnit",
10 | "define",
11 | "console",
12 | "equal",
13 | "notEqual",
14 | "notStrictEqual",
15 | "test",
16 | "asyncTest",
17 | "testBoth",
18 | "testWithDefault",
19 | "raises",
20 | "throws",
21 | "deepEqual",
22 | "start",
23 | "stop",
24 | "ok",
25 | "strictEqual",
26 | "module",
27 | "moduleFor",
28 | "moduleForComponent",
29 | "moduleForModel",
30 | "process",
31 | "expect",
32 | "visit",
33 | "exists",
34 | "fillIn",
35 | "click",
36 | "keyEvent",
37 | "triggerEvent",
38 | "find",
39 | "findWithAssert",
40 | "wait",
41 | "DS",
42 | "isolatedContainer",
43 | "startApp",
44 | "andThen",
45 | "currentURL",
46 | "currentPath",
47 | "currentRouteName"
48 | ],
49 | "node": false,
50 | "browser": false,
51 | "boss": true,
52 | "curly": false,
53 | "debug": false,
54 | "devel": false,
55 | "eqeqeq": true,
56 | "evil": true,
57 | "forin": false,
58 | "immed": false,
59 | "laxbreak": false,
60 | "newcap": true,
61 | "noarg": true,
62 | "noempty": false,
63 | "nonew": false,
64 | "nomen": false,
65 | "onevar": false,
66 | "plusplus": false,
67 | "regexp": false,
68 | "undef": true,
69 | "sub": true,
70 | "strict": false,
71 | "white": false,
72 | "eqnull": true,
73 | "esnext": true
74 | }
75 |
--------------------------------------------------------------------------------
/app/components/ps-pane-link.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import { animate } from 'liquid-fire';
3 | import ChildComponent from 'ember-cli-panels/mixins/child-component';
4 | import findComponentByName from 'ember-cli-panels/utils/find-component-by-name';
5 |
6 | export default Ember.Component.extend(ChildComponent, {
7 | parentName: 'menu', // for ChildComponent
8 | menu: findComponentByName('ps-pane-menu'),
9 | currentPane: Ember.computed.alias('menu.currentPane'),
10 | panePrefix: Ember.computed.alias('menu.panePrefix'),
11 |
12 | tagName: 'a',
13 | classNames: 'ps-pane-link',
14 |
15 | attributeBindings: ['href'],
16 | href: '#',
17 |
18 | classNameBindings: ['active'],
19 | active: Ember.computed('currentPane', 'prefixedTo', function() {
20 | return Ember.isEqual(this.get('currentPane'), this.get('prefixedTo'));
21 | }),
22 |
23 | prefixedTo: Ember.computed('panePrefix', 'to', function() {
24 | if (this.get('panePrefix')) {
25 | return this.get('panePrefix') + '/' + this.get('to');
26 | }
27 |
28 | return this.get('to');
29 | }),
30 |
31 | click: function(e) {
32 | e.preventDefault();
33 |
34 | this.get('menu').sendAction('action', this.get('prefixedTo'));
35 | this.get('menu').switchPane(this.get('prefixedTo'));
36 | },
37 |
38 | scrollIntoCenter: function(menu, duration) {
39 | return animate(this, 'scroll', {
40 | container: menu.$(),
41 | axis: 'x',
42 | offset: this.calculateOffset(menu.$(), this.$()),
43 | duration: duration
44 | });
45 | },
46 |
47 | calculateOffset: function($menu, $el) {
48 | var menuWidth = $menu.outerWidth();
49 | var elWidth = $el.outerWidth();
50 |
51 | return elWidth / 2 - menuWidth / 2;
52 | }
53 | });
54 |
--------------------------------------------------------------------------------
/app/components/ps-pane-menu.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ComponentRegistry from 'ember-cli-panels/mixins/component-registry';
3 |
4 | export default Ember.Component.extend(ComponentRegistry, {
5 | classNames: 'ps-pane-menu',
6 |
7 | currentPane: null,
8 | panePrefix: null,
9 | childComponentsName: 'linkComponents', // for ComponentRegistry
10 | linkComponents: Ember.A([]),
11 | animating: false,
12 | isFirstRender: true,
13 |
14 | switchPane: function(newPane) {
15 | if (this.get('animating')) {
16 | return;
17 | }
18 |
19 | // hackyhack fixes flashing bug from clicking to next pane too quick in menu
20 | // I think the run loop / ember ends up merging all the changes into one
21 | // when I use the timeout? I figured it would do this anyways but it doesn't
22 | // seem to.
23 | Ember.run.later(this, function() {
24 | this.set('currentPane', newPane);
25 | }, 100);
26 | },
27 |
28 | activeLink: Ember.computed('linkComponents.@each.active', function() {
29 | return this.get('linkComponents').findBy('active', true);
30 | }),
31 |
32 | initialScrollToCenter: Ember.on('didInsertElement', function() {
33 | return this.scrollToCenter();
34 | }),
35 |
36 | scrollToCenter: Ember.observer('currentPane', function() {
37 | var menu = this;
38 | var activeLink = this.get('activeLink');
39 |
40 | if (!activeLink) {
41 | return;
42 | }
43 |
44 | var ms = 200;
45 | if (menu.get('isFirstRender')) {
46 | menu.set('isFirstRender', false);
47 | ms = 0;
48 | }
49 |
50 | menu.set('animating', true);
51 | activeLink.scrollIntoCenter(menu, ms).then(function() {
52 | menu.set('animating', false);
53 | });
54 | })
55 | });
56 |
--------------------------------------------------------------------------------
/app/components/ps-pane.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ChildComponent from 'ember-cli-panels/mixins/child-component';
3 | import findComponentByName from 'ember-cli-panels/utils/find-component-by-name';
4 |
5 | export default Ember.Component.extend(ChildComponent, {
6 | name: Ember.computed.alias('pane.name'),
7 | scrolling: false,
8 |
9 | classNames: 'ps-pane',
10 |
11 | parentName: 'panel', // for ChildComponent
12 | panel: findComponentByName('ps-panel'),
13 | animating: Ember.computed.alias('panel.animating'),
14 |
15 | addScrollHandler: Ember.on('didInsertElement', function() {
16 | this.$().on('scrollstart', this.scrollstart.bind(this));
17 | this.$().on('scrollstop', this.scrollstop.bind(this));
18 | }),
19 |
20 | setupHeight: Ember.on('didInsertElement', function(){
21 | // cannot target panes using 'this.$()' because they have different
22 | // offsets when initially inserted into the DOM
23 | var offset = $('.ps-pane').offset().top
24 | var height = $(window).height()
25 | var paneHeight = height - offset
26 | $('.ps-pane').height(paneHeight);
27 | }),
28 |
29 | removeScrollHandler: Ember.on('willDestroyElement', function() {
30 | this.$().off('scrollstart', this.scrollstart.bind(this));
31 | this.$().off('scrollstop', this.scrollstop.bind(this));
32 | }),
33 |
34 | scrollstart: function() {
35 | this.set('scrolling', true);
36 | },
37 |
38 | scrollstop: function() {
39 | this.set('scrolling', false);
40 | },
41 |
42 | _hideAnimation: function() {
43 | if (this.get('hideAnimation')) {
44 | return this.get('hideAnimation')(this);
45 | }
46 |
47 | return this.$().hide();
48 | },
49 |
50 | _showAnimation: function() {
51 | if (this.get('showAnimation')) {
52 | return this.get('showAnimation')(this);
53 | }
54 |
55 | return this.$().show();
56 | }
57 | });
58 |
--------------------------------------------------------------------------------
/addon/mixins/draggable-panel.js:
--------------------------------------------------------------------------------
1 | /* global Hammer */
2 |
3 | import Ember from 'ember';
4 |
5 | // Can only be mixed into ps-panel
6 | export default Ember.Mixin.create({
7 | setupPanHammer: Ember.on('didInsertElement', function() {
8 | if (!this.get('draggable')) {
9 | return;
10 | }
11 |
12 | var panel = this;
13 |
14 | var hammer = new Hammer(this.$()[0], {
15 | touchAction: 'pan-y'
16 | });
17 |
18 | this.set('hammer', hammer);
19 |
20 | hammer.get('pan').set({
21 | direction: Hammer.DIRECTION_HORIZONTAL
22 | });
23 |
24 | hammer.on('panmove', function(event) {
25 | return panel.panmove(event);
26 | });
27 |
28 | hammer.on('panend', function(event) {
29 | return panel.panend(event);
30 | });
31 | }),
32 |
33 | threshold: Ember.computed('paneWidth', function() {
34 | return this.get('elWidth') * 0.30;
35 | }),
36 |
37 | disallowPan: Ember.computed.or('isPaneScrolling', 'animating'),
38 |
39 | disallowPanChanged: Ember.observer('disallowPan', 'hammer', function() {
40 | var hammer = this.get('hammer');
41 | if (!hammer) {
42 | return;
43 | }
44 |
45 | if (this.get('disallowPan')) {
46 | hammer.get('pan').set({enable: false});
47 | } else {
48 | hammer.get('pan').set({enable: true});
49 | }
50 | }),
51 |
52 | panmove: function(event) {
53 | var containerXOffset = this.get('containerXOffset');
54 | var offset = containerXOffset + event.deltaX;
55 |
56 | var xSwipe = Math.abs(event.deltaX) >= Math.abs(event.deltaY);
57 |
58 | // animate only if pan is a horizontal swipe
59 | if (xSwipe) {
60 | Ember.$.Velocity(this.get('$container'), {
61 | translateX: offset
62 | }, { duration: 0 });
63 | }
64 | },
65 |
66 | panend: function(event) {
67 | return this.panChooseAnimation(event);
68 | },
69 |
70 | panChooseAnimation: function(event) {
71 | var deltaX = event.deltaX;
72 | var deltaY = event.deltaY;
73 | var xSwipe = Math.abs(deltaX) >= Math.abs(deltaY);
74 |
75 | var threshold = this.get('threshold');
76 | var prevPane = this.get('prevPane');
77 | var nextPane = this.get('nextPane');
78 |
79 | if (prevPane && (deltaX > threshold) && xSwipe) {
80 | return this.animateToPane(prevPane);
81 |
82 | } else if ((deltaX > 0) && xSwipe) {
83 | return this.animateToCurrentPane();
84 |
85 | } else if ((nextPane && Math.abs(deltaX) > threshold) && xSwipe) {
86 | return this.animateToPane(nextPane);
87 |
88 | } else {
89 | return this.animateToCurrentPane();
90 | }
91 | },
92 | });
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ember-cli-panels
2 |
3 | Disclaimer: This is a WIP undergoing heavy development. Breaking changes may be ahead.
4 |
5 | ## Tip
6 |
7 | Make sure to have a meta tag like this in your app if you want to prevent
8 | zooming and weird scrolling issues.
9 |
10 | ```html
11 |
12 | ```
13 |
14 | ## Installation
15 |
16 | `npm install ember-cli-panels --save-dev`
17 |
18 | ## Getting Started
19 |
20 | This package exposes a `ps-panel` component that wraps dynamically created `ps-pane` components.
21 |
22 | The `ps-panel` is defined in your template like so:
23 |
24 | ```javascript
25 | // app/templates/index.hbs
26 |
27 | {{ps-panel
28 | draggable=true
29 | currentPaneName=pane
30 | paneControllers=paneControllers
31 | showAnimation=showAnimation
32 | hideAnimation=hideAnimation
33 | }}
34 | ```
35 |
36 | Where `currentPaneName` is a pane you would like to initially present when rendering the template, `paneControllers` is a list of controllers that are associated with each pane, and `showAnimation`, `hideAnimation`, are [liquid-fire](https://github.com/ef4/liquid-fire) animations that handle the transitions.
37 |
38 | `ps-panel` components are of two kinds, static and draggable. Draggable allows for horizontal swipe-to-navigate functionality between panes, and static (by removing `draggable=true`) will allow for immediate pane switching based on links or actions.
39 |
40 | There is some setup required in the controller for the template. Specifically, a `pane` query param needs to be registered and initialized with the pane that is shown on rendering. This is the `pane` property passed into the `ps-pane` above.
41 |
42 | Optionally, in the controller for this template you can extend from `PanelController`.
43 |
44 | ```javascript
45 | // app/controllers/index.js
46 |
47 | import PanelController from 'ember-cli-panels/controllers/panel';
48 | import initPaneControllers from 'ember-cli-panels/utils/init-pane-controllers';
49 |
50 | export default PanelController.extend({
51 | // define this property to declare the initially rendered pane
52 | pane: 'panes/main-nav/upcoming',
53 |
54 | // order here reflects left/right order of panes in template
55 | paneControllers: initPaneControllers([
56 | 'panes/main-nav/rsvp', 'panes/main-nav/upcoming', 'panes/main-nav/past-events'
57 | ]),
58 | });
59 | ```
60 |
61 | This will set up the `pane` query parameter and provide default `showAnimation` and `hideAnimation` functions. These can be overridden if need be by importing the `animate` function from liquid-fire.
62 |
63 | A pane is defined by a file in the `templates/` folder and a matching file in the `controllers/` folder. So the above pane property will look for a template in `app/templates/panes/main-nav/upcoming` and a controller in `app/controllers/panes/main-nav/upcoming`.
64 |
65 | ## Running
66 |
67 | * `ember server`
68 | * Visit your app at http://localhost:4200.
69 |
70 | ## Running Tests
71 |
72 | * `ember test`
73 | * `ember test --server`
74 |
75 | ## Building
76 |
77 | * `ember build`
78 |
79 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/).
80 |
--------------------------------------------------------------------------------
/app/components/ps-panel.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import ComponentRegistry from 'ember-cli-panels/mixins/component-registry';
3 | import DraggablePanelMixin from 'ember-cli-panels/mixins/draggable-panel';
4 | import animate from 'ember-cli-panels/utils/animate';
5 |
6 | export default Ember.Component.extend(ComponentRegistry, DraggablePanelMixin, {
7 | animating: false,
8 |
9 | classNames: 'ps-panel',
10 |
11 | childComponentsName: 'paneComponents', // for ComponentRegistry
12 | paneControllers: Ember.A([]), // public [1,2,3]
13 | paneComponents: Ember.A([]),
14 | currentPaneName: null, // public
15 | currentPane: null,
16 |
17 | $container: null,
18 | $panel: null,
19 | elWidth: 0,
20 | containerWidth: 0,
21 | containerXOffset: 0,
22 | isFirstRender: true,
23 |
24 | updateVisiblePane: Ember.observer('currentPaneName', 'paneComponents.[]', 'paneControllers.[]', function() {
25 | // Guard to make sure all paneControllers are rendered before running any animations.
26 | if (this.get('paneComponents.length') !== this.get('paneControllers.length')) {
27 | return;
28 | }
29 |
30 | if (this.get('animating')) {
31 | return;
32 | }
33 |
34 | var hasShownPane = false;
35 | var currentPaneName = this.get('currentPaneName');
36 | var component = this;
37 |
38 | var animations = this.get('paneComponents').map(function(pane, index) {
39 | if (pane.get('name') === currentPaneName) {
40 | hasShownPane = true;
41 | this.set('currentPane', pane);
42 |
43 | return Ember.RSVP.resolve(pane._showAnimation()).then(function() {
44 | var ms;
45 |
46 | if (component.get('isFirstRender')) {
47 | component.set('isFirstRender', false)
48 | ms = 0;
49 | }
50 |
51 | return component.animateToPaneAtIndex(index, ms);
52 | });
53 |
54 | } else {
55 | return Ember.RSVP.resolve(pane._hideAnimation());
56 | }
57 | }, this);
58 |
59 | if (!hasShownPane) {
60 | throw new Ember.Error('Could not find pane with name "' + currentPaneName + '" to show.');
61 | }
62 |
63 | return Ember.RSVP.all(animations).finally(function() {
64 | component.send('stopAnimating');
65 | });
66 | }),
67 |
68 | paneIndex: Ember.computed('paneComponents.[]', 'currentPane', function() {
69 | return this.get('paneComponents').indexOf(this.get('currentPane'));
70 | }),
71 |
72 | prevPane: Ember.computed('paneComponents.[]', 'paneIndex', function() {
73 | return this.get('paneComponents')[this.get('paneIndex') - 1]
74 | }),
75 |
76 | nextPane: Ember.computed('paneComponents.[]', 'paneIndex', function() {
77 | return this.get('paneComponents')[this.get('paneIndex') + 1]
78 | }),
79 |
80 | scrollingPanes: Ember.computed.filterBy('paneComponents', 'scrolling', true),
81 | isPaneScrolling: Ember.computed('scrollingPanes.[]', function() {
82 | return this.get('scrollingPanes.length') > 0;
83 | }),
84 |
85 | setupJqCache: Ember.on('didInsertElement', function() {
86 | var $panel = this.$();
87 | this.set('$panel', $panel);
88 | this.set('$container', $panel.find('.ps-panel-container'));
89 | }),
90 |
91 | containerWidthChanged: Ember.observer('containerWidth', '$container',
92 | 'elWidth', 'paneComponents.[]', function() {
93 | var $container = this.get('$container');
94 | if ($container) {
95 | $container.width(this.get('containerWidth'));
96 | }
97 |
98 | this.get('paneComponents').forEach(function(component) {
99 | if (this.get('elWidth')) {
100 | component.updateWidth(this.get('elWidth'))
101 | }
102 | }, this);
103 | }),
104 |
105 | animateToPaneAtIndex: function(index, ms) {
106 | if (this.get('animating')) {
107 | return;
108 | }
109 |
110 | if (ms !== 0 && !ms) {
111 | ms = 325;
112 | }
113 |
114 | var component = this;
115 |
116 | component.send('startAnimating');
117 |
118 | var containerXOffset = component.xOffsetForIndex(index);
119 | this.set('containerXOffset', containerXOffset);
120 |
121 | var pane = this.get('paneComponents')[index];
122 |
123 | return animate(component.get('$container'), {
124 | translateX: containerXOffset
125 | }, ms).then(function() {
126 | component.setProperties({
127 | currentPane: pane,
128 | currentPaneName: pane.get('name')
129 | });
130 | component.send('stopAnimating');
131 | });
132 | },
133 |
134 | animateToPane: function(pane) {
135 | var index = this.get('paneComponents').indexOf(pane);
136 |
137 | return this.animateToPaneAtIndex(index, 200);
138 | },
139 |
140 | updateContainerWidth: function(singlePaneWidth) {
141 | var newWidth = singlePaneWidth * this.get('paneControllers.length');
142 | this.set('elWidth', singlePaneWidth);
143 | this.set('containerWidth', newWidth);
144 | },
145 |
146 | animateToCurrentPane: function() {
147 | return this.animateToPaneAtIndex(this.get('paneIndex'));
148 | },
149 |
150 | xOffsetForIndex: function(index) {
151 | var elWidth = this.get('elWidth');
152 | var offset = -(index * elWidth);
153 |
154 | return offset;
155 | },
156 |
157 | actions: {
158 | startAnimating: function() {
159 | this.set('animating', true);
160 | },
161 |
162 | stopAnimating: function() {
163 | this.set('animating', false);
164 | }
165 | }
166 | });
167 |
--------------------------------------------------------------------------------