Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

remount

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

remount - npm Package Compare versions

Comparing version 0.5.0 to 0.7.0

12

CHANGELOG.md
## Pre-v1.0.0 changes
[v0.7.0]
- Significant refactor, and support MutationObserver fallback. This means IE9 support (hopefully)! :tada:
[v0.7.0]: https://github.com/rstacruz/remount/compare/v0.6.0...v0.7.0
[v0.6.0]
- Fix `remount/esm` module.
[v0.6.0]: https://github.com/rstacruz/remount/compare/v0.5.0...v0.6.0
[v0.5.0]

@@ -4,0 +16,0 @@

286

dist/remount.es5.js

@@ -9,6 +9,29 @@ (function (global, factory) {

/* global HTMLElement */
var injected = void 0;
/*
* Adapted from https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.0.4/custom-elements-es5-adapter.js
* Rolling this in so we don't need another polyfill.
*/
function inject() {
if (injected || void 0 === window.Reflect || void 0 === window.customElements || window.customElements.hasOwnProperty('polyfillWrapFlushCallback')) {
return;
}
var a = HTMLElement;
window.HTMLElement = function () {
return Reflect.construct(a, [], this.constructor);
};
HTMLElement.prototype = a.prototype;
HTMLElement.prototype.constructor = HTMLElement;
Object.setPrototypeOf(HTMLElement, a);
injected = true;
}
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

@@ -23,52 +46,39 @@

/*::
export type Component = React.ComponentType<{}>
export type PropertyMap = {
[string]: ?string
}
export type ElementMap = {
[string]: ElementSpec | Component
}
export type Defaults = {
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
}
export type ElementSpec = {
component: Component,
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
}
import type {
Component,
PropertyMap,
ElementMap,
Defaults,
ElementSpec,
ReactAdapter,
ElementEvents
} from './types'
*/
/**
* Registers elements.
* Registers a custom element.
*
* This creates a custom element (ie, a subclass of `window.HTMLElement`) and
* registers it (ie, `window.customElements.define`).
*
* Events will be triggered when something interesting happens.
*
* @example
* defineElement(
* { component: Tooltip },
* 'x-tooltip',
* { onUpdate, onUnmount }
* )
*
* @private
*/
function define(components /*: ElementMap */
, defaults /*: ?Defaults */
function defineElement(elSpec /*: ElementSpec */
, name /*: string */
, _ref /*: ElementEvents */
) {
Object.keys(components).forEach(function (name /*: string */) {
var elSpec /*: ElementSpec */ = toElementSpec(components[name]);
defineOne(Object.assign({}, defaults, elSpec), name);
});
}
var onUpdate = _ref.onUpdate,
onUnmount = _ref.onUnmount;
function toElementSpec(thing /*: ElementSpec | Component */
) /*: ElementSpec */{
// $FlowFixMe$
if ((typeof thing === 'undefined' ? 'undefined' : _typeof(thing)) === 'object' && thing.component) return thing;
return { component: thing };
}
/**
* Registers one element.
* @private
*/
function defineOne(elSpec /*: ElementSpec */, name /*: string */) {
inject();
var attributes = elSpec.attributes || [];

@@ -89,3 +99,3 @@

this._mountPoint = createMountPoint(this, elSpec);
update(this, elSpec, this._mountPoint);
onUpdate(this, this._mountPoint);
}

@@ -96,3 +106,3 @@ }, {

if (!this._mountPoint) return;
ReactDOM.unmountComponentAtNode(this._mountPoint);
onUnmount(this, this._mountPoint);
}

@@ -103,3 +113,3 @@ }, {

if (!this._mountPoint) return;
update(this, elSpec, this._mountPoint);
onUpdate(this, this._mountPoint);
}

@@ -116,5 +126,5 @@ }], [{

if (!ensureSupported()) return;
// Supress warning when quiet mode is on
// Supress warning when quiet mode is on
if (elSpec.quiet && window.customElements.get(name)) {

@@ -127,14 +137,4 @@ return;

/**
* Ensures that custom elements are supported
* @private
*/
function ensureSupported() {
if (!window.customElements || !window.customElements.define) {
console.error("remount: Custom elements aren't support in this browser. " + 'Remount will not work. ' + 'Including polyfills will likely fix this. ' + 'See Remount documentation for more info: ' + 'https://github.com/rstacruz/remount');
return false;
}
return true;
function isSupported() {
return window.customElements && window.customElements.define;
}

@@ -144,3 +144,3 @@

* Creates a `<span>` element that serves as the mounting point for React
* components.
* components. If `shadow: true` is requested, it'll attach a shadow node.
* @private

@@ -150,5 +150,5 @@ */

function createMountPoint(element /*: Element */
, _ref /*: ElementSpec */
, _ref2 /*: ElementSpec */
) {
var shadow = _ref.shadow;
var shadow = _ref2.shadow;

@@ -164,2 +164,55 @@ if (shadow) {

var name = 'CustomElements';
var ElementsAdapter = /*#__PURE__*/Object.freeze({
defineElement: defineElement,
isSupported: isSupported,
name: name
});
function isSupported$1() {
return !!window.MutationObserver;
}
function defineElement$1(elSpec, name, _ref) {
var onUpdate = _ref.onUpdate,
onUnmount = _ref.onUnmount;
name = name.toLowerCase();
var observer = new window.MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeName.toLowerCase() !== name) return;
onUpdate(node, node);
});
// todo handle update
mutation.removedNodes.forEach(function (node) {
if (node.nodeName.toLowerCase() !== name) return;
onUnmount(node, node);
});
});
});
observer.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});
}
var name$1 = 'MutationObserver';
var MutationAdapter = /*#__PURE__*/Object.freeze({
isSupported: isSupported$1,
defineElement: defineElement$1,
name: name$1
});
/*::
import type { ElementSpec } from './types'
*/
/**

@@ -170,13 +223,10 @@ * Updates a custom element by calling `ReactDOM.render()`.

function update(element /* Element */
, _ref2 /*: ElementSpec */
, mountPoint /* Element */
function update(_ref /*: ElementSpec */
, mountPoint /*: Element */
, props /*: {} */
) {
var component = _ref2.component,
attributes = _ref2.attributes;
var component = _ref.component,
attributes = _ref.attributes;
var props = element.hasAttribute('props-json') ? JSON.parse(element.getAttribute('props-json')) : getProps(element, attributes);
var reactElement = React.createElement(component, props);
ReactDOM.render(reactElement, mountPoint);

@@ -186,7 +236,93 @@ }

