Skip to content

Commit

Permalink
Adds portalElement config property to Component. Closes #330
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert-Frampton authored and Robert-Frampton committed Jan 5, 2018
1 parent 592c9b0 commit ecdb177
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 3 deletions.
40 changes: 40 additions & 0 deletions packages/metal-component/src/Component.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ class Component extends EventEmitter {
*/
this.initialConfig_ = config || {};

/**
* Indicates whether the component should be rendered as a Portal, outside
* of the parent component.
* @type {string|Element|boolean}
*/
this.portalElement = null;

/**
* Whether the element was rendered.
* @type {boolean}
Expand All @@ -154,6 +161,8 @@ class Component extends EventEmitter {
this.setUpDataManager_();
this.setUpSyncUpdates_();

this.setUpPortal_(this.initialConfig_.portalElement);

this.on('stateWillChange', this.handleStateWillChange_);
this.on('stateChanged', this.handleComponentStateChanged_);
this.on('eventsChanged', this.onEventsChanged_);
Expand Down Expand Up @@ -658,6 +667,37 @@ class Component extends EventEmitter {
);
}

/**
* Overwrites element property if portalElement is passed. Creates
* a nested placeholder so that portalElement is not removed from the
* DOM when component first renders. When portalElement is equal to true,
* component is appeneded to the body.
*
* @param {string|Element|boolean} portalElement
*/
setUpPortal_(portalElement) {
if (
!isElement(portalElement) &&
!isString(portalElement) &&
!isBoolean(portalElement)
) {
return;
} else if (isBoolean(portalElement) && portalElement) {
portalElement = 'body';
}

portalElement = toElement(portalElement);

if (portalElement) {
const placeholder = document.createElement('div');

portalElement.appendChild(placeholder);

this.element = placeholder;
this.portalElement = portalElement;
}
}

/**
* Sets up the component's renderer.
* @protected
Expand Down
4 changes: 3 additions & 1 deletion packages/metal-incremental-dom/src/IncrementalDomRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ class IncrementalDomRenderer extends ComponentRenderer.constructor {
for (let i = 0; i < data.childComponents.length; i++) {
const child = data.childComponents[i];
if (!child.isDisposed()) {
child.element = null;
if (!child.portalElement) {
child.element = null;
}
child.dispose();
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/metal-incremental-dom/src/cleanup/unused.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export function disposeUnused() {
if (!comp.isDisposed() && !getData(comp).parent) {
// Don't let disposing cause the element to be removed, since it may
// be currently being reused by another component.
comp.element = null;
if (!comp.portalElement) {
comp.element = null;
}
comp.dispose();
}
}
Expand Down
5 changes: 4 additions & 1 deletion packages/metal-incremental-dom/src/render/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,10 @@ function renderSubComponent_(tagOrCtor, config, owner) {
config.key = parentData.config.key;
}

comp.getRenderer().renderInsidePatch(comp);
if (!comp.portalElement) {
comp.getRenderer().renderInsidePatch(comp);
}

if (!comp.wasRendered) {
comp.renderComponent();
}
Expand Down

0 comments on commit ecdb177

Please sign in to comment.