884 lines
24 KiB
JavaScript
884 lines
24 KiB
JavaScript
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('react-dom')) :
|
|
typeof define === 'function' && define.amd ? define(['exports', 'react', 'react-dom'], factory) :
|
|
(global = global || self, factory(global.Remount = {}, global.React, global.ReactDOM));
|
|
}(this, function (exports, React, ReactDOM) { 'use strict';
|
|
|
|
function _typeof(obj) {
|
|
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
|
_typeof = function (obj) {
|
|
return typeof obj;
|
|
};
|
|
} else {
|
|
_typeof = function (obj) {
|
|
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
};
|
|
}
|
|
|
|
return _typeof(obj);
|
|
}
|
|
|
|
function _classCallCheck(instance, Constructor) {
|
|
if (!(instance instanceof Constructor)) {
|
|
throw new TypeError("Cannot call a class as a 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);
|
|
}
|
|
}
|
|
|
|
function _createClass(Constructor, protoProps, staticProps) {
|
|
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
return Constructor;
|
|
}
|
|
|
|
function _inherits(subClass, superClass) {
|
|
if (typeof superClass !== "function" && superClass !== null) {
|
|
throw new TypeError("Super expression must either be null or a function");
|
|
}
|
|
|
|
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
constructor: {
|
|
value: subClass,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
if (superClass) _setPrototypeOf(subClass, superClass);
|
|
}
|
|
|
|
function _getPrototypeOf(o) {
|
|
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
|
|
return o.__proto__ || Object.getPrototypeOf(o);
|
|
};
|
|
return _getPrototypeOf(o);
|
|
}
|
|
|
|
function _setPrototypeOf(o, p) {
|
|
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
o.__proto__ = p;
|
|
return o;
|
|
};
|
|
|
|
return _setPrototypeOf(o, p);
|
|
}
|
|
|
|
function isNativeReflectConstruct() {
|
|
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
if (Reflect.construct.sham) return false;
|
|
if (typeof Proxy === "function") return true;
|
|
|
|
try {
|
|
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function _construct(Parent, args, Class) {
|
|
if (isNativeReflectConstruct()) {
|
|
_construct = Reflect.construct;
|
|
} else {
|
|
_construct = function _construct(Parent, args, Class) {
|
|
var a = [null];
|
|
a.push.apply(a, args);
|
|
var Constructor = Function.bind.apply(Parent, a);
|
|
var instance = new Constructor();
|
|
if (Class) _setPrototypeOf(instance, Class.prototype);
|
|
return instance;
|
|
};
|
|
}
|
|
|
|
return _construct.apply(null, arguments);
|
|
}
|
|
|
|
function _isNativeFunction(fn) {
|
|
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
|
}
|
|
|
|
function _wrapNativeSuper(Class) {
|
|
var _cache = typeof Map === "function" ? new Map() : undefined;
|
|
|
|
_wrapNativeSuper = function _wrapNativeSuper(Class) {
|
|
if (Class === null || !_isNativeFunction(Class)) return Class;
|
|
|
|
if (typeof Class !== "function") {
|
|
throw new TypeError("Super expression must either be null or a function");
|
|
}
|
|
|
|
if (typeof _cache !== "undefined") {
|
|
if (_cache.has(Class)) return _cache.get(Class);
|
|
|
|
_cache.set(Class, Wrapper);
|
|
}
|
|
|
|
function Wrapper() {
|
|
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
|
|
}
|
|
|
|
Wrapper.prototype = Object.create(Class.prototype, {
|
|
constructor: {
|
|
value: Wrapper,
|
|
enumerable: false,
|
|
writable: true,
|
|
configurable: true
|
|
}
|
|
});
|
|
return _setPrototypeOf(Wrapper, Class);
|
|
};
|
|
|
|
return _wrapNativeSuper(Class);
|
|
}
|
|
|
|
function _assertThisInitialized(self) {
|
|
if (self === void 0) {
|
|
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
function _possibleConstructorReturn(self, call) {
|
|
if (call && (typeof call === "object" || typeof call === "function")) {
|
|
return call;
|
|
}
|
|
|
|
return _assertThisInitialized(self);
|
|
}
|
|
|
|
function _toConsumableArray(arr) {
|
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
|
|
}
|
|
|
|
function _arrayWithoutHoles(arr) {
|
|
if (Array.isArray(arr)) {
|
|
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
|
|
|
|
return arr2;
|
|
}
|
|
}
|
|
|
|
function _iterableToArray(iter) {
|
|
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
|
|
}
|
|
|
|
function _nonIterableSpread() {
|
|
throw new TypeError("Invalid attempt to spread non-iterable instance");
|
|
}
|
|
|
|
/* global HTMLElement */
|
|
|
|
/*
|
|
* 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 (window.HTMLElement && window.HTMLElement._babelES5Adapter || 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);
|
|
HTMLElement._babelES5Adapter = true;
|
|
}
|
|
|
|
/**
|
|
* The name of this strategy.
|
|
* @type string
|
|
*/
|
|
|
|
var name = 'CustomElements';
|
|
/**
|
|
* 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
|
|
* @param {ElementSpec} elSpec
|
|
* @param {string} elName
|
|
* @param {ElementEvents} events
|
|
*/
|
|
|
|
function defineElement(elSpec, elName, events) {
|
|
var onUpdate = events.onUpdate,
|
|
onUnmount = events.onUnmount,
|
|
onMount = events.onMount;
|
|
inject();
|
|
var attributes = elSpec.attributes || [];
|
|
|
|
var ComponentElement =
|
|
/*#__PURE__*/
|
|
function (_HTMLElement) {
|
|
_inherits(ComponentElement, _HTMLElement);
|
|
|
|
function ComponentElement() {
|
|
_classCallCheck(this, ComponentElement);
|
|
|
|
return _possibleConstructorReturn(this, _getPrototypeOf(ComponentElement).apply(this, arguments));
|
|
}
|
|
|
|
_createClass(ComponentElement, [{
|
|
key: "connectedCallback",
|
|
value: function connectedCallback() {
|
|
this._mountPoint = createMountPoint(this, elSpec);
|
|
onMount(this, this._mountPoint);
|
|
}
|
|
}, {
|
|
key: "disconnectedCallback",
|
|
value: function disconnectedCallback() {
|
|
if (!this._mountPoint) {
|
|
return;
|
|
}
|
|
|
|
onUnmount(this, this._mountPoint);
|
|
}
|
|
}, {
|
|
key: "attributeChangedCallback",
|
|
value: function attributeChangedCallback() {
|
|
if (!this._mountPoint) {
|
|
return;
|
|
}
|
|
|
|
onUpdate(this, this._mountPoint);
|
|
}
|
|
}], [{
|
|
key: "observedAttributes",
|
|
get: function get() {
|
|
return ['props-json'].concat(_toConsumableArray(attributes));
|
|
}
|
|
}]);
|
|
|
|
return ComponentElement;
|
|
}(_wrapNativeSuper(HTMLElement)); // Supress warning when quiet mode is on
|
|
|
|
|
|
if (elSpec.quiet && window.customElements.get(elName)) {
|
|
return;
|
|
}
|
|
|
|
window.customElements.define(elName, ComponentElement);
|
|
}
|
|
function isSupported() {
|
|
return !!(window.customElements && window.customElements.define);
|
|
}
|
|
/**
|
|
* Creates a `<span>` element that serves as the mounting point for React
|
|
* components. If `shadow: true` is requested, it'll attach a shadow node.
|
|
*
|
|
* @private
|
|
* @param {HTMLElement} element
|
|
* @param {ElementSpec} elSpec
|
|
*/
|
|
|
|
function createMountPoint(element, elSpec) {
|
|
var shadow = elSpec.shadow;
|
|
|
|
if (shadow && element.attachShadow) {
|
|
var mountPoint = document.createElement('span');
|
|
element.attachShadow({
|
|
mode: 'open'
|
|
}).appendChild(mountPoint);
|
|
return mountPoint;
|
|
} else {
|
|
return element;
|
|
}
|
|
}
|
|
/**
|
|
* Check if Shadow DOM is supported.
|
|
*/
|
|
|
|
|
|
function supportsShadow() {
|
|
return !!(document && document.body && document.body.attachShadow);
|
|
}
|
|
|
|
var CustomElementsStrategy = /*#__PURE__*/Object.freeze({
|
|
name: name,
|
|
defineElement: defineElement,
|
|
isSupported: isSupported,
|
|
supportsShadow: supportsShadow
|
|
});
|
|
|
|
// @ts-check
|
|
|
|
/**
|
|
* Some implementations of MutationObserver don't have .forEach,
|
|
* so we need our own `forEach` shim. This is usually the case with
|
|
* polyfilled environments.
|
|
*
|
|
* @type { import('./types').Each }
|
|
*/
|
|
function each(
|
|
/** @type any */
|
|
list,
|
|
/** @type any */
|
|
fn) {
|
|
for (var i = 0, len = list.length; i < len; i++) {
|
|
fn(list[i]);
|
|
}
|
|
}
|
|
|
|
// @ts-check
|
|
/**
|
|
* The name of this strategy.
|
|
* @type string
|
|
*/
|
|
|
|
var name$1 = 'MutationObserver';
|
|
/**
|
|
* List of observers tags.
|
|
* @type ObserverList
|
|
*/
|
|
|
|
var observers = {};
|
|
function isSupported$1() {
|
|
return 'MutationObserver' in window;
|
|
}
|
|
/**
|
|
* Defines a custom element.
|
|
*
|
|
* @example
|
|
* defineElement(
|
|
* { component: MyComponent },
|
|
* 'my-div',
|
|
* {
|
|
* onMount: () => {},
|
|
* onUpdate: () => {},
|
|
* onUnmount: () => {},
|
|
* }
|
|
* )
|
|
*
|
|
* @private
|
|
* @param {ElementSpec} elSpec
|
|
* @param {string} elName
|
|
* @param {ElementEvents} events
|
|
*/
|
|
|
|
function defineElement$1(elSpec, elName, events) {
|
|
elName = elName.toLowerCase(); // Maintain parity with what would happen in Custom Elements mode
|
|
|
|
if (!isValidName(elName)) {
|
|
if (elSpec.quiet) {
|
|
return;
|
|
}
|
|
|
|
throw new Error("Remount: \"".concat(elName, "\" is not a valid custom element elName"));
|
|
}
|
|
|
|
if (observers[elName]) {
|
|
if (elSpec.quiet) {
|
|
return;
|
|
}
|
|
|
|
throw new Error("Remount: \"".concat(elName, "\" is already registered"));
|
|
}
|
|
|
|
var observer = new MutationObserver(
|
|
/** @type MutationCallback */
|
|
function (mutations) {
|
|
each(mutations, function (
|
|
/** @type MutationRecord */
|
|
mutation) {
|
|
each(mutation.addedNodes, function (
|
|
/** @type Node */
|
|
node) {
|
|
if (isElement(node)) {
|
|
checkForMount(node, elName, events);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
observer.observe(document.body, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
observers[name$1] =
|
|
/* true */
|
|
observer;
|
|
window.addEventListener('DOMContentLoaded', function () {
|
|
var nodes = document.getElementsByTagName(name$1);
|
|
each(nodes, function (
|
|
/** @type HTMLElement */
|
|
node) {
|
|
return checkForMount(node, name$1, events);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Checks if this new element should fire an `onUpdate` hook.
|
|
* Recurses down to its descendant nodes.
|
|
*
|
|
* @param {HTMLElement} node
|
|
* @param {string} elName
|
|
* @param {ElementEvents} events
|
|
*/
|
|
|
|
function checkForMount(node, elName, events) {
|
|
if (node.nodeName.toLowerCase() === elName) {
|
|
// It's a match!
|
|
events.onMount(node, node);
|
|
observeForUpdates(node, events);
|
|
observeForRemoval(node, events);
|
|
} else if (node.children && node.children.length) {
|
|
// Recurse down into the other additions
|
|
each(node.children, function (
|
|
/** @type HTMLElement */
|
|
subnode) {
|
|
if (isElement(subnode)) {
|
|
checkForMount(subnode, elName, events);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Observes for any changes in attributes.
|
|
*
|
|
* @param {Element} node
|
|
* @param {ElementEvents} events
|
|
*/
|
|
|
|
|
|
function observeForUpdates(node, events) {
|
|
var onUpdate = events.onUpdate;
|
|
var observer = new MutationObserver(
|
|
/** @type MutationCallback */
|
|
function (mutations) {
|
|
each(mutations, function (
|
|
/** @type MutationRecord */
|
|
mutation) {
|
|
var targetNode = mutation.target;
|
|
|
|
if (isElement(targetNode)) {
|
|
onUpdate(targetNode, targetNode);
|
|
}
|
|
});
|
|
});
|
|
observer.observe(node, {
|
|
attributes: true
|
|
});
|
|
}
|
|
/**
|
|
* Observes a node's parent to wait until the node is removed
|
|
* @param {HTMLElement} node
|
|
* @param {ElementEvents} events
|
|
*/
|
|
|
|
|
|
function observeForRemoval(node, events) {
|
|
var onUnmount = events.onUnmount;
|
|
var parent = node.parentNode; // Not sure when this can happen, but let's add this for type safety
|
|
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
|
|
var observer = new MutationObserver(
|
|
/** @type MutationCallback */
|
|
function (mutations) {
|
|
each(mutations, function (
|
|
/** @type MutationRecord */
|
|
mutation) {
|
|
each(mutation.removedNodes, function (
|
|
/** @type Node */
|
|
subnode) {
|
|
if (node !== subnode) {
|
|
return;
|
|
}
|
|
|
|
if (isElement(node)) {
|
|
// @ts-ignore TypeScript expects 0 arguments...?
|
|
observer.disconnect(parent);
|
|
onUnmount(node, node);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
observer.observe(parent, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
}
|
|
/**
|
|
* Validate a custom tag.
|
|
*
|
|
* Since Remount can work with either Custom Elements or MutationObserver API's,
|
|
* it'd be wise if we rejected element names that won't work in Custom Elements
|
|
* mode (even if we're using MutationObserver mode).
|
|
*
|
|
* @param {string} elName
|
|
* @returns {boolean}
|
|
*
|
|
* @example
|
|
* isValidName('div') // => false
|
|
* isValidName('my-div') // => true
|
|
* isValidName('123-456') // => false
|
|
* isValidName('my-123') // => true
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
|
|
function isValidName(elName) {
|
|
return !!(elName.indexOf('-') !== -1 && elName.match(/^[a-z][a-z0-9-]*$/));
|
|
}
|
|
/**
|
|
* Shadow DOM is not supported with the Mutation Observer strategy.
|
|
*/
|
|
|
|
|
|
function supportsShadow$1() {
|
|
return false;
|
|
}
|
|
/**
|
|
* Checks if a given Node is an HTMLElement.
|
|
*
|
|
* It's possible that a mutation's `addedNodes` return something that isn't an
|
|
* HTMLElement.
|
|
*
|
|
* @param {any} node
|
|
* @returns {node is HTMLElement}
|
|
*/
|
|
|
|
function isElement(node) {
|
|
if (node) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
var MutationObserverStrategy = /*#__PURE__*/Object.freeze({
|
|
name: name$1,
|
|
observers: observers,
|
|
isSupported: isSupported$1,
|
|
defineElement: defineElement$1,
|
|
supportsShadow: supportsShadow$1
|
|
});
|
|
|
|
var reactEvents = ["onAbort", "onAnimationCancel", "onAnimationEnd", "onAnimationIteration", "onAuxClick", "onBlur",
|
|
"onChange", "onClick", "onClose", "onContextMenu", "onDoubleClick", "onError", "onFocus", "onGotPointerCapture",
|
|
"onInput", "onKeyDown", "onKeyPress", "onKeyUp", "onLoad", "onLoadEnd", "onLoadStart", "onLostPointerCapture",
|
|
"onMouseDown", "onMouseMove", "onMouseOut", "onMouseOver", "onMouseUp", "onPointerCancel", "onPointerDown",
|
|
"onPointerEnter", "onPointerLeave", "onPointerMove", "onPointerOut", "onPointerOver", "onPointerUp", "onReset",
|
|
"onResize", "onScroll", "onSelect", "onSelectionChange", "onSelectStart", "onSubmit", "onTouchCancel",
|
|
"onTouchMove", "onTouchStart", "onTransitionCancel", "onTransitionEnd", "onDrag", "onDragEnd", "onDragEnter",
|
|
"onDragExit", "onDragLeave", "onDragOver", "onDragStart", "onDrop", "onFocusOut"];
|
|
|
|
var divergentNativeEvents = {
|
|
onDoubleClick: 'dblclick'
|
|
};
|
|
|
|
var mimickedReactEvents = {
|
|
onInput: 'onChange',
|
|
onFocusOut: 'onBlur',
|
|
onSelectionChange: 'onSelect'
|
|
};
|
|
|
|
var reactShadowDomRetargetEvents = function retargetEvents(shadowRoot) {
|
|
|
|
reactEvents.forEach(function (reactEventName) {
|
|
|
|
var nativeEventName = getNativeEventName(reactEventName);
|
|
|
|
shadowRoot.addEventListener(nativeEventName, function (event) {
|
|
|
|
var path = event.path || (event.composedPath && event.composedPath()) || composedPath(event.target);
|
|
|
|
for (var i = 0; i < path.length; i++) {
|
|
|
|
var el = path[i];
|
|
var reactComponent = findReactComponent(el);
|
|
var props = findReactProps(reactComponent);
|
|
|
|
if (reactComponent && props) {
|
|
dispatchEvent(event, reactEventName, props);
|
|
}
|
|
|
|
if (reactComponent && props && mimickedReactEvents[reactEventName]) {
|
|
dispatchEvent(event, mimickedReactEvents[reactEventName], props);
|
|
}
|
|
|
|
if (event.cancelBubble) {
|
|
break;
|
|
}
|
|
|
|
if (el === shadowRoot) {
|
|
break;
|
|
}
|
|
}
|
|
}, false);
|
|
});
|
|
};
|
|
|
|
function findReactComponent(item) {
|
|
for (var key in item) {
|
|
if (item.hasOwnProperty(key) && key.indexOf('_reactInternal') !== -1) {
|
|
return item[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
function findReactProps(component) {
|
|
if (!component) return undefined;
|
|
if (component.memoizedProps) return component.memoizedProps; // React 16 Fiber
|
|
if (component._currentElement && component._currentElement.props) return component._currentElement.props; // React <=15
|
|
|
|
}
|
|
|
|
function dispatchEvent(event, eventType, componentProps) {
|
|
if (componentProps[eventType]) {
|
|
componentProps[eventType](event);
|
|
}
|
|
}
|
|
|
|
function getNativeEventName(reactEventName) {
|
|
if (divergentNativeEvents[reactEventName]) {
|
|
return divergentNativeEvents[reactEventName];
|
|
}
|
|
return reactEventName.replace(/^on/, '').toLowerCase();
|
|
}
|
|
|
|
function composedPath(el) {
|
|
var path = [];
|
|
while (el) {
|
|
path.push(el);
|
|
if (el.tagName === 'HTML') {
|
|
path.push(document);
|
|
path.push(window);
|
|
return path;
|
|
}
|
|
el = el.parentElement;
|
|
}
|
|
}
|
|
|
|
// @ts-check
|
|
/**
|
|
* @param {ElementSpec} elSpec
|
|
* @param {HTMLElement} mountPoint
|
|
* @param {object} props
|
|
* @param {HTMLElement | null} element
|
|
*/
|
|
|
|
function mount(elSpec, mountPoint, props, element) {
|
|
return update(elSpec, mountPoint, props, element);
|
|
}
|
|
/**
|
|
* Updates a custom element by calling `ReactDOM.render()`.
|
|
* @private
|
|
*
|
|
* @param {ElementSpec} elSpec
|
|
* @param {HTMLElement} mountPoint
|
|
* @param {object} props
|
|
* @param {HTMLElement | null} element
|
|
*/
|
|
|
|
function update(elSpec, mountPoint, props, element) {
|
|
var component = elSpec.component;
|
|
var reactElement = React.createElement(component, props);
|
|
ReactDOM.render(reactElement, mountPoint);
|
|
|
|
if (element) {
|
|
reactShadowDomRetargetEvents(element.shadowRoot);
|
|
}
|
|
}
|
|
/**
|
|
* Unmounts a component.
|
|
* @private
|
|
*
|
|
* @param {ElementSpec} elSpec
|
|
* @param {HTMLElement} mountPoint
|
|
*/
|
|
|
|
function unmount(elSpec, mountPoint) {
|
|
ReactDOM.unmountComponentAtNode(mountPoint);
|
|
}
|
|
|
|
var ReactAdapter = /*#__PURE__*/Object.freeze({
|
|
mount: mount,
|
|
update: update,
|
|
unmount: unmount
|
|
});
|
|
|
|
/**
|
|
* Cache of the strategy determined by `getStrategy()`.
|
|
* @type {Strategy | null | undefined}
|
|
*/
|
|
|
|
var cachedStrategy;
|
|
/**
|
|
* Detect what API can be used.
|
|
*
|
|
* @example
|
|
* Remount.getStrategy().name
|
|
*/
|
|
|
|
function getStrategy() {
|
|
if (cachedStrategy) {
|
|
return cachedStrategy;
|
|
}
|
|
|
|
var StrategyUsed = [CustomElementsStrategy, MutationObserverStrategy].find(function (strategy) {
|
|
return !!strategy.isSupported();
|
|
});
|
|
|
|
if (!StrategyUsed) {
|
|
/* tslint:disable no-console */
|
|
console.warn("Remount: This browser doesn't support the " + 'MutationObserver API or the Custom Elements API. Including ' + 'polyfills might fix this. Remount elements will not work. ' + 'https://github.com/rstacruz/remount');
|
|
}
|
|
|
|
cachedStrategy = StrategyUsed;
|
|
return StrategyUsed;
|
|
}
|
|
/**
|
|
* Registers custom elements and links them to React components.
|
|
* @param {ElementMap} components
|
|
* @param {Defaults=} defaults
|
|
*
|
|
* @example
|
|
* define({ 'x-tooltip': Tooltip })
|
|
*
|
|
* @example
|
|
* define(
|
|
* { 'x-tooltip': Tooltip },
|
|
* { attributes: ['title', 'body'] }
|
|
* )
|
|
*/
|
|
|
|
function define(components, defaults) {
|
|
var Strategy = getStrategy();
|
|
|
|
if (!Strategy) {
|
|
return;
|
|
}
|
|
|
|
Object.keys(components).forEach(function (
|
|
/** @type string */
|
|
name$$1) {
|
|
// Construct the specs for the element.
|
|
// (eg, { component: Tooltip, attributes: ['title'] })
|
|
|
|
/** @type ElementSpec */
|
|
var elSpec = Object.assign({}, defaults, toElementSpec(components[name$$1]));
|
|
/** @type Adapter */
|
|
|
|
var adapter = elSpec.adapter || ReactAdapter; // Define a custom element.
|
|
|
|
Strategy.defineElement(elSpec, name$$1, {
|
|
onMount: function onMount(element, mountPoint) {
|
|
var props = getProps(element, elSpec.attributes);
|
|
|
|
if (elSpec.shadow && elSpec.retarget) {
|
|
adapter.mount(elSpec, mountPoint, props, element);
|
|
} else {
|
|
adapter.mount(elSpec, mountPoint, props, null);
|
|
}
|
|
},
|
|
onUpdate: function onUpdate(element, mountPoint) {
|
|
var props = getProps(element, elSpec.attributes);
|
|
adapter.update(elSpec, mountPoint, props, null);
|
|
},
|
|
onUnmount: function onUnmount(element, mountPoint) {
|
|
adapter.unmount(elSpec, mountPoint);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Coerces something into an `ElementSpec` type.
|
|
*
|
|
* @param {ElementSpec | Component} thing
|
|
* @returns {ElementSpec}
|
|
* @private
|
|
*
|
|
* @example
|
|
* toElementSpec(Tooltip)
|
|
* // => { component: Tooltip }
|
|
*
|
|
* toElementSpec({ component: Tooltip })
|
|
* // => { component: Tooltip }
|
|
*/
|
|
|
|
function toElementSpec(thing) {
|
|
if (isElementSpec(thing)) {
|
|
return thing;
|
|
}
|
|
|
|
return {
|
|
component: thing
|
|
};
|
|
}
|
|
/**
|
|
* Checks if a given `spec` is an ElementSpec.
|
|
*
|
|
* @param {any} spec
|
|
* @returns {spec is ElementSpec}
|
|
*/
|
|
|
|
|
|
function isElementSpec(spec) {
|
|
return _typeof(spec) === 'object' && spec.component;
|
|
}
|
|
/**
|
|
* Returns properties for a given HTML element.
|
|
*
|
|
* @private
|
|
* @param {HTMLElement} element
|
|
* @param {string[] | null | undefined} attributes
|
|
*
|
|
* @example
|
|
* getProps(div, ['name'])
|
|
* // => { name: 'Romeo' }
|
|
*/
|
|
|
|
|
|
function getProps(element, attributes) {
|
|
var rawJson = element.getAttribute('props-json');
|
|
|
|
if (rawJson) {
|
|
return JSON.parse(rawJson);
|
|
}
|
|
|
|
var names = attributes || [];
|
|
return names.reduce(function (
|
|
/** @type PropertyMap */
|
|
result,
|
|
/** @type string */
|
|
attribute) {
|
|
result[attribute] = element.getAttribute(attribute);
|
|
return result;
|
|
}, {});
|
|
}
|
|
|
|
exports.getStrategy = getStrategy;
|
|
exports.define = define;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
}));
|