/**
* Unmounts a component.
* @private
*/
function unmount(_ /*: any */, mountPoint /*: Element */) {
ReactDOM.unmountComponentAtNode(mountPoint);
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
/*::
import type {
Component,
PropertyMap,
ElementMap,
Defaults,
ElementSpec
} from './lib/types'
*/
var Adapter = isSupported() ? ElementsAdapter : isSupported$1() ? MutationAdapter : null;
if (!Adapter) {
throw new Error('Unsupported platform');
} else {
console.log('Remount: using adapter', Adapter.name);
}
/**
* Inspect `Remount.adapterName` to see what adapter's being used.
*/
var adapterName = Adapter.name;
/**
* Registers elements.
*/
function define(components /*: ElementMap */
, defaults /*: ?Defaults */
) {
Object.keys(components).forEach(function (name$$1 /*: string */) {
// Construct the specs for the element.
// (eg, { component: Tooltip, attributes: ['title'] })
var elSpec /*: ElementSpec */ = Object.assign({}, defaults, toElementSpec(components[name$$1]));
// Define a custom element.
Adapter.defineElement(elSpec, name$$1, {
onUpdate: function onUpdate(element /*: Element */, mountPoint /*: Element */) {
var props = getProps(element, elSpec.attributes);
update(elSpec, mountPoint, props);
},
onUnmount: function onUnmount(element /*: Element */, mountPoint /*: Element */) {
unmount(elSpec, mountPoint);
}
});
});
}
/**
* Coerces something into an `ElementSpec` type.
* @private
*
* @example
* toElementSpec(Tooltip)
* // => { component: Tooltip }
*
* toElementSpec({ component: Tooltip })
* // => { component: Tooltip }
*/
function toElementSpec(thing /*: ElementSpec | Component */
) /*: ElementSpec */{
// $FlowFixMe$
if ((typeof thing === 'undefined' ? 'undefined' : _typeof(thing)) === 'object' && thing.component) return thing;
return { component: thing };
}
/**
* Returns properties for a given HTML element.
* @private
*
* @example
* getProps(div, ['name'])
* // => { name: 'Romeo' }
*/
function getProps(element /*: Element */, attributes /*: ?Array<string> */) {
var rawJson = element.getAttribute('props-json');
if (rawJson) return JSON.parse(rawJson);
var names /*: Array<string> */ = attributes || [];

@@ -197,7 +333,5 @@ return names.reduce(function (result /*: PropertyMap */, attribute /*: string */) {

}, {});
// By the way, did you know el.getAttributeNames()
// will not work in IE11? Now you do.
}
exports.adapterName = adapterName;
exports.define = define;

@@ -204,0 +338,0 @@

@@ -1,1 +0,1 @@

(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports,require('react'),require('react-dom')):'function'==typeof define&&define.amd?define(['exports','react','react-dom'],b):b(a.Remount={},a.React,a.ReactDOM)})(this,function(a,b,c){'use strict';function d(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b<a.length;b++)c[b]=a[b];return c}return Array.from(a)}function e(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function f(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function g(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function h(a){return'object'===('undefined'==typeof a?'undefined':o(a))&&a.component?a:{component:a}}function i(a,b){var h=a.attributes||[],i=function(b){function i(){return e(this,i),f(this,(i.__proto__||Object.getPrototypeOf(i)).apply(this,arguments))}return g(i,b),n(i,[{key:'connectedCallback',value:function(){this._mountPoint=k(this,a),l(this,a,this._mountPoint)}},{key:'disconnectedCallback',value:function(){this._mountPoint&&c.unmountComponentAtNode(this._mountPoint)}},{key:'attributeChangedCallback',value:function(){this._mountPoint&&l(this,a,this._mountPoint)}}],[{key:'observedAttributes',get:function(){return['props-json'].concat(d(h))}}]),i}(window.HTMLElement);!j()||a.quiet&&window.customElements.get(b)||window.customElements.define(b,i)}function j(){return!!(window.customElements&&window.customElements.define)||(console.error('remount: Custom elements aren\'t support in this browser. Remount will not work. Including polyfills will likely fix this. See Remount documentation for more info: https://github.com/rstacruz/remount'),!1)}function k(a,b){var c=b.shadow;if(c){var d=document.createElement('span');return a.attachShadow({mode:'open'}).appendChild(d),d}return a}function l(a,d,e){var f=d.component,g=d.attributes,h=a.hasAttribute('props-json')?JSON.parse(a.getAttribute('props-json')):m(a,g),i=b.createElement(f,h);c.render(i,e)}function m(a,b){return(b||[]).reduce(function(b,c){return b[c]=a.getAttribute(c),b},{})}c=c&&c.hasOwnProperty('default')?c['default']:c;var n=function(){function a(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,'value'in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),o='function'==typeof Symbol&&'symbol'==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&'function'==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?'symbol':typeof a};a.define=function(a,b){Object.keys(a).forEach(function(c){var d=h(a[c]);i(Object.assign({},b,d),c)})},Object.defineProperty(a,'__esModule',{value:!0})});
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports,require('react'),require('react-dom')):'function'==typeof define&&define.amd?define(['exports','react','react-dom'],b):b(a.Remount={},a.React,a.ReactDOM)})(this,function(a,b,c){'use strict';function d(){if(!(p||void 0===window.Reflect||void 0===window.customElements||window.customElements.hasOwnProperty('polyfillWrapFlushCallback'))){var b=HTMLElement;window.HTMLElement=function(){return Reflect.construct(b,[],this.constructor)},HTMLElement.prototype=b.prototype,HTMLElement.prototype.constructor=HTMLElement,Object.setPrototypeOf(HTMLElement,b),p=!0}}function e(a){if(Array.isArray(a)){for(var b=0,c=Array(a.length);b<a.length;b++)c[b]=a[b];return c}return Array.from(a)}function f(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function g(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function h(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}function i(){return window.customElements&&window.customElements.define}function j(a,b){var c=b.shadow;if(c){var d=document.createElement('span');return a.attachShadow({mode:'open'}).appendChild(d),d}return a}function k(){return!!window.MutationObserver}function l(a,d,e){var f=a.component,g=a.attributes,h=b.createElement(f,e);c.render(h,d)}function m(a,b){c.unmountComponentAtNode(b)}function n(a){return'object'===('undefined'==typeof a?'undefined':t(a))&&a.component?a:{component:a}}function o(a,b){var c=a.getAttribute('props-json');if(c)return JSON.parse(c);return(b||[]).reduce(function(b,c){return b[c]=a.getAttribute(c),b},{})}c=c&&c.hasOwnProperty('default')?c['default']:c;var p=void 0,q=function(){function a(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,'value'in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}(),r=Object.freeze({defineElement:function(a,b,c){var i=c.onUpdate,k=c.onUnmount;d();var l=a.attributes||[],m=function(b){function c(){return f(this,c),g(this,(c.__proto__||Object.getPrototypeOf(c)).apply(this,arguments))}return h(c,b),q(c,[{key:'connectedCallback',value:function(){this._mountPoint=j(this,a),i(this,this._mountPoint)}},{key:'disconnectedCallback',value:function(){this._mountPoint&&k(this,this._mountPoint)}},{key:'attributeChangedCallback',value:function(){this._mountPoint&&i(this,this._mountPoint)}}],[{key:'observedAttributes',get:function(){return['props-json'].concat(e(l))}}]),c}(window.HTMLElement);a.quiet&&window.customElements.get(b)||window.customElements.define(b,m)},isSupported:i,name:'CustomElements'}),s=Object.freeze({isSupported:k,defineElement:function(a,b,c){var d=c.onUpdate,e=c.onUnmount;b=b.toLowerCase();var f=new window.MutationObserver(function(a){a.forEach(function(a){a.addedNodes.forEach(function(a){a.nodeName.toLowerCase()!==b||d(a,a)}),a.removedNodes.forEach(function(a){a.nodeName.toLowerCase()!==b||e(a,a)})})});f.observe(document.body,{attributes:!0,childList:!0,subtree:!0})},name:'MutationObserver'}),t='function'==typeof Symbol&&'symbol'==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&'function'==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?'symbol':typeof a},u=i()?r:k()?s:null;if(!u)throw new Error('Unsupported platform');else console.log('Remount: using adapter',u.name);var v=u.name;a.adapterName=v,a.define=function(a,b){Object.keys(a).forEach(function(c){var d=Object.assign({},b,n(a[c]));u.defineElement(d,c,{onUpdate:function(a,b){var c=o(a,d.attributes);l(d,b,c)},onUnmount:function(a,b){m(d,b)}})})},Object.defineProperty(a,'__esModule',{value:!0})});

@@ -9,57 +9,70 @@ (function (global, factory) {

// @flow
/* global HTMLElement */
/*::
export type Component = React.ComponentType<{}>
let injected;
export type PropertyMap = {
[string]: ?string
}
/*
* Adapted from https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.0.4/custom-elements-es5-adapter.js
* Rolling this in so we don't need another polyfill.
*/
export type ElementMap = {
[string]: ElementSpec | Component
}
function inject () {
if (
injected ||
void 0 === window.Reflect ||
void 0 === window.customElements ||
window.customElements.hasOwnProperty('polyfillWrapFlushCallback')
) {
return
}
const a = HTMLElement;
export type Defaults = {
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
}
window.HTMLElement = function () {
return Reflect.construct(a, [], this.constructor)
};
export type ElementSpec = {
component: Component,
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
HTMLElement.prototype = a.prototype;
HTMLElement.prototype.constructor = HTMLElement;
Object.setPrototypeOf(HTMLElement, a);
injected = true;
}
*/
/**
* Registers elements.
*/
// @flow
function define (
components /*: ElementMap */,
defaults /*: ?Defaults */
) {
Object.keys(components).forEach((name /*: string */) => {
const elSpec /*: ElementSpec */ = toElementSpec(components[name]);
defineOne(Object.assign({}, defaults, elSpec), name);
});
}
/*::
import type {
Component,
PropertyMap,
ElementMap,
Defaults,
ElementSpec,
ReactAdapter,
ElementEvents
} from './types'
*/
function toElementSpec (
thing /*: ElementSpec | Component */
) /*: ElementSpec */ {
// $FlowFixMe$
if (typeof thing === 'object' && thing.component) return thing
return { component: thing }
}
/**
* Registers one element.
* Registers a custom element.
*
* This creates a custom element (ie, a subclass of `window.HTMLElement`) and
* registers it (ie, `window.customElements.define`).
*
* Events will be triggered when something interesting happens.
*
* @example
* defineElement(
* { component: Tooltip },
* 'x-tooltip',
* { onUpdate, onUnmount }
* )
*
* @private
*/
function defineOne (elSpec /*: ElementSpec */, name /*: string */) {
function defineElement (
elSpec /*: ElementSpec */,
name /*: string */,
{ onUpdate, onUnmount } /*: ElementEvents */
) {
inject();
const attributes = elSpec.attributes || [];

@@ -74,3 +87,3 @@

this._mountPoint = createMountPoint(this, elSpec);
update(this, elSpec, this._mountPoint);
onUpdate(this, this._mountPoint);
}

@@ -80,3 +93,3 @@

if (!this._mountPoint) return
ReactDOM.unmountComponentAtNode(this._mountPoint);
onUnmount(this, this._mountPoint);
}

@@ -86,8 +99,6 @@

if (!this._mountPoint) return
update(this, elSpec, this._mountPoint);
onUpdate(this, this._mountPoint);
}
}
if (!ensureSupported()) return
// Supress warning when quiet mode is on

@@ -101,20 +112,4 @@ if (elSpec.quiet && window.customElements.get(name)) {

/**
* Ensures that custom elements are supported
* @private
*/
function ensureSupported () {
if (!window.customElements || !window.customElements.define) {
console.error(
"remount: Custom elements aren't support in this browser. " +
'Remount will not work. ' +
'Including polyfills will likely fix this. ' +
'See Remount documentation for more info: ' +
'https://github.com/rstacruz/remount'
);
return false
}
return true
function isSupported () {
return window.customElements && window.customElements.define
}

@@ -124,3 +119,3 @@

* Creates a `<span>` element that serves as the mounting point for React
* components.
* components. If `shadow: true` is requested, it'll attach a shadow node.
* @private

@@ -142,2 +137,54 @@ */

const name = 'CustomElements';
var ElementsAdapter = /*#__PURE__*/Object.freeze({
defineElement: defineElement,
isSupported: isSupported,
name: name
});
function isSupported$1 () {
return !!window.MutationObserver
}
function defineElement$1 (elSpec, name, { onUpdate, onUnmount }) {
name = name.toLowerCase();
const observer = new window.MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeName.toLowerCase() !== name) return
onUpdate(node, node);
});
// todo handle update
mutation.removedNodes.forEach(node => {
if (node.nodeName.toLowerCase() !== name) return
onUnmount(node, node);
});
});
});
observer.observe(document.body, {
attributes: true,
childList: true,
subtree: true
});
}
const name$1 = 'MutationObserver';
var MutationAdapter = /*#__PURE__*/Object.freeze({
isSupported: isSupported$1,
defineElement: defineElement$1,
name: name$1
});
// @flow
/*::
import type { ElementSpec } from './types'
*/
/**

@@ -149,12 +196,7 @@ * Updates a custom element by calling `ReactDOM.render()`.

function update (
element /* Element */,
{ component, attributes } /*: ElementSpec */,
mountPoint /* Element */
mountPoint /*: Element */,
props /*: {} */
) {
const props = element.hasAttribute('props-json')
? JSON.parse(element.getAttribute('props-json'))
: getProps(element, attributes);
const reactElement = React.createElement(component, props);
ReactDOM.render(reactElement, mountPoint);

@@ -164,7 +206,104 @@ }

