-
Notifications
You must be signed in to change notification settings - Fork 7
How Overlays Work
Four components are used:
- ember-wormhole
- overlay-animate
- overlay-fullscreen-layout
- overlay-fullscreen
Breaking responsibilities out into small components will allow us to make changes where needed for the special snowflake overlays.
A third-party component that renders content outside of the local template context. This allows us to put overlays at the root of the DOM tree and worry less about z-index, etc. This is similar to how we were rendering overlays into an outlet at the root: `{{outlet "overlay"}}`
Component that just animates overlays in and out. It uses Velocity.js and maybe it's just me but the frame rate seems higher.
This component is (mostly) for styling. Wraps fullscreen overlay markup and styles around a child context. In the future we could have `overlay-card-layout`, `task-accordion-layout`, etc.
Convenience wrapper. Brings together `ember-wormhole`, `overlay-animate` and `overlay-fullscreen-layout`. Responsible for adding/removing them from the DOM.
Chunks of functionality are simply components. At most they only need to know how to `close`. I'm hoping this will help in many ways like: reduce markup, make testing easier, make future development easier, etc. Some bugs/hacks/data-cleanup can be avoided because we are no longer using controllers which are singletons. And bonus: one step closer to Ember 2.0.
/*
-- controller.js --
Before, overlay display was managed at the top application route level.
While that allowed for simple action calls: `showOverlay` and `closeOverlay`,
people found the action bubbling confusing and harder to debug.
While moving previous overlays to this new style I found having a much more
local scope helped with that issue. Also, no more odd controller lookups for
setting data; you just set properties like any other component (see template).
*/
showMyOverlay: false,
actions: {
showMyOverlay() {
this.set('showMyOverlay', true);
}
hideMyOverlay() {
this.set('showMyOverlay', false);
}
}
{{!-- template.hbs --}}
{{#overlay-fullscreen visible=showMyOverlay
outAnimationComplete=(action "hideMyOverlay")
title="My Stuff"
overlayClass="my-stuff-overlay"
as |overlay|}}
{{my-stuff close=(action overlay.animateOut)}}
{{/overlay-fullscreen}}
The lifecycle should go like this:
- User somehow triggers `showMyOverlay` in the controller
- `overlay-fullscreen` `visible` property is changed, rendering everything in its template
- `ember-wormole` renders `animate-overlay` with the component at the DOM root
- `animate-overlay` animates in the component
- ...
- User somehow triggers `close` action (save button, escape key, click "X", etc.)
- `animate-overlay` animates out the component
- `animate-overlay` calls `outAnimationComplete` callback
- This callback from the controller toggles a property
- `overlay-fullscreen` `visible` property is changed, removing everything in its template from the DOM
All the nesting ends up looking like this:
{{#overlay-fullscreen}}
{{#if visible}}
{{#ember-wormhole}}
{{#overlay-animate}}
{{#overlay-fullscreen-layout}}
{{my-stuff}}
{{/overlay-fullscreen-layout}}
{{/overlay-animate}}
{{/ember-wormhole}}
{{/if}}
{{/overlay-fullscreen}
Sometimes overlays need data. I think the best practice should be: use controllers to load data and pass that into the component. Keep the components as dumb as possible. There are a couple components in this PR that lookup the DS.Store and load data themselves.
/* -- controller.js -- */
showMyOverlay: false,
stuffIsLoading: false,
stuff: [],
actions: {
showMyOverlay() {
this.setProperties({
stuffIsLoading: true,
showMyOverlay: true
});
this.doAjax().then(stuff => {
this.setProperties({
stuffIsLoading: false,
stuff: stuff
});
});
}
hideMyOverlay() {
this.set('showMyOverlay', false);
}
}
{{!-- template.hbs --}}
{{#overlay-fullscreen visible=showMyOverlay
outAnimationComplete=(action "hideMyOverlay")
title="My Stuff"
overlayClass="my-stuff-overlay"
as |overlay|}}
{{my-stuff isLoading=stuffIsLoading
data=stuff
close=(action overlay.animateOut)}}
{{/overlay-fullscreen}}