app/assets/javascripts/luca/containers/container.coffee in luca-0.9.89 vs app/assets/javascripts/luca/containers/container.coffee in luca-0.9.91
- old
+ new
@@ -126,10 +126,14 @@
#
# container.defines
# rowFluid: true
#
# #### Using a layout template with CSS Selectors
+# If you find yourself needing a container view with a complicated
+# visual layout, you can provide your own DOM template as a `@bodyTemplate`
+# and assign each child view in `@components` to its own specific CSS selector.
+#
# ...
# container.contains
# role: "filter"
# container: "#filter-wrapper-dom-selector"
# ,
@@ -151,37 +155,98 @@
"first:activation"
container.replaces "Luca.Container"
container.publicConfiguration
+ # @components should contain a list of object configurations for child view(s)
+ # of this container. The values specified in the configuration object will override the
+ # values defined as properties and methods on your view prototypes.
+ #
+ # There are special properties you can define in your components configuration items
+ # that will effect the container:
+ #
+ # - role: will create a camelized getter for you on the container. e.g. when role is `my_custom_role`,
+ # the container will have a method `getMyCustomRole()` that returns that child view.
+ #
+ # - name: a name for the child view. this allows you to access the component by name using
+ # the find() method on the container.
+ #
+ # - type: a type alias from the component registry. type alias are underscore'd strings
+ # matching the component class name. e.g. App.views.MyCustomView type alias is `my_custom_view`
+ #
+ # - component: a convenience property for setting type, role, and name to be equal.
components:[]
-container.privateConfiguration
- emptyContainerElements: false
- className: 'luca-ui-container'
- componentTag: 'div'
- componentClass: 'luca-ui-panel'
- isContainer: true
- rendered: false
+ # The `@defaults` property is an object of configuration parameters which will be set
+ # on each child component. Values explicitly defines in the components config will
+ # take precedence over the default.
+ defaults: {}
+ # The `@extensions` property is useful when you are subclassing a container view
+ # which already defines an array of components, and you want to specifically override
+ # properties and settings on the children. The `@extensions` property expects either:
+ #
+ # An object whose keys match the names of the `@role` property defined on the child components.
+ # The value should be an object which will override any values defined on the parent class.
+ #
+ # or:
+ #
+ # An array of objects in the same array position / index as the target child view you wish to extend.
+ extensions: {}
+
# @componentEvents provides declarative syntax for responding to events on
# the components in this container. the format of the syntax is very similar
# to the other event binding helpers:
#
- # component_accessor component:trigger
+ # `component_accessor component:trigger`
#
- # where component_accessor is either the name of the role, or a method on the container
- # which will find the component in question.
- #
- # myContainer = new Luca.Container
- # componentEvents:
- # "name component:trigger" : "handler"
- # "role component:trigger" : "handler"
- # "getter component:trigger" : "handler"
- #
+ # where component_accessor is either the name of the component, or a the role
+ # property on the component, component:trigger is the event that component fires.
+ # handler is a method on the container which will respond to the child component event.
+ # <pre>
+ # myContainer = new Luca.Container
+ # componentEvents:
+ # "name component:trigger" : "handler"
+ # "role component:trigger" : "handler"
+ # "getter component:trigger" : "handler"
+ # components:[
+ # name: "name"
+ # ]
+ # </pre>
componentEvents: {}
+container.privateConfiguration
+ className: 'luca-ui-container'
+
+ # This is a convenience attribute for identifying
+ # views which are luca containers
+ isContainer: true
+
+ # if set to true, we will generate DOM elements
+ # to wrap each of our components in. This should
+ # generally be avoided IMO as it pollutes the DOM,
+ # but is currently necessary for some container implementations
+ generateComponentElements: false
+
+ # if set to true, the DOM elements which wrap
+ # our components will be emptied prior to rendering
+ # the component inside this container.
+ emptyContainerElements: false
+
+ # if @generateComponentElements is true, which tag should this
+ # container wrap our components in?
+ componentTag: 'div'
+
+ # if @generateComponentElements is true, which class should we
+ # apply to the container elements which wrap our components?
+ componentClass: 'luca-ui-panel'
+
+ rendered: false
+
+
+
+container.privateMethods
initialize: (@options={})->
_.extend @, @options
# aliases for the components property
@components ||= @fields ||= @pages ||= @cards ||= @views
@@ -197,10 +262,11 @@
validateContainerConfiguration(@)
Luca.View::initialize.apply @, arguments
+ # Removing a container will call remove on all of the nested components as well.
remove: ()->
Luca.View::remove.apply(@, arguments)
@eachComponent (component)->
component.remove?()
@@ -342,12 +408,11 @@
@componentsCreated = true
map
- # Trigger the Rendering Pipeline process on all of the nested
- # components
+ # Trigger the Rendering Pipeline process on all of the nested components
renderComponents: (@debugMode="")->
@debug "container render components"
container = @
@@ -381,12 +446,10 @@
console.log e.message
console.log e.stack
throw e unless Luca.silenceRenderErrors? is true
- #### Container Activation
- #
# When a container is first activated is a good time to perform
# operations which are not needed unless that component becomes
# visible. This first activation event should be relayed to all
# of the nested components. Components which hide / display
# other components, such as a CardView or TabContainer
@@ -399,31 +462,10 @@
# passing as arguments the component itself, and the component doing the activation
unless component?.previously_activated is true
component?.trigger?.call component, "first:activation", component, activator
component.previously_activated = true
- #### Underscore Methods For Working with Components
- _: ()-> _( @components )
-
- pluck: (attribute)->
- @_().pluck(attribute)
-
- invoke: (method)->
- @_().invoke(method)
-
- select: (fn)->
- @_().select(fn)
-
- detect: (fn)->
- @_().detect(attribute)
-
- reject: (fn)->
- @_().reject(fn)
-
- map: (fn)->
- @_().map(fn)
-
registerComponentEvents: (eventList, direction="on")->
container = @
for listener, handler of (eventList || @componentEvents||{})
[componentNameOrRole,eventId] = listener.split(' ')
@@ -441,26 +483,59 @@
console.log "Error registering component event", listener, componentNameOrRole, eventId
throw "Invalid component event definition: #{ componentNameOrRole }"
component[direction](eventId, @[handler], container)
+container.publicMethods
+ # Returns an underscore.js object that wraps the components array
+ _: ()-> _( @components )
+ # Return the value of attribute of each component
+ pluck: (attribute)->
+ @_().pluck(attribute)
+
+ # Invoke the passed method name on each component
+ invoke: (method)->
+ @_().invoke(method)
+
+ # Select any component for which the passed iterator returns true
+ select: (iterator)->
+ @_().select(iterator)
+
+ # Find the first matching component for which the passed iterator returns true
+ detect: (iterator)->
+ @_().detect(iterator)
+
+ # Return a list of components without the components for which the passed iterator returns true
+ reject: (iterator)->
+ @_().reject(iterator)
+
+ # Run the passed iterator over each component and return the result in an array
+ map: (fn)->
+ @_().map(fn)
+
+ # Returns a list of nested components which are also containers
subContainers: ()->
@select (component)->
component.isContainer is true
roles: ()->
- _( @allChildren() ).pluck('role')
+ _( @allChildren() ).chain().pluck('role').compact().value()
allChildren: ()->
children = @components
grandchildren = _( @subContainers() ).map (component)->
component?.allChildren?()
_([children,grandchildren]).chain().compact().flatten().value()
+ # Find a direct component on this card by its name.
+ find: (name)->
+ _( @components ).detect (c)->
+ c.name is name
+
findComponentForEventBinding: (nameRoleOrGetter, deep=true)->
@findComponentByName(nameRoleOrGetter, deep) || @findComponentByGetter( nameRoleOrGetter, deep ) || @findComponentByRole( nameRoleOrGetter, deep )
findComponentByGetter: (getter, deep=false)->
_( @allChildren() ).detect (component)->
@@ -504,14 +579,21 @@
eachComponent: (fn, deep=true)->
_( @components ).each (component, index)=>
fn.call component, component, index
component?.eachComponent?.apply component, [fn,deep] if deep
- indexOf: (name)->
+ indexOfComponentName: (name)->
names = _( @components ).pluck('name')
_( names ).indexOf(name)
+ indexOf: (nameOrComponent)->
+ if _.isString(nameOrComponent)
+ return @indexOfComponentName(nameOrComponent)
+
+ if _.isObject(nameOrComponent)
+ _( @components ).indexOf( nameOrComponent )
+
activeComponent: ()->
return @ unless @activeItem
return @components[ @activeItem ]
componentElements: ()->
@@ -615,11 +697,9 @@
@registerComponentEvents()
validateContainerConfiguration = ()->
true
-# Private Helpers
-#
# indexComponent( component ).at( index ).in( componentsInternalIndexMap )
indexComponent = (component)->
at: (index)->
in: (map)->
if component.cid?