/**
* Unmounts a component.
* @private
*/
function unmount (_ /*: any */, mountPoint /*: Element */) {
ReactDOM.unmountComponentAtNode(mountPoint);
}
// @flow
/*::
import type {
Component,
PropertyMap,
ElementMap,
Defaults,
ElementSpec
} from './lib/types'
*/
const Adapter = isSupported()
? ElementsAdapter
: isSupported$1()
? MutationAdapter
: null;
if (!Adapter) {
throw new Error('Unsupported platform')
} else {
console.log('Remount: using adapter', Adapter.name);
}
/**
* Inspect `Remount.adapterName` to see what adapter's being used.
*/
const adapterName = Adapter.name;
/**
* Registers elements.
*/
function define (
components /*: ElementMap */,
defaults /*: ?Defaults */
) {
Object.keys(components).forEach((name$$1 /*: string */) => {
// Construct the specs for the element.
// (eg, { component: Tooltip, attributes: ['title'] })
const elSpec /*: ElementSpec */ = Object.assign(
{},
defaults,
toElementSpec(components[name$$1])
);
// Define a custom element.
Adapter.defineElement(elSpec, name$$1, {
onUpdate (element /*: Element */, mountPoint /*: Element */) {
const props = getProps(element, elSpec.attributes);
update(elSpec, mountPoint, props);
},
onUnmount (element /*: Element */, mountPoint /*: Element */) {
unmount(elSpec, mountPoint);
}
});
});
}
/**
* Coerces something into an `ElementSpec` type.
* @private
*
* @example
* toElementSpec(Tooltip)
* // => { component: Tooltip }
*
* toElementSpec({ component: Tooltip })
* // => { component: Tooltip }
*/
function toElementSpec (
thing /*: ElementSpec | Component */
) /*: ElementSpec */ {
// $FlowFixMe$
if (typeof thing === 'object' && thing.component) return thing
return { component: thing }
}
/**
* Returns properties for a given HTML element.
* @private
*
* @example
* getProps(div, ['name'])
* // => { name: 'Romeo' }
*/
function getProps (element /*: Element */, attributes /*: ?Array<string> */) {
const rawJson = element.getAttribute('props-json');
if (rawJson) return JSON.parse(rawJson)
const names /*: Array<string> */ = attributes || [];

@@ -175,7 +314,5 @@ return names.reduce((result /*: PropertyMap */, attribute /*: string */) => {

}, {})
// By the way, did you know el.getAttributeNames()
// will not work in IE11? Now you do.
}
exports.adapterName = adapterName;
exports.define = define;

