mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-11-10 00:11:34 +08:00
133 lines
4.9 KiB
CoffeeScript
133 lines
4.9 KiB
CoffeeScript
React = require 'react'
|
|
_ = require 'underscore'
|
|
UnsafeComponent = require './unsafe-component'
|
|
Flexbox = require './flexbox'
|
|
InjectedComponentLabel = require './injected-component-label'
|
|
{Utils,
|
|
Actions,
|
|
WorkspaceStore,
|
|
ComponentRegistry} = require "nylas-exports"
|
|
|
|
|
|
###
|
|
Public: InjectedComponent makes it easy to include a set of dynamically registered
|
|
components inside of your React render method. Rather than explicitly render
|
|
an array of buttons, for example, you can use InjectedComponentSet:
|
|
|
|
```coffee
|
|
<InjectedComponentSet className="message-actions"
|
|
matching={role: 'ThreadActionButton'}
|
|
exposedProps={thread:@props.thread, message:@props.message}>
|
|
```
|
|
|
|
InjectedComponentSet will look up components registered for the location you provide,
|
|
render them inside a {Flexbox} and pass them `exposedProps`. By default, all injected
|
|
children are rendered inside {UnsafeComponent} wrappers to prevent third-party code
|
|
from throwing exceptions that break React renders.
|
|
|
|
InjectedComponentSet monitors the ComponentRegistry for changes. If a new component
|
|
is registered into the location you provide, InjectedComponentSet will re-render.
|
|
|
|
If no matching components is found, the InjectedComponent renders an empty span.
|
|
|
|
Section: Component Kit
|
|
###
|
|
class InjectedComponentSet extends React.Component
|
|
@displayName: 'InjectedComponentSet'
|
|
|
|
###
|
|
Public: React `props` supported by InjectedComponentSet:
|
|
|
|
- `matching` Pass an {Object} with ComponentRegistry descriptors
|
|
This set of descriptors is provided to {ComponentRegistry::findComponentsForDescriptor}
|
|
to retrieve components for display.
|
|
- `matchLimit` (optional) A {Number} that indicates the max number of matching elements to render
|
|
- `className` (optional) A {String} class name for the containing element.
|
|
- `children` (optional) Any React elements rendered inside the InjectedComponentSet
|
|
will always be displayed.
|
|
- `onComponentsDidRender` Callback that will be called when the injected component set
|
|
is successfully rendered onto the DOM.
|
|
- `exposedProps` (optional) An {Object} with props that will be passed to each
|
|
item rendered into the set.
|
|
- `containersRequired` (optional). Pass false to optionally remove the containers
|
|
placed around injected components to isolate them from the rest of the app.
|
|
|
|
- Any other props you provide, such as `direction`, `data-column`, etc.
|
|
will be applied to the {Flexbox} rendered by the InjectedComponentSet.
|
|
###
|
|
@propTypes:
|
|
matching: React.PropTypes.object.isRequired
|
|
children: React.PropTypes.array
|
|
className: React.PropTypes.string
|
|
matchLimit: React.PropTypes.number
|
|
exposedProps: React.PropTypes.object
|
|
containersRequired: React.PropTypes.bool
|
|
onComponentsDidRender: React.PropTypes.func
|
|
|
|
@defaultProps:
|
|
direction: 'row'
|
|
containersRequired: true
|
|
onComponentsDidRender: ->
|
|
|
|
constructor: (@props) ->
|
|
@state = @_getStateFromStores()
|
|
@_renderedComponents = new Set()
|
|
|
|
componentDidMount: =>
|
|
@_componentUnlistener = ComponentRegistry.listen =>
|
|
@setState(@_getStateFromStores())
|
|
@props.onComponentsDidRender() if @props.containersRequired is false
|
|
|
|
componentWillUnmount: =>
|
|
@_componentUnlistener() if @_componentUnlistener
|
|
|
|
componentWillReceiveProps: (newProps) =>
|
|
if newProps.location isnt @props?.location
|
|
@setState(@_getStateFromStores(newProps))
|
|
|
|
componentDidUpdate: =>
|
|
@props.onComponentsDidRender() if @props.containersRequired is false
|
|
|
|
render: =>
|
|
@_renderedComponents = new Set()
|
|
flexboxProps = Utils.fastOmit(@props, Object.keys(@constructor.propTypes))
|
|
flexboxClassName = @props.className ? ""
|
|
exposedProps = @props.exposedProps ? {}
|
|
|
|
elements = @state.components.map (component) =>
|
|
if @props.containersRequired is false or component.containerRequired is false
|
|
return <component key={component.displayName} {...exposedProps} />
|
|
else
|
|
return (
|
|
<UnsafeComponent
|
|
key={component.displayName}
|
|
component={component}
|
|
onComponentDidRender={@_onComponentDidRender.bind(@, component.displayName)}
|
|
{...exposedProps} />
|
|
)
|
|
|
|
|
|
if @state.visible
|
|
flexboxClassName += " registered-region-visible"
|
|
elements.splice(0,0, <InjectedComponentLabel key="_label" matching={@props.matching} {...exposedProps} />)
|
|
elements.push(<span key="_clear" style={clear:'both'}/>)
|
|
|
|
<Flexbox className={flexboxClassName} {...flexboxProps}>
|
|
{elements}
|
|
{@props.children ? []}
|
|
</Flexbox>
|
|
|
|
_onComponentDidRender: (componentName) =>
|
|
@_renderedComponents.add(componentName)
|
|
if @_renderedComponents.size is @state.components.length
|
|
@props.onComponentsDidRender()
|
|
|
|
_getStateFromStores: (props) =>
|
|
props ?= @props
|
|
limit = props.matchLimit
|
|
|
|
components: ComponentRegistry.findComponentsMatching(@props.matching)[...limit]
|
|
visible: ComponentRegistry.showComponentRegions()
|
|
|
|
|
|
module.exports = InjectedComponentSet
|