Skip to content

Instantly share code, notes, and snippets.

@andrejewski
Created February 21, 2018 15:20
Show Gist options
  • Select an option

  • Save andrejewski/429b9c43c0b8916fcabb490b26fc6bf1 to your computer and use it in GitHub Desktop.

Select an option

Save andrejewski/429b9c43c0b8916fcabb490b26fc6bf1 to your computer and use it in GitHub Desktop.
Portals for Can.js (V2)
import Map from 'can/map/';
import Component from 'can/component/';
// Range methods from https://github.com/yapplabs/ember-wormhole/blob/f6bdea79ee1d9b3ef6b7f19e378e3bfb103b2e5e/addon/components/ember-wormhole.js
function appendRange (destinationElement, firstNode, lastNode) {
while(firstNode) {
destinationElement.insertBefore(firstNode, null);
firstNode = firstNode !== lastNode ? lastNode.parentNode.firstChild : null;
}
}
function removeRange (firstNode, lastNode) {
var node = lastNode;
do {
var next = node.previousSibling;
if (node.parentNode) {
node.parentNode.removeChild(node);
if (node === firstNode) {
break;
}
}
node = next;
} while (node);
}
export const viewModel = Map.extend('PortalVM', {
componentDidMount (element) {
const destinationElementId = this.attr('destination');
let destinationElement = document.getElementById(destinationElementId);
if (!destinationElement) {
if (this.attr('renderInRoot')) {
destinationElement = document.createElement('div');
destinationElement.className = 'can-portal-outlet';
document.body.appendChild(destinationElement);
this._rootElement = destinationElement;
} else {
return; // no-op; render in place
}
}
this._portalHeadNode = document.createTextNode('');
this._portalTailNode = document.createTextNode('');
element.insertBefore(this._portalHeadNode, element.firstChild);
element.appendChild(this._portalTailNode);
appendRange(destinationElement, this._portalHeadNode, this._portalTailNode);
},
componentDidUnmount () {
removeRange(this._portalHeadNode, this._portalTailNode);
if (this._rootElement) {
document.body.removeChild(this._rootElement);
}
}
});
export default Component.extend({
tag: 'can-portal',
viewModel,
template: '<content/>',
events: {
inserted () {
this.viewModel.componentDidMount(this.element[0]);
},
removed () {
this.viewModel.componentDidUnmount(this.element[0]);
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment