Skip to content

Commit

Permalink
Pills Component.
Browse files Browse the repository at this point in the history
This commit also contains an abstract generic Views and Mixins for Items & Item selection.

Fixes #8
  • Loading branch information
asaf committed Aug 29, 2013
1 parent ee7757c commit 81c1256
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 2 deletions.
3 changes: 2 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,8 @@ module.exports = function (grunt) {
'.tmp/scripts/bs-progressbar.js': [
'<%= yeoman.app %>/templates/components/bs-progress.hbs',
'<%= yeoman.app %>/templates/components/bs-progressbar.hbs'
]
],
'.tmp/scripts/bs-pills.js': '<%= yeoman.app %>/templates/components/bs-pills.hbs',
}
},
compile_showcase: {
Expand Down
15 changes: 14 additions & 1 deletion app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<body>
<!-- build:js showcase/scripts/showcase-components.js -->
<script src="bower_components/jquery/jquery.js"></script>
<script src="bower_components/handlebars/handlebars.runtime.js"></script>
<script src="bower_components/handlebars/handlebars.js"></script>
<script src="bower_components/ember/ember.js"></script>
<script src="bower_components/highlightjs/highlight.pack.js"></script>
<script src="bower_components/bootstrap/assets/js/holder.js"></script>
Expand All @@ -40,6 +40,15 @@
<script src="scripts/mixins/Register.js"></script>
<script src="scripts/mixins/type.js"></script>
<script src="scripts/mixins/size.js"></script>

<!-- items -->
<script src="scripts/views/ItemView.js"></script>
<script src="scripts/views/ItemsView.js"></script>
<script src="scripts/mixins/ItemSelection.js"></script>
<script src="scripts/mixins/ItemsSelection.js"></script>
<script src="scripts/mixins/SelectableView.js"></script>
<script src="scripts/mixins/Nav.js"></script>
<script src="scripts/mixins/NavItem.js"></script>
<!-- endbuild -->

<!--COMPONENTS -->
Expand Down Expand Up @@ -77,6 +86,10 @@
<script src="scripts/bs-progressbar.js"></script>
<!-- endbuild -->

<!-- build:js({.tmp,app}) js/bs-pills.min.js-->
<script src="scripts/components/BsPill.js"></script>
<script src="scripts/components/BsPills.js"></script>
<!-- endbuild -->

<!-- build:js({.tmp,app}) showcase/scripts/showcase.js -->
<script src="scripts/showcase-templates.js"></script>
Expand Down
10 changes: 10 additions & 0 deletions app/scripts/components/BsPill.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Bootstrap.BsPill = Bootstrap.ItemView.extend(Bootstrap.NavItem, Bootstrap.ItemSelection,
template: Ember.Handlebars.compile('{{view view.pillAsLinkView}}')

pillAsLinkView: Ember.View.extend(
tagName: 'a'
template: Ember.Handlebars.compile('{{view.parentView.title}}')
attributeBindings: ['href']
href: "#"
)
)
9 changes: 9 additions & 0 deletions app/scripts/components/BsPills.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Bootstrap.BsPills = Bootstrap.ItemsView.extend(Bootstrap.Nav,
navType: 'pills'
classNameBindings: ['stacked:nav-stacked', 'justified:nav-justified']
attributeBindings: ['style']

itemViewClass: Bootstrap.BsPill
)

Ember.Handlebars.helper('bs-pills', Bootstrap.BsPills)
48 changes: 48 additions & 0 deletions app/scripts/mixins/ItemSelection.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
###
A Mixin to enhance items enhanced with the 'IsItem' Mixin with selection capability.
When a click event is received the current item will be stored in the parent view 'selected' property,
An extra 'active' css class will be assigned to the Item (this) if this is a selected item.
###
Bootstrap.ItemSelection = Ember.Mixin.create(
classNameBindings: ["isActive:active"]

###
Determine whether the current item is selected,
if true the 'active' css class will be associated with the this DOM's element.
This is a calculated property and will be retriggered if the 'value' property of the item has changed or the 'selected' property
in the parent ItemsView.
###
isActive: (->
#TODO: Ensure parentView is inherited from ItemsView
itemsView = @get('parentView')
if not itemsView?
return
selected = itemsView.get 'selected'
value = @get 'value'
selected is value
).property('value', 'parentView.selected').cacheable()

###
Handle selection by click event.
The identifier of the selection is based on the 'content' property of this item.
###
click: (event) ->
#event.stopPropagation()
event.preventDefault()

#TODO: Ensure parentView is inherited from ItemsView
itemsView = @get('parentView')
if not itemsView?
return

content = @get('content')
#TODO: Ensure its an Ember object
if typeof(content) is 'object'
return if content.get('disabled')

#Currently multi selection is not supported
itemsView.set('selected', @get('value'))
)
14 changes: 14 additions & 0 deletions app/scripts/mixins/ItemsSelection.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
###
A Mixin to enhance views that extends from 'ItemsView' with selection capability.
###
Bootstrap.ItemsSelection = Ember.Mixin.create(
###
If true, multiple selection is supported
###
multiSelection: false

###
An array of selected item(s), can be also bound to a controller property via 'selectedBinding'
###
selected: []
)
13 changes: 13 additions & 0 deletions app/scripts/mixins/Nav.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
###
A Mixin that provides the basic configuration for rendering a Bootstrap navigation such as tabs and pills
###
Bootstrap.Nav = Ember.Mixin.create(
classNames: ['nav']
classNameBindings: ['navTypeClass']
tagName: 'ul'
navType: null

navTypeClass: ( ->
if @navType? then "nav-#{@navType}" else null
).property('navType').cacheable()
)
7 changes: 7 additions & 0 deletions app/scripts/mixins/NavItem.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
###
A Mixin that provides the basic configuration for rendering and interacting with Bootstrap navigation item such a pill or a tab.
###
Bootstrap.NavItem = Ember.Mixin.create(Bootstrap.SelectableView
#unncessary as Ember magically matches this according to the parent collectionView tag
#tagName: 'li'
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Showcase.ShowComponentsPillsController = Ember.Controller.extend(
content: ['Home', 'Profile', 'Messages']

init: () ->
@._super()
@set('content1', Ember.A([
Ember.Object.create({ title: 'Home', disabled: false }),
Ember.Object.create({ title: 'Admin', disabled: true })
]))

@set('selected1', @get('content1').objectAt(0))

contentChanged: (->
console.log("Selection has changed to: #{@get('selected')}")
).observes('selected')

disableHome: () ->
@get('content1').objectAt(0).set('disabled', true)
)
1 change: 1 addition & 0 deletions app/scripts/showcase/router.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ Showcase.Router.map(() ->
@route 'page-header'
@route 'button'
@route 'progressbar'
@route 'pills'
)
1 change: 1 addition & 0 deletions app/scripts/showcase/routes/ApplicationRoute.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ Showcase.ApplicationRoute = Ember.Route.extend(
Ember.Object.create({title: "Page Header", route: "show_components.page-header"})
Ember.Object.create({title: "Button", route: "show_components.button"})
Ember.Object.create({title: "Progressbar", route: "show_components.progressbar"})
Ember.Object.create({title: "Nav - Pills", route: "show_components.pills"})
]
)
65 changes: 65 additions & 0 deletions app/scripts/views/ItemView.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
###
Views that are rendered as an Item of the ItemsView should extends from this view.
When a click event is received the current item will be stored in the parent view 'selected' property,
An extra 'active' css class will be assigned to the Item (this) if this is a selected item.
Views that extends this view can be enhanced with:
ItemSelection: Makes the item selectable.
###
Bootstrap.ItemView = Ember.View.extend (
isItem: true
classNameBindings: ['disabled']

###
The value of the item, currently Items content supports only an array of strings, so value is the actual 'content' property
of the item.
###
value: (->
#TODO: Ensure parentView is inherited from ItemsView
itemsView = @get('parentView')
if not itemsView?
return

value = @get('content')
value
).property('content').cacheable()

###
A calculated property that defines the title of the item.
###
title: (->
#TODO: Ensure parentView is inherited from ItemsView
itemsView = @get('parentView')
if not itemsView?
return

itemTitleKey = itemsView.get('itemTitleKey') || 'title'
content = @get('content')
#TODO: content is 'object' is not sufficient, it is required to also check that this is an Ember object or has a get method
if typeof(content) is 'object' then content.get(itemTitleKey) else content
).property('content').cacheable()

###
Determine whether the item is disabled or not
###
disabled: (->
#TODO: Ensure parentView is inherited from ItemsView
itemsView = @get('parentView')
if not itemsView?
return

content = @get('content')
#TODO: content is 'object' is not sufficient, it is required to also check that this is an Ember object or has a get method
if typeof(content) is 'object'
if content.get('disabled')
#remove selection if item is selected
if @get('isActive')
itemsView.set('selected', null)
return content.get('disabled')
else
false

false
).property('content', 'content.disabled').cacheable()
)
8 changes: 8 additions & 0 deletions app/scripts/views/ItemsView.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
###
A parent view of views that supports multiple items rendering such as Navigations (Tabs, Pills)
Views that inherits from this view can be enhanced with:
- ItemsSelection: Enhances with selection capability.
###
Bootstrap.ItemsView = Ember.CollectionView.extend(
)
104 changes: 104 additions & 0 deletions app/templates/showcase/show_components/pills.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Pills

[Pills](http://getbootstrap.com/components/#nav-pills) are a button-like navigation style,

Bootstrap doesn't dictate nor assist with how you respond when an item is selected in the Pills navigation,
But using _bs-pills_ helps by making the navigation more interactive by providing selection bindings and styling the
active element, see examples below.

<div class="bs-example">
{{bs-pills contentBinding="content" selectedBinding="selected"}}
Selected: {{selected}}
</div>

``` html

\{\{bs-pills contentBinding="content" selectedBinding="selected"\}\}

Selected: \{\{selected\}\}
```

_content_ and _selected_ are properties in the current _controller_


* The _content_ controller property is an array of strings where each string is rendered as a possible item selection.
* When an item selection is changed, the _selected_ property in the current _controller_ will be set with the value of the selected item.

Here's the controller sample code:

``` javascript
Showcase.ShowComponentsPillsController = Ember.Controller.extend({
content: ['Home', 'Profile', 'Messages'],
contentChanged: (function() {
return console.log("Selection has changed to: " + (this.get('selected')));
}).observes('selected')
});
```

# Stacked

> Pills are also vertically stackable. Just add `stacked=true``
<div class="bs-example">
{{bs-pills contentBinding="content" selectedBinding="selected" stacked=true style="max-width: 300px;"}}
</div>

``` html
\{\{bs-pills contentBinding="content" selectedBinding="selected" stacked=true style="max-width: 300px;"\}\}
```


# Justified

> Easily make tabs or pills equal widths of their parent with setting `justified=true`
<div class="bs-example">
{{bs-pills contentBinding="content" selectedBinding="selected" justified=true}}
</div>

``` html
\{\{bs-pills contentBinding="content" selectedBinding="selected" justified=true\}\}
```

# Disabled links

Bootstrap doesn't stop the propagation of the redirection of disabled links for you:

> This class will only change the _anchor_'s appearance, not its functionality. Use custom JavaScript to disable links here.
<small>[By Bootstrap](http://getbootstrap.com/components/#nav-disabled-links)</small>

But using _bs-pills_ ease your pain by giving you the opportunity to bind the disable state of each item by providing extra metadata per item, clicking a disabled item disables the links too.

To define links (items) as disabed, you need to provide extra metadata per defined item in the _contentBinding_ property.

<div class="bs-example">
{{bs-pills contentBinding="content1" selectedBinding="selected1"}}
{{bs-button clicked="disableHome" content="Disable Home"}}
</div>

``` html
\{\{bs-pills contentBinding="content" selectedBinding="selected"\}\}
\{\{bs-button clicked="disableHome" content="Disable Home"\}\}
```

Controller sample code:

``` javascript

Showcase.ShowComponentsPillsController = Ember.Controller.extend({
init: function() {
this._super();
this.set('content', Ember.A([
Ember.Object.create({title: 'Home',disabled: false}),
Ember.Object.create({title: 'Admin',disabled: true})
]));
this.set('selected', this.get('content').objectAt(0));
},

disableHome: function() {
this.get('content').objectAt(0).set('disabled', true);
}
});
```

0 comments on commit 81c1256

Please sign in to comment.