Created
August 5, 2016 16:45
-
-
Save nbibler/b1e795ee06962b080fee17dee8cc142a to your computer and use it in GitHub Desktop.
tabs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import layout from '../templates/components/ivy-tab-list'; | |
/** | |
* @module ivy-tabs | |
*/ | |
/** | |
* @class IvyTabListComponent | |
* @namespace IvyTabs | |
* @extends Ember.Component | |
*/ | |
export default Ember.Component.extend({ | |
layout: layout, | |
tagName: 'ul', | |
attributeBindings: ['aria-multiselectable'], | |
classNames: ['ivy-tab-list'], | |
init() { | |
this._super(...arguments); | |
Ember.run.once(this, this._registerWithTabsContainer); | |
}, | |
willDestroy() { | |
this._super(...arguments); | |
Ember.run.once(this, this._unregisterWithTabsContainer); | |
}, | |
/** | |
* Tells screenreaders that only one tab can be selected at a time. | |
* | |
* @property aria-multiselectable | |
* @type String | |
* @default 'false' | |
*/ | |
'aria-multiselectable': 'false', | |
/** | |
* The `role` attribute of the tab list element. | |
* | |
* See http://www.w3.org/TR/wai-aria/roles#tablist | |
* | |
* @property ariaRole | |
* @type String | |
* @default 'tablist' | |
*/ | |
ariaRole: 'tablist', | |
/** | |
* Gives focus to the selected tab. | |
* | |
* @method focusSelectedTab | |
*/ | |
focusSelectedTab() { | |
this.get('selectedTab').$().focus(); | |
}, | |
/** | |
* Event handler for navigating tabs via arrow keys. The left (or up) arrow | |
* selects the previous tab, while the right (or down) arrow selects the next | |
* tab. | |
* | |
* @method navigateOnKeyDown | |
* @param {Event} event | |
*/ | |
navigateOnKeyDown: Ember.on('keyDown', function(event) { | |
switch (event.keyCode) { | |
case 37: /* left */ | |
case 38: /* up */ | |
this.selectPreviousTab(); | |
break; | |
case 39: /* right */ | |
case 40: /* down */ | |
this.selectNextTab(); | |
break; | |
default: | |
return; | |
} | |
event.preventDefault(); | |
Ember.run.scheduleOnce('afterRender', this, this.focusSelectedTab); | |
}), | |
/** | |
* Adds a tab to the `tabs` array. | |
* | |
* @method registerTab | |
* @param {IvyTabs.IvyTabComponent} tab | |
*/ | |
registerTab(tab) { | |
this.get('tabs').pushObject(tab); | |
}, | |
/** | |
* Selects the next tab in the list, if any. | |
* | |
* @method selectNextTab | |
*/ | |
selectNextTab() { | |
let index = this.get('selected-index') + 1; | |
if (index === this.get('tabs.length')) { index = 0; } | |
this.selectTabByIndex(index); | |
}, | |
/** | |
* Selects the previous tab in the list, if any. | |
* | |
* @method selectPreviousTab | |
*/ | |
selectPreviousTab() { | |
let index = this.get('selected-index') - 1; | |
// Previous from the first tab should select the last tab. | |
if (index < 0) { index = this.get('tabs.length') - 1; } | |
// This would only happen if there are no tabs, so stay at 0. | |
if (index < 0) { index = 0; } | |
this.selectTabByIndex(index); | |
}, | |
'selected-index': Ember.computed.alias('tabsContainer.selected-index'), | |
/** | |
* The currently-selected `ivy-tab` instance. | |
* | |
* @property selectedTab | |
* @type IvyTabs.IvyTabComponent | |
*/ | |
selectedTab: Ember.computed('selected-index', 'tabs.[]', function() { | |
return this.get('tabs').objectAt(this.get('selected-index')); | |
}), | |
/** | |
* Select the given tab. | |
* | |
* @method selectTab | |
* @param {IvyTabs.IvyTabComponent} tab | |
*/ | |
selectTab(tab) { | |
this.selectTabByIndex(this.get('tabs').indexOf(tab)); | |
}, | |
/** | |
* Select the tab at `index`. | |
* | |
* @method selectTabByIndex | |
* @param {Number} index | |
*/ | |
selectTabByIndex(index) { | |
this.sendAction('on-select', index); | |
}, | |
tabs: Ember.computed(function() { | |
return Ember.A(); | |
}).readOnly(), | |
/** | |
* The `ivy-tabs` component. | |
* | |
* @property tabsContainer | |
* @type IvyTabs.IvyTabsComponent | |
* @default null | |
*/ | |
tabsContainer: null, | |
/** | |
* Removes a tab from the `tabs` array. | |
* | |
* @method unregisterTab | |
* @param {IvyTabs.IvyTabComponent} tab | |
*/ | |
unregisterTab(tab) { | |
const index = tab.get('index'); | |
this.get('tabs').removeObject(tab); | |
if (index < this.get('selected-index')) { | |
this.selectPreviousTab(); | |
} else if (tab.get('isSelected')) { | |
if (index !== 0) { | |
this.selectPreviousTab(); | |
} | |
} | |
}, | |
_registerWithTabsContainer() { | |
this.get('tabsContainer').registerTabList(this); | |
}, | |
_unregisterWithTabsContainer() { | |
this.get('tabsContainer').unregisterTabList(this); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
/** | |
* @module ivy-tabs | |
*/ | |
/** | |
* @class IvyTabPanelComponent | |
* @namespace IvyTabs | |
* @extends Ember.Component | |
*/ | |
export default Ember.Component.extend({ | |
attributeBindings: ['aria-hidden', 'aria-labelledby'], | |
classNames: ['ivy-tab-panel'], | |
classNameBindings: ['active'], | |
init() { | |
this._super(...arguments); | |
Ember.run.once(this, this._registerWithTabsContainer); | |
}, | |
willDestroy() { | |
this._super(...arguments); | |
Ember.run.once(this, this._unregisterWithTabsContainer); | |
}, | |
/** | |
* Tells screenreaders whether or not the panel is visible. | |
* | |
* See http://www.w3.org/TR/wai-aria/states_and_properties#aria-hidden | |
* | |
* @property aria-hidden | |
* @type Boolean | |
* @readOnly | |
*/ | |
'aria-hidden': Ember.computed.not('isSelected').readOnly(), | |
/** | |
* Tells screenreaders which tab labels this panel. | |
* | |
* See http://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby | |
* | |
* @property aria-labelledby | |
* @type String | |
* @readOnly | |
*/ | |
'aria-labelledby': Ember.computed.readOnly('tab.elementId'), | |
/** | |
* See http://www.w3.org/TR/wai-aria/roles#tabpanel | |
* | |
* @property ariaRole | |
* @type String | |
* @default 'tabpanel' | |
*/ | |
ariaRole: 'tabpanel', | |
/** | |
* Accessed as a className binding to apply the panel's `activeClass` CSS | |
* class to the element when the panel's `isSelected` property is true. | |
* | |
* @property active | |
* @type String | |
* @readOnly | |
*/ | |
active: Ember.computed('isSelected', function() { | |
if (this.get('isSelected')) { return this.get('activeClass'); } | |
}), | |
/** | |
* The CSS class to apply to a panel's element when its `isSelected` property | |
* is `true`. | |
* | |
* @property activeClass | |
* @type String | |
* @default 'active' | |
*/ | |
activeClass: 'active', | |
/** | |
* The index of this panel in the `ivy-tabs` component. | |
* | |
* @property index | |
* @type Number | |
*/ | |
index: Ember.computed('tabPanels.[]', function() { | |
return this.get('tabPanels').indexOf(this); | |
}), | |
/** | |
* Whether or not this panel's associated tab is selected. | |
* | |
* @property isSelected | |
* @type Boolean | |
* @readOnly | |
*/ | |
isSelected: Ember.computed.readOnly('tab.isSelected'), | |
/** | |
* If `false`, this panel will appear hidden in the DOM. This is an alias to | |
* `isSelected`. | |
* | |
* @property isVisible | |
* @type Boolean | |
* @readOnly | |
*/ | |
isVisible: Ember.computed.readOnly('isSelected'), | |
/** | |
* The `ivy-tab` associated with this panel. | |
* | |
* @property tab | |
* @type IvyTabs.IvyTabComponent | |
*/ | |
tab: Ember.computed('tabs.[]', 'index', function() { | |
const tabs = this.get('tabs'); | |
if (tabs) { return tabs.objectAt(this.get('index')); } | |
}), | |
/** | |
* The `ivy-tab-list` component this panel belongs to. | |
* | |
* @property tabList | |
* @type IvyTabs.IvyTabListComponent | |
* @readOnly | |
*/ | |
tabList: Ember.computed.readOnly('tabsContainer.tabList'), | |
/** | |
* The array of all `ivy-tab-panel` instances within the `ivy-tabs` | |
* component. | |
* | |
* @property tabPanels | |
* @type Array | IvyTabs.IvyTabPanelComponent | |
* @readOnly | |
*/ | |
tabPanels: Ember.computed.readOnly('tabsContainer.tabPanels'), | |
/** | |
* The array of all `ivy-tab` instances within the `ivy-tab-list` component. | |
* | |
* @property tabs | |
* @type Array | IvyTabs.IvyTabComponent | |
* @readOnly | |
*/ | |
tabs: Ember.computed.readOnly('tabList.tabs'), | |
/** | |
* The `ivy-tabs` component. | |
* | |
* @property tabsContainer | |
* @type IvyTabs.IvyTabsComponent | |
* @default null | |
*/ | |
tabsContainer: null, | |
_registerWithTabsContainer() { | |
this.get('tabsContainer').registerTabPanel(this); | |
}, | |
_unregisterWithTabsContainer() { | |
this.get('tabsContainer').unregisterTabPanel(this); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
/** | |
* @module ivy-tabs | |
*/ | |
/** | |
* @class IvyTabComponent | |
* @namespace IvyTabs | |
* @extends Ember.Component | |
*/ | |
export default Ember.Component.extend({ | |
tagName: 'li', | |
attributeBindings: ['aria-controls', 'aria-expanded', 'aria-selected', 'selected', 'tabindex'], | |
classNames: ['ivy-tab'], | |
classNameBindings: ['active'], | |
init() { | |
this._super(...arguments); | |
Ember.run.once(this, this._registerWithTabList); | |
}, | |
willDestroy() { | |
this._super(...arguments); | |
Ember.run.once(this, this._unregisterWithTabList); | |
}, | |
/** | |
* Tells screenreaders which panel this tab controls. | |
* | |
* See http://www.w3.org/TR/wai-aria/states_and_properties#aria-controls | |
* | |
* @property aria-controls | |
* @type String | |
* @readOnly | |
*/ | |
'aria-controls': Ember.computed.readOnly('tabPanel.elementId'), | |
/** | |
* Tells screenreaders whether or not this tab's panel is expanded. | |
* | |
* See http://www.w3.org/TR/wai-aria/states_and_properties#aria-expanded | |
* | |
* @property aria-expanded | |
* @type String | |
* @readOnly | |
*/ | |
'aria-expanded': Ember.computed.readOnly('aria-selected'), | |
/** | |
* Tells screenreaders whether or not this tab is selected. | |
* | |
* See http://www.w3.org/TR/wai-aria/states_and_properties#aria-selected | |
* | |
* @property aria-selected | |
* @type String | |
*/ | |
'aria-selected': Ember.computed('isSelected', function() { | |
return this.get('isSelected') + ''; // coerce to 'true' or 'false' | |
}), | |
/** | |
* The `role` attribute of the tab element. | |
* | |
* See http://www.w3.org/TR/wai-aria/roles#tab | |
* | |
* @property ariaRole | |
* @type String | |
* @default 'tab' | |
*/ | |
ariaRole: 'tab', | |
/** | |
* The `selected` attribute of the tab element. If the tab's `isSelected` | |
* property is `true` this will be the literal string 'selected', otherwise | |
* it will be `undefined`. | |
* | |
* @property selected | |
* @type String | |
*/ | |
selected: Ember.computed('isSelected', function() { | |
if (this.get('isSelected')) { return 'selected'; } | |
}), | |
/** | |
* Makes the selected tab keyboard tabbable, and prevents tabs from getting | |
* focus when clicked with a mouse. | |
* | |
* @property tabindex | |
* @type Number | |
*/ | |
tabindex: Ember.computed('isSelected', function() { | |
if (this.get('isSelected')) { return 0; } | |
}), | |
/** | |
* Accessed as a className binding to apply the tab's `activeClass` CSS class | |
* to the element when the tab's `isSelected` property is true. | |
* | |
* @property active | |
* @type String | |
* @readOnly | |
*/ | |
active: Ember.computed('isSelected', function() { | |
if (this.get('isSelected')) { return this.get('activeClass'); } | |
}), | |
/** | |
* The CSS class to apply to a tab's element when its `isSelected` property | |
* is `true`. | |
* | |
* @property activeClass | |
* @type String | |
* @default 'active' | |
*/ | |
activeClass: 'active', | |
/** | |
* The index of this tab in the `ivy-tab-list` component. | |
* | |
* @property index | |
* @type Number | |
*/ | |
index: Ember.computed('tabs.[]', function() { | |
return this.get('tabs').indexOf(this); | |
}), | |
/** | |
* Whether or not this tab is selected. | |
* | |
* @property isSelected | |
* @type Boolean | |
*/ | |
isSelected: Ember.computed('tabList.selectedTab', function() { | |
return this.get('tabList.selectedTab') === this; | |
}), | |
/** | |
* Called when the user clicks on the tab. Selects this tab. | |
* | |
* @method select | |
*/ | |
select: Ember.on('click', 'touchEnd', function() { | |
this.get('tabList').selectTab(this); | |
}), | |
/** | |
* The `ivy-tab-list` component this tab belongs to. | |
* | |
* @property tabList | |
* @type IvyTabs.IvyTabListComponent | |
* @default null | |
*/ | |
tabList: null, | |
/** | |
* The `ivy-tab-panel` associated with this tab. | |
* | |
* @property tabPanel | |
* @type IvyTabs.IvyTabPanelComponent | |
*/ | |
tabPanel: Ember.computed('tabPanels.[]', 'index', function() { | |
return this.get('tabPanels').objectAt(this.get('index')); | |
}), | |
/** | |
* The array of all `ivy-tab-panel` instances within the `ivy-tabs` | |
* component. | |
* | |
* @property tabPanels | |
* @type Array | IvyTabs.IvyTabPanelComponent | |
* @readOnly | |
*/ | |
tabPanels: Ember.computed.readOnly('tabsContainer.tabPanels'), | |
/** | |
* The array of all `ivy-tab` instances within the `ivy-tab-list` component. | |
* | |
* @property tabs | |
* @type Array | IvyTabs.IvyTabComponent | |
* @readOnly | |
*/ | |
tabs: Ember.computed.readOnly('tabList.tabs'), | |
/** | |
* The `ivy-tabs` component. | |
* | |
* @property tabsContainer | |
* @type IvyTabs.IvyTabsComponent | |
* @readOnly | |
*/ | |
tabsContainer: Ember.computed.readOnly('tabList.tabsContainer'), | |
_registerWithTabList() { | |
this.get('tabList').registerTab(this); | |
}, | |
_unregisterWithTabList() { | |
this.get('tabList').unregisterTab(this); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import layout from '../templates/components/ivy-tabs'; | |
/** | |
* @module ivy-tabs | |
*/ | |
/** | |
* @class IvyTabsComponent | |
* @namespace IvyTabs | |
* @extends Ember.Component | |
*/ | |
export default Ember.Component.extend({ | |
layout: layout, | |
classNames: ['ivy-tabs'], | |
/** | |
* Set this to the index of the tab you'd like to be selected. Usually it is | |
* bound to a controller property that is used as a query parameter, but can | |
* be bound to anything. | |
* | |
* @property selected-index | |
* @type Number | |
* @default 0 | |
*/ | |
'selected-index': 0, | |
/** | |
* Registers the `ivy-tab-list` instance. | |
* | |
* @method registerTabList | |
* @param {IvyTabs.IvyTabListComponent} tabList | |
*/ | |
registerTabList(tabList) { | |
this.set('tabList', tabList); | |
Ember.run.once(this, this._selectTabByIndex); | |
}, | |
/** | |
* Adds a panel to the `tabPanels` array. | |
* | |
* @method registerTabPanel | |
* @param {IvyTabs.IvyTabPanelComponent} tabPanel | |
*/ | |
registerTabPanel(tabPanel) { | |
this.get('tabPanels').pushObject(tabPanel); | |
}, | |
tabPanels: Ember.computed(function() { | |
return Ember.A(); | |
}).readOnly(), | |
/** | |
* Removes the `ivy-tab-list` component. | |
* | |
* @method unregisterTabList | |
* @param {IvyTabs.IvyTabListComponent} tabList | |
*/ | |
unregisterTabList(/* tabList */) { | |
this.set('tabList', null); | |
}, | |
/** | |
* Removes a panel from the `tabPanels` array. | |
* | |
* @method unregisterTabPanel | |
* @param {IvyTabs.IvyTabPanelComponent} tabPanel | |
*/ | |
unregisterTabPanel(tabPanel) { | |
this.get('tabPanels').removeObject(tabPanel); | |
}, | |
_selectTabByIndex() { | |
let selectedIndex = this.get('selected-index'); | |
if (Ember.isNone(selectedIndex)) { selectedIndex = 0; } | |
this.get('tabList').selectTabByIndex(selectedIndex); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import IvyTabListComponent from './ivy-tab-list'; | |
export default IvyTabListComponent.extend({ | |
tagName: 'ul', | |
classNames: ['nav', 'nav-tabs'] | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import IvyTabComponent from './ivy-tab'; | |
export default IvyTabComponent.extend({ | |
tagName: 'li', | |
actions: { | |
select() { | |
this.select(); | |
} | |
}, | |
panelHref: Ember.computed('index', function() { | |
return '#' + this.get('index'); | |
}) | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Controller.extend({ | |
appName: 'Ember Twiddle', | |
init() { | |
this._super(...arguments); | |
console.log('hi'); | |
this.set('selectedIndex', document.location.hash); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.10.4", | |
"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.7.0", | |
"ember-data": "2.7.0", | |
"ember-template-compiler": "2.7.0" | |
}, | |
"addons": {} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment