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?