@@ -182,0 +319,0 @@

@@ -1,1 +0,1 @@

(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports,require('react'),require('react-dom')):'function'==typeof define&&define.amd?define(['exports','react','react-dom'],b):b(a.Remount={},a.React,a.ReactDOM)})(this,function(a,b,c){'use strict';function d(a){return'object'==typeof a&&a.component?a:{component:a}}function e(a,b){const d=a.attributes||[];class e extends window.HTMLElement{static get observedAttributes(){return['props-json',...d]}connectedCallback(){this._mountPoint=g(this,a),h(this,a,this._mountPoint)}disconnectedCallback(){this._mountPoint&&c.unmountComponentAtNode(this._mountPoint)}attributeChangedCallback(){this._mountPoint&&h(this,a,this._mountPoint)}}!f()||a.quiet&&window.customElements.get(b)||window.customElements.define(b,e)}function f(){return!!(window.customElements&&window.customElements.define)||(console.error('remount: Custom elements aren\'t support in this browser. Remount will not work. Including polyfills will likely fix this. See Remount documentation for more info: https://github.com/rstacruz/remount'),!1)}function g(a,{shadow:b}){if(b){const b=document.createElement('span');return a.attachShadow({mode:'open'}).appendChild(b),b}return a}function h(a,{component:d,attributes:e},f){const g=a.hasAttribute('props-json')?JSON.parse(a.getAttribute('props-json')):i(a,e),h=b.createElement(d,g);c.render(h,f)}function i(a,b){return(b||[]).reduce((b,c)=>(b[c]=a.getAttribute(c),b),{})}c=c&&c.hasOwnProperty('default')?c['default']:c,a.define=function(a,b){Object.keys(a).forEach((c)=>{const f=d(a[c]);e(Object.assign({},b,f),c)})},Object.defineProperty(a,'__esModule',{value:!0})});
(function(a,b){'object'==typeof exports&&'undefined'!=typeof module?b(exports,require('react'),require('react-dom')):'function'==typeof define&&define.amd?define(['exports','react','react-dom'],b):b(a.Remount={},a.React,a.ReactDOM)})(this,function(a,b,c){'use strict';function d(){if(l||void 0===window.Reflect||void 0===window.customElements||window.customElements.hasOwnProperty('polyfillWrapFlushCallback'))return;const b=HTMLElement;window.HTMLElement=function(){return Reflect.construct(b,[],this.constructor)},HTMLElement.prototype=b.prototype,HTMLElement.prototype.constructor=HTMLElement,Object.setPrototypeOf(HTMLElement,b),l=!0}function e(){return window.customElements&&window.customElements.define}function f(a,{shadow:b}){if(b){const b=document.createElement('span');return a.attachShadow({mode:'open'}).appendChild(b),b}return a}function g(){return!!window.MutationObserver}function h({component:a,attributes:d},e,f){const g=b.createElement(a,f);c.render(g,e)}function i(a,b){c.unmountComponentAtNode(b)}function j(a){return'object'==typeof a&&a.component?a:{component:a}}function k(a,b){const c=a.getAttribute('props-json');if(c)return JSON.parse(c);return(b||[]).reduce((b,c)=>(b[c]=a.getAttribute(c),b),{})}c=c&&c.hasOwnProperty('default')?c['default']:c;let l;var m=Object.freeze({defineElement:function(a,b,{onUpdate:c,onUnmount:e}){d();const g=a.attributes||[];class h extends window.HTMLElement{static get observedAttributes(){return['props-json',...g]}connectedCallback(){this._mountPoint=f(this,a),c(this,this._mountPoint)}disconnectedCallback(){this._mountPoint&&e(this,this._mountPoint)}attributeChangedCallback(){this._mountPoint&&c(this,this._mountPoint)}}a.quiet&&window.customElements.get(b)||window.customElements.define(b,h)},isSupported:e,name:'CustomElements'});var n=Object.freeze({isSupported:g,defineElement:function(a,b,{onUpdate:c,onUnmount:d}){b=b.toLowerCase();const e=new window.MutationObserver(a=>{a.forEach(a=>{a.addedNodes.forEach(a=>{a.nodeName.toLowerCase()!==b||c(a,a)}),a.removedNodes.forEach(a=>{a.nodeName.toLowerCase()!==b||d(a,a)})})});e.observe(document.body,{attributes:!0,childList:!0,subtree:!0})},name:'MutationObserver'});const o=e()?m:g()?n:null;if(!o)throw new Error('Unsupported platform');else console.log('Remount: using adapter',o.name);const p=o.name;a.adapterName=p,a.define=function(a,b){Object.keys(a).forEach((c)=>{const d=Object.assign({},b,j(a[c]));o.defineElement(d,c,{onUpdate(a,b){const c=k(a,d.attributes);h(d,b,c)},onUnmount(a,b){i(d,b)}})})},Object.defineProperty(a,'__esModule',{value:!0})});
// @flow
import * as React from 'react'
import ReactDOM from 'react-dom'
import * as ElementsAdapter from './lib/custom_elements'
import * as MutationAdapter from './lib/mutation_observer'
import * as ReactAdapter from './lib/react'
/*::
export type Component = React.ComponentType<{}>
import type {
Component,
PropertyMap,
ElementMap,
Defaults,
ElementSpec
} from './lib/types'
*/
export type PropertyMap = {
[string]: ?string
}
const Adapter = ElementsAdapter.isSupported()
? ElementsAdapter
: MutationAdapter.isSupported()
? MutationAdapter
: null
export type ElementMap = {
[string]: ElementSpec | Component
if (!Adapter) {
throw new Error('Unsupported platform')
} else {
console.log('Remount: using adapter', Adapter.name)
}
export type Defaults = {
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
}
/**
* Inspect `Remount.adapterName` to see what adapter's being used.
*/
export type ElementSpec = {
component: Component,
attributes?: Array<string>,
quiet?: boolean,
shadow?: boolean
}
*/
export const adapterName = Adapter.name

@@ -39,7 +43,36 @@ /**

Object.keys(components).forEach((name /*: string */) => {
const elSpec /*: ElementSpec */ = toElementSpec(components[name])
defineOne(Object.assign({}, defaults, elSpec), name)
// Construct the specs for the element.
// (eg, { component: Tooltip, attributes: ['title'] })
const elSpec /*: ElementSpec */ = Object.assign(
{},
defaults,
toElementSpec(components[name])
)
// Define a custom element.
Adapter.defineElement(elSpec, name, {
onUpdate (element /*: Element */, mountPoint /*: Element */) {
const props = getProps(element, elSpec.attributes)
ReactAdapter.update(elSpec, mountPoint, props)
},
onUnmount (element /*: Element */, mountPoint /*: Element */) {
ReactAdapter.unmount(elSpec, mountPoint)
}
})
})
}
/**
* Coerces something into an `ElementSpec` type.
* @private
*
* @example
* toElementSpec(Tooltip)
* // => { component: Tooltip }
*
* toElementSpec({ component: Tooltip })
* // => { component: Tooltip }
*/
function toElementSpec (

@@ -54,104 +87,14 @@ thing /*: ElementSpec | Component */

/**
* Registers one element.
* @private
*/
function defineOne (elSpec /*: ElementSpec */, name /*: string */) {
const attributes = elSpec.attributes || []
class ComponentElement extends window.HTMLElement {
static get observedAttributes () {
return ['props-json', ...attributes]
}
connectedCallback () {
this._mountPoint = createMountPoint(this, elSpec)
update(this, elSpec, this._mountPoint)
}
disconnectedCallback () {
if (!this._mountPoint) return
ReactDOM.unmountComponentAtNode(this._mountPoint)
}
attributeChangedCallback () {
if (!this._mountPoint) return
update(this, elSpec, this._mountPoint)
}
}
if (!ensureSupported()) return
// Supress warning when quiet mode is on
if (elSpec.quiet && window.customElements.get(name)) {
return
}
window.customElements.define(name, ComponentElement)
}
/**
* Ensures that custom elements are supported
* @private
*/
function ensureSupported () {
if (!window.customElements || !window.customElements.define) {
console.error(
"remount: Custom elements aren't support in this browser. " +
'Remount will not work. ' +
'Including polyfills will likely fix this. ' +
'See Remount documentation for more info: ' +
'https://github.com/rstacruz/remount'
)
return false
}
return true
}
/**
* Creates a `<span>` element that serves as the mounting point for React
* components.
* @private
*/
function createMountPoint (
element /*: Element */,
{ shadow } /*: ElementSpec */
) {
if (shadow) {
const mountPoint = document.createElement('span')
element.attachShadow({ mode: 'open' }).appendChild(mountPoint)
return mountPoint
} else {
return element
}
}
/**
* Updates a custom element by calling `ReactDOM.render()`.
* @private
*/
function update (
element /* Element */,
{ component, attributes } /*: ElementSpec */,
mountPoint /* Element */
) {
const props = element.hasAttribute('props-json')
? JSON.parse(element.getAttribute('props-json'))
: getProps(element, attributes)
const reactElement = React.createElement(component, props)
ReactDOM.render(reactElement, mountPoint)
}
/**
* Returns properties for a given HTML element.
* @private
*
* @example
* getProps(div, ['name'])
* // => { name: 'Romeo' }
*/
function getProps (element /*: Element */, attributes /*: ?Array<string> */) {
const rawJson = element.getAttribute('props-json')
if (rawJson) return JSON.parse(rawJson)
const names /*: Array<string> */ = attributes || []

@@ -162,5 +105,2 @@ return names.reduce((result /*: PropertyMap */, attribute /*: string */) => {

}, {})
// By the way, did you know el.getAttributeNames()
// will not work in IE11? Now you do.
}
{
"name": "remount",
"description": "Mount React components to the DOM using custom elements",
"version": "0.5.0",
"version": "0.7.0",
"author": "Rico Sta. Cruz <rstacruz@users.noreply.github.com>",

@@ -16,2 +16,3 @@ "bugs": {

"flow-bin": "^0.79.1",
"gh-pages": "^1.2.0",
"jest": "^23.5.0",

@@ -69,2 +70,3 @@ "npm-run-all": "^4.1.3",

"eslint": "eslint '*.js'",
"deploy": "gh-pages -d . -s 'dist/**/*'",
"flow": "flow",

@@ -71,0 +73,0 @@ "jest": "jest",

<br>
<p align='center'><img src='docs/images/remount.png' width='500'></p>
<p align='center'><a href='https://github.com/rstacruz/remount'><img src='docs/images/remount.png' width='450'></a></p>
<br>
<h1 align='center'>Remount</h1>
<p align='center'>
⚡ <a href='https://codepen.io/rstacruz/pen/EpBZRv?editors=1010'><b>Demo</b></a> ⚡
<a href='https://travis-ci.org/rstacruz/remount'><img src='https://api.travis-ci.org/rstacruz/remount.svg?branch=master' alt='Travis CI' /></a>
</p>
<p align='center'>Use your React components anywhere in your <br> HTML as web components (custom elements).</p>
<p align='center'>
<a href='https://codepen.io/rstacruz/pen/EpBZRv?editors=1010'>Demo</a> ⚡ <a href='https://github.com/rstacruz/remount#remount'>Docs</a>
<br>
1kb gzip'd &nbsp;&middot;&nbsp; No dependencies &nbsp;&middot;&nbsp; IE9 support
<em>1kb gzip'd &nbsp;&middot;&nbsp; No dependencies &nbsp;&middot;&nbsp; IE support</em>
</p>

@@ -13,13 +23,7 @@

# Remount
<br>
> 🔌 Mount React components to the DOM using custom elements
_Experimental_ - Remount lets you use your React components anywhere in the page as a web component (custom element).
[![Status](https://travis-ci.org/rstacruz/remount.svg?branch=master)](https://travis-ci.org/rstacruz/remount 'See test builds')
## Installation
Remount is available through the [npm package repository](https://yarnpkg.com/en/package/remount).
Remount is available through the npm package repository.

@@ -29,7 +33,7 @@ - Via yarn: `yarn add remount`

Be sure to use the [recommended polyfills](#polyfills) below as well!
Be sure to use the recommended polyfills below as well. [#](#polyfills)
## Usage
Given any React component, such as this:
Let's start with any React component. Here's one:

@@ -42,3 +46,3 @@ ```js

You can use _define()_ to define custom elements. Let's define `<x-greeter>` like so:
Use _define()_ to define custom elements. Let's define a `<x-greeter>` element:

@@ -48,8 +52,6 @@ ```js

define({
'x-greeter': Greeter
})
define({ 'x-greeter': Greeter })
```
You can then use it in your HTML, or even in your other React components!
You can now use it anywhere in your HTML! :boom:

@@ -60,3 +62,3 @@ ```html

➡️ More at **[API documentation](docs/api.md)**
⚡ **[API documentation →](docs/api.md)**

@@ -69,61 +71,27 @@ ## Use cases

| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <img src='http://source.unsplash.com/350x250?sea'> | ✨ **Adding React to non-SPA apps** <br> You can use React components on any page of a "regular" HTML site. Great for adding React to apps built on Rails or Phoenix. |
| <br><img src='./docs/images/non-spa.png' width='400'><br><br> | ✨ **Adding React to non-SPA apps** <br> You can use React components on any page of a "regular" HTML site. Great for adding React to apps built on Rails or Phoenix. |
| | |
| <img src='http://source.unsplash.com/350x250?sun'> | 💞 **Interop with other frameworks** <br> Remount lets you use your React components just like any other HTML element. This means you can use React with Vue, Angular, or any other DOM library/framework. |
| | |
| <br><img src='./docs/images/interop.png' width='400'><br><br> | 💞 **Interop with other frameworks** <br> Remount lets you use your React components just like any other HTML element. This means you can use React with Vue, Angular, or any other DOM library/framework. |
## Custom properties
## More features
> `<x-greeter name="John"></x-greeter>`
- JSON props (eg, `<x-greeter props-json="{...}">`) ([docs](./docs/api.md))
- Named attributes (eg, `<x-greeter name="John">`) ([docs](./docs/api.md))
- Shadow DOM ([docs](./docs/api.md))
Only the `props-json` attribute is supported by default. To support custom properties like above, pass the names of attributes you want Remount to use.
## Browser support
```js
import { define } from 'remount'
Remount supports all modern browsers, including IE11 (Internet Explorer's oldest supported version as of 2016). Remember to use the polyfills below to ensure the best compatibility.
define({
'x-greeter': {
component: Greeter,
attributes: ['name']
}
})
```
⚡ [Browser support docs →](./docs/polyfills.md)
## Limitations
Remount relies on the [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) HTML API, so all limitations of the Custom Elements API apply. Keep these in mind:
- Custom components names require a hyphen, and are case insensitive.
- Attributes are case insensitive.
## Browser support
Remount supports all browsers [supported by React](https://reactjs.org/docs/react-dom.html#browser-support). Use the polyfills below to ensure the best compatibility.
## Polyfills
We recommend these two polyfills provided by the [@webcomponents/webcomponentsjs][@webcomponents/webcomponentsjs] package. Load it via JavaScript in your app's entry point:
More info on this on the [Polyfill docs](./docs/polyfills.md).
```js
// Add the package via: yarn add @webcomponents/webcomponentsjs
import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js'
import '@webcomponents/webcomponentsjs/webcomponents-loader.js'
```
## Documentation
Or you can load it via CDN:
```html
<script crossorigin src='https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.0.4/custom-elements-es5-adapter.js'></script>
<script crossorigin src='https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.0.4/webcomponents-loader.js'></script>
```
[@webcomponents/webcomponentsjs]: https://yarn.pm/@webcomponents/webcomponentsjs
More info at the [Polyfills documentation](./docs/polyfills.md).
## More info
- [API documentation](./docs/api.md)
- [Builds](./docs/builds.md) &mdash; ES2015+ and ES Module builds are also provided.
- [API documentation](./docs/api.md)
- [Frequently-asked questions](./docs/faq.md)
- [FAQ and Troubleshooting](./docs/faq.md) &mdash; Start here if you find any issues.
- [Comparison with alternatives](./docs/comparison.md)

@@ -130,0 +98,0 @@ - [Polyfills](./docs/polyfills.md)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc