Skip to content

Instantly share code, notes, and snippets.

@ro0gr
Last active March 12, 2017 21:31
Show Gist options
  • Select an option

  • Save ro0gr/ca118c1e9cf72cfb4034cd944f634f95 to your computer and use it in GitHub Desktop.

Select an option

Save ro0gr/ca118c1e9cf72cfb4034cd944f634f95 to your computer and use it in GitHub Desktop.
routable-tabs
import Ember from 'ember';
import layout from '../templates/components/routable-tabs';
export default Ember.Component.extend({
classNameBindings: ['blockName'],
layout,
blockName: 'routable-tabs',
tagName: 'ul'
});
import Ember from 'ember';
import layout from '../../templates/components/routable-tabs/tab-item';
export default Ember.Component.extend({
classNameBindings: ['elementName'],
layout,
elementName: 'routable-tabs__tab-item',
tagName: 'li'
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
import Ember from 'ember';
const {
computed,
isEmpty,
assign,
ArrayProxy,
get,
setProperties,
getOwner
} = Ember;
const { service } = Ember.inject;
export default ArrayProxy.extend({
name: '',
routing: service('-routing'),
_routerMicrolib: computed('routing', function() {
return this.get('routing').router.router;
}),
_fromCurrent() {
const currentInfos = get(this, '_routerMicrolib').currentHandlerInfos;
return {
routeName: leafName(currentInfos),
params: getParamsHash(currentInfos)
};
},
attach(navItem) {
if (!navItem) {
navItem = this._fromCurrent();
}
const incomingInfos = this._recognize(navItem);
assign(navItem, extractTabSettingsFromHandlerInfos.call(this, incomingInfos), {
linkParams: [ navItem.routeName ]
.concat(getParamsValues(incomingInfos))
});
let existing = this.findOpened(incomingInfos);
if (!existing) {
this.addObject(navItem);
} else {
// @todo: replace
setProperties(existing, navItem);
}
},
detach(tab) {
const router = this.get('_routerMicrolib');
const isTabActive = router.isActive.apply(router, tab.linkParams);
const pos = this.indexOf(tab);
this.removeObject(tab);
if (isTabActive) {
let prevTab = this.objectAt(pos > 0 ? pos - 1 : pos);
if (prevTab) {
// router.replaceWith.apply(router, prevTab.linkParams);
} else { // all tabs are deleted
router.replaceWith([this.name]);
}
}
},
// @todo: test
_recognize({ routeName, params = null }) {
const routeRecognizer = get(this, '_routerMicrolib').recognizer;
const url = routeRecognizer.generate(routeName, params);
const recognized = routeRecognizer.recognize(url);
return recognized.slice().map(
hi => assign({}, hi, {
name: hi.handler
})
);
},
/**
*
*
* @param {Array} incomingInfos
*/
// @todo:
// - tests
// - fix permormance
findOpened(incomingInfos) {
return this.find(
tab => commonRoot(incomingInfos, this._recognize(tab))
);
}
});
function lastIndexWithParams(handlerInfos) {
let i = -1;
handlerInfos.forEach((hi, pos) => {
if (!Ember.isEmpty(hi.params)) {
i = pos;
}
});
return i;
}
function commonRoot(handlerInfos1, handlerInfos2) {
let lastIndexWithParams1 = lastIndexWithParams(handlerInfos1),
lastIndexWithParams2 = lastIndexWithParams(handlerInfos2);
if (lastIndexWithParams1 !== lastIndexWithParams2) {
return false;
}
for (let i = 0; i < lastIndexWithParams1; i++) {
if (handlerInfos1[i].handler !== handlerInfos2[i].handler) {
return false;
}
if (JSON.stringify(handlerInfos1[i].params) !== JSON.stringify(handlerInfos2[i].params)) {
return false;
}
}
return true;
}
function getParamsHash(handlerInfos) {
return assign.apply(null, getParams(handlerInfos));
}
function getParamsValues(handlerInfos) {
return Array.prototype.concat.apply(
[], getParams(handlerInfos).map(segmentParams => {
return Object.values(segmentParams);
})
);
}
function getParams(handlerInfos) {
return handlerInfos.map(hi => {
return JSON.parse(JSON.stringify(hi.params));
});
}
function leafName(handlerInfos) {
return handlerInfos.slice(-1)[0].name;
}
function readTabSettingsFromRouteHandler(routeHandler) {
let tab = get(routeHandler, 'tab');
return typeof tab === 'function' ?
tab.call(routeHandler, routeHandler.context) :
tab;
}
function extractTabSettingsFromHandlerInfos(handlerInfos) {
const owner = getOwner(this);
let settingsPerTab = handlerInfos
.map(routeHandler => {console.log(routeHandler.handler); return owner.lookup(`route:${routeHandler.handler}`)})
.filter(tab => !isEmpty(tab))
.map(readTabSettingsFromRouteHandler)
.map(tab => JSON.parse(JSON.stringify(tab)));
return assign.apply(this, [{}].concat(settingsPerTab));
}
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('customers');
this.route('customer', {
path: '/customer/:customerId'
}, function() {
this.route('edit');
});
});
export default Router;
import Ember from 'ember';
import routableTabs from '../utils/routable-tabs';
export default Ember.Route.extend({
tabs: routableTabs('application'),
tab: {
title: 'Main',
sticky: true
},
setupController(controller, model) {
this._super(...arguments);
const tabs = this.get('tabs');
controller.set('tabs', tabs);
//debugger
tabs.attach({
routeName: 'customers'
});
tabs.attach({
routeName: 'customer',
params: {
customerId: 1
}
});
tabs.attach({
routeName: 'customer',
params: {
customerId: 2
}
});
tabs.attach({
routeName: 'customer',
params: {
customerId: 3
}
});
console.log('tabs.length', tabs.get('length'))
},
didTransition() {
const tabs = this.get('tabs');
//tabs.attach();
}
});
import Ember from 'ember';
export default Ember.Route.extend({
tab({name}) {
return {
title: `Customer "${name}"`
}
},
model({customerId}) {
console.log('customerId', customerId)
return this.get('customers').find(customerId);
}
});
import Ember from 'ember';
export default Ember.Route.extend({
customers: Ember.inject.service(),
tab: {
title: 'Customers'
},
model() {
return this.get('customers').find();
}
});
import Ember from 'ember';
const {RSVP, A} = Ember;
// @todo: mirage
const customers = A([{
name: "Ivan", customerId: 1
}, {
name: "Vasiliy", customerId: 2
}, {
name: "Petro", customerId: 3
}]);
export default Ember.Service.extend({
find(id = null) {
if (id !== null) {
return customers.find(c => c.customerId === id);
}
return new RSVP.Promise(function(resolve) {
return customers;
});
}
});
import Ember from 'ember';
import TabsContainer from '../models/tabs-container';
const { A, setOwner, getOwner } = Ember;
export default Ember.Service.extend({
init() {
this._items = {};
},
containerFor(containerId) {
if (!this._items[containerId]) {
this._items[containerId] = TabsContainer.create({
'name': containerId,
content: A([])
});
let owner = getOwner(this);
setOwner(this._items[containerId], owner);
}
return this._items[containerId];
}
});
<h1>Welcome to {{appName}}</h1>
<br>
<br>
{{outlet}}
<br>
<br>
{{routable-tabs tabs=tabs}}
{{#each tabs as |tab|}}
{{#if hasBlock}}
{{yield (component 'routable-tabs/tab-item' tab=tab) tab}}
{{else}}
{{routable-tabs/tab-item tab=tab}}
{{/if}}
{{/each}}
{{#link-to params=tab.linkParams}}
{{#if hasBlock}}
{{yield tab}}
{{else}}
{{tab.title}}
{{/if}}
{{/link-to}}
{
"version": "0.11.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.11.0",
"ember-data": "2.11.0",
"ember-template-compiler": "2.11.0",
"ember-testing": "2.11.0"
},
"addons": {
}
}
import Ember from 'ember';
const {
getOwner,
computed
} = Ember;
/**
* Returns tabs root container
*
* @param {string} name root name
* @return {Model.TabsContainer}
*/
export default function routableTabs(name) {
return computed(function() {
return getOwner(this)
.lookup('service:tabs')
.containerFor(name);
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment