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

panel

Package Overview
Dependencies
Maintainers
6
Versions
131
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

panel - npm Package Compare versions

Comparing version 5.13.0 to 6.0.0

build/component-utils/shallowEqual.js

4

build/component-utils/hook-helpers.js

@@ -7,2 +7,3 @@ "use strict";

exports.delayedAttrRemove = delayedAttrRemove;
/**

@@ -14,4 +15,3 @@ * A simple remove hook generator so we remove an element after it's finished its closing animation.

function delayedAttrRemove(attr, value) {
var waitMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
let waitMs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 500;
return function (vnode, callback) {

@@ -18,0 +18,0 @@ vnode.elm.setAttribute(attr, value);

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

'use strict';
"use strict";

@@ -6,11 +6,8 @@ Object.defineProperty(exports, "__esModule", {

});
exports.default = void 0;
var _stateController = require('./state-controller');
var _stateController = _interopRequireDefault(require("./state-controller"));
var _stateController2 = _interopRequireDefault(_stateController);
var _stateStore = _interopRequireDefault(require("./state-store"));
var _stateStore = require('./state-store');
var _stateStore2 = _interopRequireDefault(_stateStore);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -29,9 +26,9 @@

*/
exports.default = {
var _default = {
/** {@link StateController} class, to be subclassed by apps */
StateController: _stateController2.default,
StateController: _stateController.default,
/** A simple subscribable state store */
StateStore: _stateStore2.default
};
StateStore: _stateStore.default
};
exports.default = _default;

@@ -6,3 +6,4 @@ "use strict";

});
exports.getNow = getNow;
exports.Perf = void 0;
/**

@@ -12,7 +13,13 @@ * Attempt to use a high resolution timestamp when in the browswer environment, but fallback to Date.now

*/
const Perf = {
getNow
};
exports.Perf = Perf;
function getNow() {
if (typeof performance !== "undefined") {
if (typeof performance !== `undefined`) {
return performance.now();
}
return Date.now();
}

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

'use strict';
"use strict";

@@ -6,13 +6,8 @@ Object.defineProperty(exports, "__esModule", {

});
exports.default = void 0;
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 _stateStore = _interopRequireDefault(require("./state-store"));
var _stateStore = require('./state-store');
var _stateStore2 = _interopRequireDefault(_stateStore);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**

@@ -25,48 +20,37 @@ * A StateController manages state for an application or component

*/
var StateController = function () {
class StateController {
// Create's a default store if one isn't given
function StateController() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$store = _ref.store,
store = _ref$store === undefined ? null : _ref$store;
constructor() {
let {
store = null
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this._store = store || new _stateStore.default();
_classCallCheck(this, StateController);
this._store = store || new _stateStore2.default();
this._update(this.defaultState);
}
_createClass(StateController, [{
key: '_update',
get defaultState() {
return {};
}
get state() {
return this._store.state;
} // Discourage external users from using state directly
// Discourage external users from using state directly
value: function _update(props) {
this._store.update(props);
}
}, {
key: 'subscribeUpdates',
value: function subscribeUpdates(listener) {
return this._store.subscribeUpdates(listener);
}
}, {
key: 'unsubscribeUpdates',
value: function unsubscribeUpdates(listener) {
return this._store.unsubscribeUpdates(listener);
}
}, {
key: 'defaultState',
get: function get() {
return {};
}
}, {
key: 'state',
get: function get() {
return this._store.state;
}
}]);
return StateController;
}();
_update(props) {
this._store.update(props);
}
exports.default = StateController;
subscribeUpdates(listener) {
return this._store.subscribeUpdates(listener);
}
unsubscribeUpdates(listener) {
return this._store.unsubscribeUpdates(listener);
}
}
var _default = StateController;
exports.default = _default;

@@ -6,14 +6,9 @@ "use strict";

});
exports.default = void 0;
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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* StateStore stores state and allows an observer to subscribe to state updates
*/
var StateStore = function () {
function StateStore() {
_classCallCheck(this, StateStore);
class StateStore {
constructor() {
this._listeners = [];

@@ -23,34 +18,25 @@ this._state = {};

_createClass(StateStore, [{
key: "update",
value: function update(props) {
// Always create a new state object.
// if lastState === newState then state hasn't changed
this._state = Object.assign({}, this._state, props);
this._listeners.forEach(function (listener) {
return listener(props);
});
}
}, {
key: "subscribeUpdates",
value: function subscribeUpdates(listener) {
this._listeners.push(listener);
}
}, {
key: "unsubscribeUpdates",
value: function unsubscribeUpdates(listener) {
this._listeners = this._listeners.filter(function (l) {
return l !== listener;
});
}
}, {
key: "state",
get: function get() {
return this._state;
}
}]);
get state() {
return this._state;
}
return StateStore;
}();
update(props) {
// Always create a new state object.
// if lastState === newState then state hasn't changed
this._state = Object.assign({}, this._state, props);
exports.default = StateStore;
this._listeners.forEach(listener => listener(props));
}
subscribeUpdates(listener) {
this._listeners.push(listener);
}
unsubscribeUpdates(listener) {
this._listeners = this._listeners.filter(l => l !== listener);
}
}
var _default = StateStore;
exports.default = _default;

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

'use strict';
"use strict";

@@ -6,40 +6,27 @@ Object.defineProperty(exports, "__esModule", {

});
exports.default = void 0;
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 _cuid = _interopRequireDefault(require("cuid"));
var _cuid = require('cuid');
var _webcomponent = _interopRequireDefault(require("webcomponent"));
var _cuid2 = _interopRequireDefault(_cuid);
var _domPatcher = require("./dom-patcher");
var _webcomponent = require('webcomponent');
var _router = _interopRequireDefault(require("./router"));
var _webcomponent2 = _interopRequireDefault(_webcomponent);
var hookHelpers = _interopRequireWildcard(require("./component-utils/hook-helpers"));
var _domPatcher = require('./dom-patcher');
var _perf = require("./component-utils/perf");
var _router = require('./router');
var _shallowEqual = _interopRequireDefault(require("./component-utils/shallowEqual"));
var _router2 = _interopRequireDefault(_router);
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
var _hookHelpers = require('./component-utils/hook-helpers');
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
var hookHelpers = _interopRequireWildcard(_hookHelpers);
var _perf = require('./component-utils/perf');
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: 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); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var DOCUMENT_FRAGMENT_NODE = 11;
var ATTR_TYPE_DEFAULTS = {
string: '',
const DOCUMENT_FRAGMENT_NODE = 11;
const ATTR_TYPE_DEFAULTS = {
string: ``,
boolean: false,

@@ -49,3 +36,4 @@ number: 0,

};
var stylesheetCache = new Map(); // key is the component constructor, value is a CSSStyleSheet instance
const PARAM_TYPES = new Set([Array, String, Boolean, Number, Object, Function, Map, Set]);
const stylesheetCache = new Map(); // key is the component constructor, value is a CSSStyleSheet instance

@@ -81,294 +69,295 @@ /**

var Component = function (_WebComponent) {
_inherits(Component, _WebComponent);
class Component extends _webcomponent.default {
/**
* Defines standard component configuration.
* @type {object}
* @property {function} template - function transforming state object to virtual dom tree
* @property {object} [helpers={}] - properties and functions injected automatically into template state object
* @property {object} [routes={}] - object mapping string route expressions to handler functions
* @property {object} [appState={}] - (app root component only) state object to share with nested descendant components;
* if not set, root component shares entire state object with all descendants
* @property {object} [defaultState={}] - default entries for component state
* @property {object} [hooks={}] - extra rendering/lifecycle callbacks
* @property {function} [hooks.preUpdate] - called before an update is applied
* @property {function} [hooks.postUpdate] - called after an update is applied
* @property {boolean} [updateSync=false] - whether to apply updates to DOM
* immediately, instead of batching to one update per frame
* @property {boolean} [useShadowDom=false] - whether to use Shadow DOM
* @property {string} [css=''] - component-specific Shadow DOM stylesheet
* @example
* get config() {
* return {
* template: state => h('.name', `My name is ${name}`),
* routes: {
* 'wombat/:wombatId': (stateUpdate={}, wombatId) => {
* // route handler implementation
* },
* },
* };
* }
*/
get config() {
return {};
}
/**
* Template helper functions defined in config object, and exposed to template code
* as $helpers. This getter uses the component's internal config cache.
* @type {object}
* @example
* {
* myHelper: () => 'some return value',
* }
*/
_createClass(Component, [{
key: 'child',
get helpers() {
return this.getConfig(`helpers`);
}
/**
* For use inside view templates, to create a child Panel component nested under this
* component, which will share its state object and update cycle.
* @param {string} tagName - the HTML element tag name of the custom element
* to be created
* @param {object} [config={}] - snabbdom node config (second argument of h())
* @returns {object} snabbdom vnode
* @example
* {template: state => h('.header', this.child('my-child-widget'))}
*/
/**
* For use inside view templates, to create a child Panel component nested under this
* component, which will share its state object and update cycle.
* @param {string} tagName - the HTML element tag name of the custom element
* to be created
* @param {object} [config={}] - snabbdom node config (second argument of h())
* @returns {object} snabbdom vnode
* @example
* {template: state => h('.header', this.child('my-child-widget'))}
*/
value: function child(tagName) {
var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
config.props = Object.assign({}, config.props, {
$panelParentID: this.panelID
});
return (0, _domPatcher.h)(tagName, config);
}
child(tagName) {
let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
config.props = Object.assign({}, config.props, {
$panelParentID: this.panelID
});
return (0, _domPatcher.h)(tagName, config);
}
/**
* Searches the component's Panel ancestors for the first component of the
* given type (HTML tag name).
* @param {string} tagName - tag name of the parent to search for
* @returns {object} Panel component
* @throws Throws an error if no parent component with the given tag name is found.
* @example
* myWidget.findPanelParentByTagName('my-app');
*/
/**
* Searches the component's Panel ancestors for the first component of the
* given type (HTML tag name).
* @param {string} tagName - tag name of the parent to search for
* @returns {object} Panel component
* @throws Throws an error if no parent component with the given tag name is found.
* @example
* myWidget.findPanelParentByTagName('my-app');
*/
}, {
key: 'findPanelParentByTagName',
value: function findPanelParentByTagName(tagName) {
tagName = tagName.toLowerCase();
for (var node = this.$panelParent; node; node = node.$panelParent) {
if (node.tagName.toLowerCase() === tagName) {
return node;
}
findPanelParentByTagName(tagName) {
tagName = tagName.toLowerCase();
for (let node = this.$panelParent; node; node = node.$panelParent) {
if (node.tagName.toLowerCase() === tagName) {
return node;
}
throw Error(tagName + ' not found');
}
/**
* Fetches a value from the component's configuration map (a combination of
* values supplied in the config() getter and defaults applied automatically).
* @param {string} key - key of config item to fetch
* @returns value associated with key
* @example
* myWidget.getConfig('css');
*/
throw Error(`${tagName} not found`);
}
/**
* Fetches a value from the component's configuration map (a combination of
* values supplied in the config() getter and defaults applied automatically).
* @param {string} key - key of config item to fetch
* @returns value associated with key
* @example
* myWidget.getConfig('css');
*/
}, {
key: 'getConfig',
value: function getConfig(key) {
return this._config[key];
}
/**
* Executes the route handler matching the given URL fragment, and updates
* the URL, as though the user had navigated explicitly to that address.
* @param {string} fragment - URL fragment to navigate to
* @param {object} [stateUpdate={}] - update to apply to state object when
* routing
* @example
* myApp.navigate('wombat/54', {color: 'blue'});
*/
getConfig(key) {
return this._config[key];
}
/**
* Executes the route handler matching the given URL fragment, and updates
* the URL, as though the user had navigated explicitly to that address.
* @param {string} fragment - URL fragment to navigate to
* @param {object} [stateUpdate={}] - update to apply to state object when
* routing
* @example
* myApp.navigate('wombat/54', {color: 'blue'});
*/
}, {
key: 'navigate',
value: function navigate() {
var _$panelRoot$router;
(_$panelRoot$router = this.$panelRoot.router).navigate.apply(_$panelRoot$router, arguments);
}
navigate() {
this.$panelRoot.router.navigate(...arguments);
}
/**
* Helper function which will queue a function to be run once the component has been
* initialized and added to the DOM. If the component has already had its connectedCallback
* run, the function will run immediately.
*
* It can optionally return a function to be enqueued to be run just before the component is
* removed from the DOM. This occurs during the disconnectedCallback lifecycle.
* @param {function} fn - callback to be run after the component has been added to the DOM. If this
* callback returns another function, the returned function will be run when the component disconnects from the DOM.
* @example
* myApp.onConnected(() => {
* const handleResize = () => calculateSize();
* document.body.addEventListener(`resize`, handleResize);
* return () => document.body.removeEventListener(`resize`, handleResize);
* });
*/
/**
* Helper function which will queue a function to be run once the component has been
* initialized and added to the DOM. If the component has already had its connectedCallback
* run, the function will run immediately.
*
* It can optionally return a function to be enqueued to be run just before the component is
* removed from the DOM. This occurs during the disconnectedCallback lifecycle.
* @param {function} fn - callback to be run after the component has been added to the DOM. If this
* callback returns another function, the returned function will be run when the component disconnects from the DOM.
* @example
* myApp.onConnected(() => {
* const handleResize = () => calculateSize();
* document.body.addEventListener(`resize`, handleResize);
* return () => document.body.removeEventListener(`resize`, handleResize);
* });
*/
}, {
key: 'onConnected',
value: function onConnected(fn) {
if (this.initialized) {
this._maybeEnqueueResult(fn.call(this));
}
this._connectedQueue.push(fn);
onConnected(fn) {
if (this.initialized) {
this._maybeEnqueueResult(fn.call(this));
}
}, {
key: '_maybeEnqueueResult',
value: function _maybeEnqueueResult(result) {
if (result && typeof result === 'function') {
result.removeAfterExec = true;
this._disconnectedQueue.push(result);
}
}
/**
* Helper function which will queue a function to be run just before the component is
* removed from the DOM. This occurs during the disconnectedCallback lifecycle.
*
* @param {function} fn - callback to be run just before the component is removed from the DOM
* @example
* connectedCallback() {
* const shiftKeyListener = () => {
* if (ev.keyCode === SHIFT_KEY_CODE) {
* const doingRangeSelect = ev.type === `keydown` && this.isMouseOver && this.lastSelectedRowIdx !== null;
* if (this.state.doingRangeSelect !== doingRangeSelect) {
* this.update({doingRangeSelect});
* }
* }
* }
* document.body.addEventListener(`keydown`, shiftKeyListener);
* this.onDisconnected(() => {
* document.body.removeEventListener(`keydown`, shiftKeyListener);
* });
* }
*/
this._connectedQueue.push(fn);
}
}, {
key: 'onDisconnected',
value: function onDisconnected(fn) {
this._disconnectedQueue.push(fn);
}
_maybeEnqueueResult(result) {
if (result && typeof result === `function`) {
result.removeAfterExec = true;
/**
* Sets a value in the component's configuration map after element
* initialization.
* @param {string} key - key of config item to set
* @param val - value to associate with key
* @example
* myWidget.setConfig('template', () => h('.new-template', 'Hi'));
*/
}, {
key: 'setConfig',
value: function setConfig(key, val) {
this._config[key] = val;
this._disconnectedQueue.push(result);
}
}
/**
* Helper function which will queue a function to be run just before the component is
* removed from the DOM. This occurs during the disconnectedCallback lifecycle.
*
* @param {function} fn - callback to be run just before the component is removed from the DOM
* @example
* connectedCallback() {
* const shiftKeyListener = () => {
* if (ev.keyCode === SHIFT_KEY_CODE) {
* const doingRangeSelect = ev.type === `keydown` && this.isMouseOver && this.lastSelectedRowIdx !== null;
* if (this.state.doingRangeSelect !== doingRangeSelect) {
* this.update({doingRangeSelect});
* }
* }
* }
* document.body.addEventListener(`keydown`, shiftKeyListener);
* this.onDisconnected(() => {
* document.body.removeEventListener(`keydown`, shiftKeyListener);
* });
* }
*/
/**
* To be overridden by subclasses, defining conditional logic for whether
* a component should rerender its template given the state to be applied.
* In most cases this method can be left untouched, but can provide improved
* performance when dealing with very many DOM elements.
* @param {object} state - state object to be used when rendering
* @returns {boolean} whether or not to render/update this component
* @example
* shouldUpdate(state) {
* // don't need to rerender if result set ID hasn't changed
* return state.largeResultSetID !== this._cachedResultID;
* }
*/
// eslint-disable-next-line no-unused-vars
}, {
key: 'shouldUpdate',
value: function shouldUpdate(state) {
return true;
}
onDisconnected(fn) {
this._disconnectedQueue.push(fn);
}
/**
* Sets a value in the component's configuration map after element
* initialization.
* @param {string} key - key of config item to set
* @param val - value to associate with key
* @example
* myWidget.setConfig('template', () => h('.new-template', 'Hi'));
*/
/**
* Applies a state update, triggering a re-render check of the component as
* well as any other components sharing the same state. This is the primary
* means of updating the DOM in a Panel application.
* @param {object|function} [stateUpdate={}] - keys and values of entries to update in
* the component's state object
* @example
* myWidget.update({name: 'Bob'});
*/
}, {
key: 'update',
value: function update() {
var stateUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
setConfig(key, val) {
this._config[key] = val;
}
/**
* To be overridden by subclasses, defining conditional logic for whether
* a component should rerender its template given the state to be applied.
* In most cases this method can be left untouched, but can provide improved
* performance when dealing with very many DOM elements.
*
* @deprecated use shouldComponentUpdate instead
* @param {object} state - state object to be used when rendering
* @returns {boolean} whether or not to render/update this component
* @example
* shouldUpdate(state) {
* // don't need to rerender if result set ID hasn't changed
* return state.largeResultSetID !== this._cachedResultID;
* }
*/
// eslint-disable-next-line no-unused-vars
this.timings.lastUpdateAt = (0, _perf.getNow)();
var stateUpdateResult = typeof stateUpdate === 'function' ? stateUpdate(this.state) : stateUpdate;
return this._updateStore(stateUpdateResult, {
store: 'state',
cascade: this.isStateShared
});
}
shouldUpdate(state) {
return true;
}
/**
*
* Same API as react's `shouldComponentUpdate` usage
* if child component implements this method, parent implmentation wil be discarded
* NOTE: never call `super` in child `shouldComponentUpdate`
*
* there a slight difference with react: `params` or `state` could sometimes be null indicating that
* the update is not related to `params` or `state`
*
* @param {object} params - new params object to be used when rendering
* @param {object} state - state object to be used when rendering
* @return {boolean}
* @example
* shouldComponentUpdate(params, state) {
* if (params.bookmark.id === this.params.bookmark.id) {
* return false;
* }
* return !shallowEqual(params, this.params);
* }
*/
/**
* Applies a state update specifically to app state shared across components.
* In apps which don't specify `appState` in the root component config, all
* state is shared across all parent and child components and the standard
* update() method should be used instead.
* @param {object} [stateUpdate={}] - keys and values of entries to update in
* the app's appState object
* @example
* myWidget.updateApp({name: 'Bob'});
*/
}, {
key: 'updateApp',
value: function updateApp() {
var stateUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return this._updateStore(stateUpdate, { store: 'appState', cascade: true });
shouldComponentUpdate(params, state) {
if (params) {
return !(0, _shallowEqual.default)(params, this.params);
}
}, {
key: 'config',
/**
* Defines standard component configuration.
* @type {object}
* @property {function} template - function transforming state object to virtual dom tree
* @property {object} [helpers={}] - properties and functions injected automatically into template state object
* @property {object} [routes={}] - object mapping string route expressions to handler functions
* @property {object} [appState={}] - (app root component only) state object to share with nested descendant components;
* if not set, root component shares entire state object with all descendants
* @property {object} [defaultState={}] - default entries for component state
* @property {object} [hooks={}] - extra rendering/lifecycle callbacks
* @property {function} [hooks.preUpdate] - called before an update is applied
* @property {function} [hooks.postUpdate] - called after an update is applied
* @property {boolean} [updateSync=false] - whether to apply updates to DOM
* immediately, instead of batching to one update per frame
* @property {boolean} [useShadowDom=false] - whether to use Shadow DOM
* @property {string} [css=''] - component-specific Shadow DOM stylesheet
* @example
* get config() {
* return {
* template: state => h('.name', `My name is ${name}`),
* routes: {
* 'wombat/:wombatId': (stateUpdate={}, wombatId) => {
* // route handler implementation
* },
* },
* };
* }
*/
get: function get() {
return {};
}
return this.shouldUpdate(state);
}
/**
* Applies a state update, triggering a re-render check of the component as
* well as any other components sharing the same state. This is the primary
* means of updating the DOM in a Panel application.
* @param {object|function} [stateUpdate={}] - keys and values of entries to update in
* the component's state object
* @example
* myWidget.update({name: 'Bob'});
*/
/**
* Template helper functions defined in config object, and exposed to template code
* as $helpers. This getter uses the component's internal config cache.
* @type {object}
* @example
* {
* myHelper: () => 'some return value',
* }
*/
}, {
key: 'helpers',
get: function get() {
return this.getConfig('helpers');
}
}]);
update() {
let stateUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.timings.lastUpdateAt = _perf.Perf.getNow();
const stateUpdateResult = typeof stateUpdate === `function` ? stateUpdate(this.state) : stateUpdate;
return this._updateStore(stateUpdateResult, {
store: `state`,
cascade: this.isStateShared
});
}
/**
* Applies a state update specifically to app state shared across components.
* In apps which don't specify `appState` in the root component config, all
* state is shared across all parent and child components and the standard
* update() method should be used instead.
* @param {object} [stateUpdate={}] - keys and values of entries to update in
* the app's appState object
* @example
* myWidget.updateApp({name: 'Bob'});
*/
function Component() {
_classCallCheck(this, Component);
var _this = _possibleConstructorReturn(this, (Component.__proto__ || Object.getPrototypeOf(Component)).call(this));
updateApp() {
let stateUpdate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return this._updateStore(stateUpdate, {
store: `appState`,
cascade: true
});
}
_this.timings = {
createdAt: (0, _perf.getNow)()
constructor() {
super();
this.timings = {
createdAt: _perf.Perf.getNow()
};
this.panelID = (0, _cuid.default)();
this._connectedQueue = [];
this._disconnectedQueue = [];
this._attrs = {};
_this.panelID = (0, _cuid2.default)();
this._syncAttrs(); // constructor sync ensures default properties are present on this._attrs
_this._connectedQueue = [];
_this._disconnectedQueue = [];
_this._attrs = {};
_this._syncAttrs(); // constructor sync ensures default properties are present on this._attrs
_this._config = Object.assign({}, {
css: '',
this._config = Object.assign({}, {
css: ``,
params: {},
defaultParams: {},
defaultContexts: {},

@@ -378,4 +367,4 @@ contexts: [],

routes: {},
template: function template() {
throw Error('No template provided by Component subclass');
template: () => {
throw Error(`No template provided by Component subclass`);
},

@@ -385,49 +374,55 @@ updateSync: false,

slowThreshold: 20
}, _this.config);
}, this.config);
_this._contexts = new Set(_this.getConfig('contexts'));
this._initializeParams();
// initialize shared state store, either in `appState` or default to `state`
this._contexts = new Set(this.getConfig(`contexts`)); // initialize shared state store, either in `appState` or default to `state`
// appState and isStateShared of child components will be overwritten by parent/root
// when the component is connected to the hierarchy
_this.state = Object.assign({}, _this.getConfig('defaultState'));
_this.appState = _this.getConfig('appState');
if (!_this.appState) {
_this.appState = {};
_this.isStateShared = true;
this.state = Object.assign({}, this.getConfig(`defaultState`));
this.appState = this.getConfig(`appState`);
if (!this.appState) {
this.appState = {};
this.isStateShared = true;
} else {
_this.isStateShared = false;
this.isStateShared = false;
}
if (_this.getConfig('useShadowDom')) {
_this.el = _this.attachShadow({ mode: 'open' });
_this.applyStaticStyle(_this.getConfig('css'));
} else if (_this.getConfig('css')) {
throw Error('"useShadowDom" config option must be set in order to use "css" config.');
if (this.getConfig(`useShadowDom`)) {
this.el = this.attachShadow({
mode: `open`
});
this.applyStaticStyle(this.getConfig(`css`));
} else if (this.getConfig(`css`)) {
throw Error(`"useShadowDom" config option must be set in order to use "css" config.`);
} else {
_this.el = _this;
this.el = this;
}
_this.postRenderCallback = function (elapsedMs) {
_this.timings.lastRenderAt = (0, _perf.getNow)();
if (elapsedMs > _this.getConfig('slowThreshold')) {
var shouldBroadcast = !_this.lastSlowRender || // SHOULD because we've never slow rendered
_this.lastSlowRender.time - (0, _perf.getNow)() > 3000 || // SHOULD because last time was more than three seconds ago
elapsedMs > (_this.slowestRenderMs || 0); // SHOULD because this time is slower
this.postRenderCallback = elapsedMs => {
this.timings.lastRenderAt = _perf.Perf.getNow();
if (elapsedMs > this.getConfig(`slowThreshold`)) {
const shouldBroadcast = !this.lastSlowRender || // SHOULD because we've never slow rendered
this.lastSlowRender.time - _perf.Perf.getNow() > 3000 || // SHOULD because last time was more than three seconds ago
elapsedMs > (this.slowestRenderMs || 0); // SHOULD because this time is slower
if (shouldBroadcast) {
var comparedToLast = _this.lastSlowRender ? {
const comparedToLast = this.lastSlowRender ? {
// bit of a hack to get the number to only 2 digits of precision
comparedToLast: +((elapsedMs - _this.lastSlowRender.elapsedMs) / _this.lastSlowRender.elapsedMs).toFixed(2),
comparedToSlowest: +((elapsedMs - _this.slowestRenderMs) / _this.slowestRenderMs).toFixed(2)
comparedToLast: +((elapsedMs - this.lastSlowRender.elapsedMs) / this.lastSlowRender.elapsedMs).toFixed(2),
comparedToSlowest: +((elapsedMs - this.slowestRenderMs) / this.slowestRenderMs).toFixed(2)
} : undefined;
_this.lastSlowRender = {
time: (0, _perf.getNow)(),
elapsedMs: elapsedMs
this.lastSlowRender = {
time: _perf.Perf.getNow(),
elapsedMs
};
_this.slowestRenderMs = Math.max(_this.slowestRenderMs || 0, elapsedMs);
_this.dispatchEvent(new CustomEvent('slowRender', {
detail: Object.assign(comparedToLast || {}, { elapsedMs: elapsedMs, component: _this.toString() }),
this.slowestRenderMs = Math.max(this.slowestRenderMs || 0, elapsedMs);
this.dispatchEvent(new CustomEvent(`slowRender`, {
detail: Object.assign(comparedToLast || {}, {
elapsedMs,
component: this.toString()
}),
bubbles: true,

@@ -439,660 +434,626 @@ composed: true

};
return _this;
}
_createClass(Component, [{
key: 'connectedCallback',
value: function connectedCallback() {
if (this.initialized) {
return;
}
connectedCallback() {
if (this.initialized) {
return;
} // Prevent re-entrant calls to connectedCallback.
// This can happen in some (probably erroneous) cases with Firefox+polyfills.
// Prevent re-entrant calls to connectedCallback.
// This can happen in some (probably erroneous) cases with Firefox+polyfills.
if (this.initializing) {
return;
}
this.initializing = true;
this.timings.initializingStartedAt = (0, _perf.getNow)();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
if (this.initializing) {
return;
}
try {
for (var _iterator = Object.keys(this._attrsSchema)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var attrsSchemaKey = _step.value;
this.initializing = true;
this.timings.initializingStartedAt = _perf.Perf.getNow();
if (!Object.prototype.hasOwnProperty.call(this._attrs, attrsSchemaKey) && this._attrsSchema[attrsSchemaKey].required) {
throw new Error(this + ': is missing required attr \'' + attrsSchemaKey + '\'');
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
for (const attrsSchemaKey of Object.keys(this._attrsSchema)) {
if (!Object.prototype.hasOwnProperty.call(this._attrs, attrsSchemaKey) && this._attrsSchema[attrsSchemaKey].required) {
throw new Error(`${this}: is missing required attr '${attrsSchemaKey}'`);
}
}
this.$panelChildren = new Set();
this.$panelChildren = new Set();
if (typeof this.$panelParentID !== 'undefined') {
this.isPanelChild = true;
// find $panelParent
for (var node = this.parentNode; node && !this.$panelParent; node = node.parentNode) {
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
// handle shadow-root
node = node.host;
}
if (node.panelID === this.$panelParentID) {
this.$panelParent = node;
this.$panelRoot = node.$panelRoot;
}
if (typeof this.$panelParentID !== `undefined`) {
this.isPanelChild = true; // find $panelParent
for (let node = this.parentNode; node && !this.$panelParent; node = node.parentNode) {
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
// handle shadow-root
node = node.host;
}
if (!this.$panelParent) {
throw Error('panelParent ' + this.$panelParentID + ' not found');
}
this.$panelParent.$panelChildren.add(this);
// share either appState or all of state
// flush any queued appState changes
this.appState = Object.assign(this.$panelRoot.appState, this.appState);
// if child element state is shared, point
// state to parent's state object and flush any
// queued state changes to the parent state
this.isStateShared = this.$panelRoot.isStateShared;
if (this.isStateShared) {
this.state = Object.assign(this.$panelRoot.state, this.state);
if (node.panelID === this.$panelParentID) {
this.$panelParent = node;
this.$panelRoot = node.$panelRoot;
}
} else {
this.isPanelRoot = true;
this.$panelRoot = this;
this.$panelParent = null;
}
this.app = this.$panelRoot;
Object.assign(this.state, this.getJSONAttribute('data-state'), this._stateFromAttributes());
if (Object.keys(this.getConfig('routes')).length) {
this.router = new _router2.default(this, { historyMethod: this.historyMethod });
this.navigate(window.location.hash);
if (!this.$panelParent) {
throw Error(`panelParent ${this.$panelParentID} not found`);
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
this.$panelParent.$panelChildren.add(this); // share either appState or all of state
// flush any queued appState changes
try {
for (var _iterator2 = this.getConfig('contexts')[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var contextName = _step2.value;
this.appState = Object.assign(this.$panelRoot.appState, this.appState); // if child element state is shared, point
// state to parent's state object and flush any
// queued state changes to the parent state
var context = this.getContext(contextName);
// Context classes can implement an optional `bindToComponent` callback that executes each time the component is connected to the DOM
if (context.bindToComponent) {
context.bindToComponent(this);
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
this.isStateShared = this.$panelRoot.isStateShared;
if (this.isStateShared) {
this.state = Object.assign(this.$panelRoot.state, this.state);
}
} else {
this.isPanelRoot = true;
this.$panelRoot = this;
this.$panelParent = null;
}
this.domPatcher = new _domPatcher.DOMPatcher(this.state, this._render.bind(this), {
updateMode: this.getConfig('updateSync') ? 'sync' : 'async',
postRenderCallback: this.postRenderCallback
this.app = this.$panelRoot;
Object.assign(this.state, this.getJSONAttribute(`data-state`), this._stateFromAttributes());
if (Object.keys(this.getConfig(`routes`)).length) {
this.router = new _router.default(this, {
historyMethod: this.historyMethod
});
this.el.appendChild(this.domPatcher.el);
this.navigate(window.location.hash);
}
for (var i = 0; i < this._connectedQueue.length; i++) {
var connectedCallbackFn = this._connectedQueue[i];
try {
this._maybeEnqueueResult(connectedCallbackFn.call(this));
} catch (err) {
console.warn('error running onConnected function', err);
}
for (const contextName of this.getConfig(`contexts`)) {
const context = this.getContext(contextName); // Context classes can implement an optional `bindToComponent` callback that executes each time the component is connected to the DOM
if (context.bindToComponent) {
context.bindToComponent(this);
}
this.initialized = true;
this.initializing = false;
this.timings.initializingCompletedAt = (0, _perf.getNow)();
this.dispatchEvent(new CustomEvent('componentInitialized', {
detail: {
elapsedMs: this.timings.initializingCompletedAt - this.timings.initializingStartedAt,
component: this.toString()
},
bubbles: true,
composed: true
}));
}
}, {
key: 'disconnectedCallback',
value: function disconnectedCallback() {
var _this2 = this;
if (!this.initialized) {
return;
}
this.domPatcher = new _domPatcher.DOMPatcher(this.state, this._render.bind(this), {
updateMode: this.getConfig(`updateSync`) ? `sync` : `async`,
postRenderCallback: this.postRenderCallback
});
this.el.appendChild(this.domPatcher.el);
for (var i = 0; i < this._disconnectedQueue.length; i++) {
var disconnectedCallbackFn = this._disconnectedQueue[i];
try {
disconnectedCallbackFn.call(this);
} catch (err) {
console.warn('error running onDisconnected function', err);
}
for (let i = 0; i < this._connectedQueue.length; i++) {
const connectedCallbackFn = this._connectedQueue[i];
try {
this._maybeEnqueueResult(connectedCallbackFn.call(this));
} catch (err) {
console.warn(`error running onConnected function`, err);
}
}
this._disconnectedQueue = this._disconnectedQueue.filter(function (fn) {
return !fn.removeAfterExec;
});
this.initialized = true;
this.initializing = false;
this.timings.initializingCompletedAt = _perf.Perf.getNow();
this.dispatchEvent(new CustomEvent(`componentInitialized`, {
detail: {
elapsedMs: this.timings.initializingCompletedAt - this.timings.initializingStartedAt,
component: this.toString()
},
bubbles: true,
composed: true
}));
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
disconnectedCallback() {
if (!this.initialized) {
return;
}
for (let i = 0; i < this._disconnectedQueue.length; i++) {
const disconnectedCallbackFn = this._disconnectedQueue[i];
try {
for (var _iterator3 = this.getConfig('contexts')[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var contextName = _step3.value;
var context = this.getContext(contextName);
// Context classes can implement an optional `unbindFromComponent` callback that executes each time the component is disconnected from the DOM
if (context.unbindFromComponent) {
context.unbindFromComponent(this);
}
}
disconnectedCallbackFn.call(this);
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
console.warn(`error running onDisconnected function`, err);
}
}
if (this.router) {
this.router.unregisterListeners();
}
this._disconnectedQueue = this._disconnectedQueue.filter(fn => !fn.removeAfterExec);
if (this.$panelParent) {
this.$panelParent.$panelChildren.delete(this);
}
for (const contextName of this.getConfig(`contexts`)) {
const context = this.getContext(contextName); // Context classes can implement an optional `unbindFromComponent` callback that executes each time the component is disconnected from the DOM
if (this.domPatcher) {
this.el.removeChild(this.domPatcher.el);
this.domPatcher.disconnect();
if (context.unbindFromComponent) {
context.unbindFromComponent(this);
}
}
this.domPatcher = null;
this._rendered = null;
this.initialized = false;
if (this.router) {
this.router.unregisterListeners();
}
// if a child component is added via child() and has keys, snabbdom uses parentEl.insertBefore
// which disconnects the element and immediately connects it at another position.
// usually the child's disconnectedCallback is called before the parent's
// but in that case the parents are removed from dom before the children
// which causes a $panelParent not found exception for the grandchildren.
// we clean up parent references in an async manner so we can handle that situation.
Promise.resolve().then(function () {
// only clear references if element hasn't been re-initialized
if (!_this2.initialized) {
_this2.$panelRoot = null;
_this2.$panelParent = null;
_this2.appState = null;
_this2.app = null;
}
});
if (this.$panelParent) {
this.$panelParent.$panelChildren.delete(this);
}
/**
* Attributes schema that defines the component's html attributes and their types
* Panel auto parses attribute changes into attrs() object and $attr template helper
*
* @typedef {object} AttrSchema
* @prop {'string' | 'number' | 'boolean' | 'json'} type - type of the attribute
* if not set, the attr parser will interpret it as 'string'
* @prop {string} default - value if the attr is not defined
* @prop {number} description - description of the attribute, what it does e.t.c
*
* @type {Object.<string, AttrSchema>}
*/
if (this.domPatcher) {
this.el.removeChild(this.domPatcher.el);
this.domPatcher.disconnect();
}
}, {
key: 'attributeChangedCallback',
value: function attributeChangedCallback(attr, oldVal, newVal) {
this.timings.lastAttributeChangedAt = (0, _perf.getNow)();
this._updateAttr(attr);
this.domPatcher = null;
this._rendered = null;
this.initialized = false; // if a child component is added via child() and has keys, snabbdom uses parentEl.insertBefore
// which disconnects the element and immediately connects it at another position.
// usually the child's disconnectedCallback is called before the parent's
// but in that case the parents are removed from dom before the children
// which causes a $panelParent not found exception for the grandchildren.
// we clean up parent references in an async manner so we can handle that situation.
if (attr === 'style-override') {
this._applyStyleOverride(newVal);
Promise.resolve().then(() => {
// only clear references if element hasn't been re-initialized
if (!this.initialized) {
this.$panelRoot = null;
this.$panelParent = null;
this.appState = null;
this.app = null;
}
});
}
/**
* Attributes schema that defines the component's html attributes and their types
* Panel auto parses attribute changes into attrs() object and $attr template helper
*
* @typedef {object} AttrSchema
* @prop {'string' | 'number' | 'boolean' | 'json'} type - type of the attribute
* if not set, the attr parser will interpret it as 'string'
* @prop {string} default - value if the attr is not defined
* @prop {number} description - description of the attribute, what it does e.t.c
*
* @type {Object.<string, AttrSchema>}
*/
if (this.initialized) {
this.update();
}
static get attrsSchema() {
return {};
}
static get observedAttributes() {
return [`style-override`].concat(Object.keys(this.attrsSchema));
}
attributeChangedCallback(attr, oldVal, newVal) {
this.timings.lastAttributeChangedAt = _perf.Perf.getNow();
this._updateAttr(attr);
if (attr === `style-override`) {
this._applyStyleOverride(newVal);
}
}, {
key: 'applyStaticStyle',
value: function applyStaticStyle(styleSheetText) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$ignoreCache = _ref.ignoreCache,
ignoreCache = _ref$ignoreCache === undefined ? false : _ref$ignoreCache;
if (styleSheetText) {
if (this.el.adoptedStyleSheets) {
// Attempt to cache the styles using Constructible StyleSheets if the feature is supported.
// Note: this technique avoids the Flash of Unstyled Content that alternative approaches like <link> tags will encounter
var componentKey = this.constructor;
var cachedStyleSheet = stylesheetCache.get(componentKey);
if (!cachedStyleSheet) {
cachedStyleSheet = new CSSStyleSheet();
cachedStyleSheet.replaceSync(styleSheetText);
stylesheetCache.set(componentKey, cachedStyleSheet);
} else if (ignoreCache) {
cachedStyleSheet.replaceSync(styleSheetText);
}
if (!this.staticStyleSheet) {
this.staticStyleSheet = cachedStyleSheet;
this.el.adoptedStyleSheets = [this.staticStyleSheet].concat(_toConsumableArray(this.el.adoptedStyleSheets.slice(1)));
}
} else {
if (!this.staticStyleTag) {
this.staticStyleTag = document.createElement('style');
this.el.insertBefore(this.staticStyleTag, this.el.childNodes[0] || null);
}
this.staticStyleTag.innerHTML = styleSheetText;
if (this.initialized) {
this.update();
}
}
applyStaticStyle(styleSheetText) {
let {
ignoreCache = false
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (styleSheetText) {
if (this.el.adoptedStyleSheets) {
// Attempt to cache the styles using Constructible StyleSheets if the feature is supported.
// Note: this technique avoids the Flash of Unstyled Content that alternative approaches like <link> tags will encounter
const componentKey = this.constructor;
let cachedStyleSheet = stylesheetCache.get(componentKey);
if (!cachedStyleSheet) {
cachedStyleSheet = new CSSStyleSheet();
cachedStyleSheet.replaceSync(styleSheetText);
stylesheetCache.set(componentKey, cachedStyleSheet);
} else if (ignoreCache) {
cachedStyleSheet.replaceSync(styleSheetText);
}
if (!this.staticStyleSheet) {
this.staticStyleSheet = cachedStyleSheet;
this.el.adoptedStyleSheets = [this.staticStyleSheet, ...this.el.adoptedStyleSheets.slice(1)];
}
} else {
if (!this.staticStyleTag) {
this.staticStyleTag = document.createElement(`style`);
this.el.insertBefore(this.staticStyleTag, this.el.childNodes[0] || null);
}
this.staticStyleTag.innerHTML = styleSheetText;
}
}
}, {
key: '_applyStyleOverride',
value: function _applyStyleOverride(styleOverride) {
if (this.getConfig('useShadowDom')) {
if (this.el.adoptedStyleSheets) {
if (!this.styleOverrideStyleSheet) {
this.styleOverrideStyleSheet = new CSSStyleSheet();
this.el.adoptedStyleSheets = this.el.adoptedStyleSheets.concat(this.styleOverrideStyleSheet);
}
this.styleOverrideStyleSheet.replaceSync(styleOverride || '');
} else {
if (!this.styleOverrideTag) {
this.styleOverrideTag = document.createElement('style');
this.el.appendChild(this.styleOverrideTag);
}
this.styleOverrideTag.innerHTML = styleOverride || '';
}
_applyStyleOverride(styleOverride) {
if (this.getConfig(`useShadowDom`)) {
if (this.el.adoptedStyleSheets) {
if (!this.styleOverrideStyleSheet) {
this.styleOverrideStyleSheet = new CSSStyleSheet();
this.el.adoptedStyleSheets = this.el.adoptedStyleSheets.concat(this.styleOverrideStyleSheet);
}
this.styleOverrideStyleSheet.replaceSync(styleOverride || ``);
} else {
if (!this.styleOverrideTag) {
this.styleOverrideTag = document.createElement(`style`);
this.el.appendChild(this.styleOverrideTag);
}
this.styleOverrideTag.innerHTML = styleOverride || ``;
}
}
}, {
key: '_logError',
value: function _logError() {
var _console;
}
(_console = console).error.apply(_console, arguments);
_logError() {
console.error(...arguments);
}
toString() {
try {
return `${(this.tagName || ``).toLowerCase()}#${this.panelID}`;
} catch (e) {
return `UNKNOWN COMPONENT`;
}
}, {
key: 'toString',
value: function toString() {
}
_render(state) {
if (this.shouldComponentUpdate(null, state)) {
try {
return (this.tagName || '').toLowerCase() + '#' + this.panelID;
} catch (e) {
return 'UNKNOWN COMPONENT';
this._rendered = this.getConfig(`template`).call(this, Object.assign({}, state, {
$app: this.appState,
$component: this,
$helpers: this.helpers,
$attr: this.attr.bind(this),
$hooks: hookHelpers
}));
} catch (error) {
this._logError(`Error while rendering`, this, `\n`, error);
this.dispatchEvent(new CustomEvent(`renderError`, {
detail: {
error,
component: this
},
bubbles: true,
composed: true
}));
}
}
}, {
key: '_render',
value: function _render(state) {
if (this.shouldUpdate(state)) {
try {
this._rendered = this.getConfig('template').call(this, Object.assign({}, state, {
$app: this.appState,
$component: this,
$helpers: this.helpers,
$attr: this.attr.bind(this),
$hooks: hookHelpers
}));
} catch (error) {
this._logError('Error while rendering', this, '\n', error);
this.dispatchEvent(new CustomEvent('renderError', {
detail: { error: error, component: this },
bubbles: true,
composed: true
}));
return this._rendered || _domPatcher.EMPTY_DIV;
} // run a user-defined hook with the given params, if configured
// cascade down tree hierarchy if option is set
runHook(hookName, options) {
if (!this.initialized) {
return;
}
const hook = (this.getConfig(`hooks`) || {})[hookName];
for (var _len = arguments.length, params = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
params[_key - 2] = arguments[_key];
}
if (hook) {
hook(...params);
}
if (options.cascade) {
for (const child of this.$panelChildren) {
if (options.exclude !== child) {
child.runHook(hookName, options, ...params);
}
}
return this._rendered || _domPatcher.EMPTY_DIV;
}
}
// run a user-defined hook with the given params, if configured
// cascade down tree hierarchy if option is set
_stateFromAttributes() {
const state = {}; // this.attributes is a NamedNodeMap, without normal iterators
}, {
key: 'runHook',
value: function runHook(hookName, options) {
if (!this.initialized) {
return;
for (let ai = 0; ai < this.attributes.length; ai++) {
const attr = this.attributes[ai];
const attrMatch = attr.name.match(/^state-(.+)/);
if (attrMatch) {
const num = Number(attr.value);
state[attrMatch[1]] = isNaN(num) ? attr.value : num;
}
}
var hook = (this.getConfig('hooks') || {})[hookName];
return state;
}
/**
* Validates attrsSchema and syncs element attributes defined in attrsSchema
*/
for (var _len = arguments.length, params = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
params[_key - 2] = arguments[_key];
_syncAttrs() {
// maintain local validated map where all schema keys are defined
this._attrsSchema = {};
const attrsSchema = this.constructor.attrsSchema;
for (const attr of Object.keys(attrsSchema)) {
// convert type shorthand to object
let attrSchema = attrsSchema[attr];
if (typeof attrSchema === `string`) {
attrSchema = {
type: attrSchema
};
} // Ensure attr type is valid
const attrType = attrSchema.type;
if (!ATTR_TYPE_DEFAULTS.hasOwnProperty(attrType)) {
throw new Error(`Invalid type: ${attrType} for attr: ${attr} in attrsSchema. ` + `Only (${Object.keys(ATTR_TYPE_DEFAULTS).map(v => `'${v}'`).join(` | `)}) is valid.`);
}
if (hook) {
hook.apply(undefined, params);
if (attrSchema.default && attrSchema.required) {
throw new Error(`${this}: attr '${attr}' cannot have both required and default`);
}
if (options.cascade) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = this.$panelChildren[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var child = _step4.value;
const attrSchemaObj = {
type: attrType,
default: attrSchema.hasOwnProperty(`default`) ? attrSchema.default : ATTR_TYPE_DEFAULTS[attrType],
required: attrSchema.hasOwnProperty(`required`) ? attrSchema.required : false
}; // convert enum to a set for perf
if (options.exclude !== child) {
child.runHook.apply(child, [hookName, options].concat(params));
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
if (attrSchema.hasOwnProperty(`enum`)) {
const attrEnum = attrSchema.enum;
if (!Array.isArray(attrEnum)) {
throw new Error(`Enum not an array for attr: ${attr}`);
}
const enumSet = new Set(attrEnum);
enumSet.add(attrSchema.default);
attrSchemaObj.enumSet = enumSet;
}
this._attrsSchema[attr] = attrSchemaObj;
this._updateAttr(attr); // updated at end so we don't console.warn on initial sync
attrSchemaObj.deprecatedMsg = attrSchema.deprecatedMsg;
}
}, {
key: '_stateFromAttributes',
value: function _stateFromAttributes() {
var state = {};
// this.attributes is a NamedNodeMap, without normal iterators
for (var ai = 0; ai < this.attributes.length; ai++) {
var attr = this.attributes[ai];
var attrMatch = attr.name.match(/^state-(.+)/);
if (attrMatch) {
var num = Number(attr.value);
state[attrMatch[1]] = isNaN(num) ? attr.value : num;
return this._attrs;
}
/**
* Parses html attribute using type information from attrsSchema and updates this._attrs
* @param {string} attr - attribute name
*/
_updateAttr(attr) {
const attrsSchema = this._attrsSchema;
if (attrsSchema.hasOwnProperty(attr)) {
const attrSchema = attrsSchema[attr];
const attrType = attrSchema.type;
let attrValue = null;
if (attrSchema.deprecatedMsg) {
console.warn(`${this}: attr '${attr}' is deprecated. ${attrSchema.deprecatedMsg}`);
}
if (!this.hasAttribute(attr)) {
if (attrType === `boolean` && (attrSchema.default || attrSchema.required)) {
throw new Error(`${this}: boolean attr '${attr}' cannot have required or default, since its value is derived from whether dom element has the attribute, not its value`);
}
if (attrSchema.required) {
// Early return because a required attribute has no explicit value
return;
}
attrValue = attrSchema.default;
} else if (attrType === `string`) {
attrValue = this.getAttribute(attr);
const enumSet = attrSchema.enumSet;
if (enumSet && !enumSet.has(attrValue)) {
throw new Error(`Invalid value: '${attrValue}' for attr: ${attr}. ` + `Only (${Array.from(enumSet).map(v => `'${v}'`).join(` | `)}) is valid.`);
}
} else if (attrType === `boolean`) {
attrValue = this.isAttributeEnabled(attr);
} else if (attrType === `number`) {
attrValue = this.getNumberAttribute(attr);
} else if (attrType === `json`) {
attrValue = this.getJSONAttribute(attr);
}
return state;
this._attrs[attr] = attrValue;
}
}
/**
* gets the parsed value of an attribute
* @param {string} attr - attribute name
*/
/**
* Validates attrsSchema and syncs element attributes defined in attrsSchema
*/
}, {
key: '_syncAttrs',
value: function _syncAttrs() {
// maintain local validated map where all schema keys are defined
this._attrsSchema = {};
var attrsSchema = this.constructor.attrsSchema;
attr(attr) {
if (attr in this._attrs) {
return this._attrs[attr];
} else {
throw new TypeError(`${this}: attr '${attr}' is not defined in attrsSchema`);
}
}
/**
* Returns the parsed attrs as a key-value POJO
* @returns {object} parsed attribute values from attrsSchema
*/
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = Object.keys(attrsSchema)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var attr = _step5.value;
attrs() {
return this._attrs;
}
/**
* parse and validate config.params and create a param schema on the component
*/
// convert type shorthand to object
var attrSchema = attrsSchema[attr];
if (typeof attrSchema === 'string') {
attrSchema = { type: attrSchema };
}
// Ensure attr type is valid
var attrType = attrSchema.type;
if (!ATTR_TYPE_DEFAULTS.hasOwnProperty(attrType)) {
throw new Error('Invalid type: ' + attrType + ' for attr: ' + attr + ' in attrsSchema. ' + ('Only (' + Object.keys(ATTR_TYPE_DEFAULTS).map(function (v) {
return '\'' + v + '\'';
}).join(' | ') + ') is valid.'));
}
_initializeParams() {
// the real value for the params
this._params = {}; // maintain local validated map where all schema keys are defined
if (attrSchema.default && attrSchema.required) {
throw new Error(this + ': attr \'' + attr + '\' cannot have both required and default');
}
this._paramSchemas = {};
const paramSchemas = this.getConfig(`params`);
const defaultParams = this.getConfig(`defaultParams`);
var attrSchemaObj = {
type: attrType,
default: attrSchema.hasOwnProperty('default') ? attrSchema.default : ATTR_TYPE_DEFAULTS[attrType],
required: attrSchema.hasOwnProperty('required') ? attrSchema.required : false
};
for (let [paramName, paramSchema] of Object.entries(paramSchemas)) {
// convert type shorthand to object
if (!paramSchema.type) {
paramSchema = {
type: paramSchema
};
} // Ensure param type is valid
// convert enum to a set for perf
if (attrSchema.hasOwnProperty('enum')) {
var attrEnum = attrSchema.enum;
if (!Array.isArray(attrEnum)) {
throw new Error('Enum not an array for attr: ' + attr);
}
var enumSet = new Set(attrEnum);
enumSet.add(attrSchema.default);
attrSchemaObj.enumSet = enumSet;
}
const type = paramSchema.type;
this._attrsSchema[attr] = attrSchemaObj;
this._updateAttr(attr);
// updated at end so we don't console.warn on initial sync
attrSchemaObj.deprecatedMsg = attrSchema.deprecatedMsg;
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
if (!PARAM_TYPES.has(type)) {
const typeString = typeof type === `function` ? type.name : String(type);
throw new Error(`Invalid type: ${typeString} for param: ${paramName} in paramSchema. ` + `Only (${Array.from(PARAM_TYPES.keys()).map(v => `'${v.name}'`).join(` | `)}) is valid.`);
}
return this._attrs;
const paramSchemaObj = {
type,
required: Boolean(paramSchema.required),
default: defaultParams[paramName]
}; // set default value for the params
this._params[paramName] = paramSchemaObj.default;
this._paramSchemas[paramName] = paramSchemaObj;
}
/**
* Parses html attribute using type information from attrsSchema and updates this._attrs
* @param {string} attr - attribute name
*/
Object.freeze(this._params);
return this._paramSchemas;
}
}, {
key: '_updateAttr',
value: function _updateAttr(attr) {
var attrsSchema = this._attrsSchema;
if (attrsSchema.hasOwnProperty(attr)) {
var attrSchema = attrsSchema[attr];
var attrType = attrSchema.type;
var attrValue = null;
get params() {
return this._params;
}
if (attrSchema.deprecatedMsg) {
console.warn(this + ': attr \'' + attr + '\' is deprecated. ' + attrSchema.deprecatedMsg);
}
setParams(params) {
const shouldComponentUpdate = this.shouldComponentUpdate(params, this.state);
const updateOptions = {
cascade: false
}; // no extra params allowed if not defined in schema
if (!this.hasAttribute(attr)) {
if (attrType === 'boolean' && (attrSchema.default || attrSchema.required)) {
throw new Error(this + ': boolean attr \'' + attr + '\' cannot have required or default, since its value is derived from whether dom element has the attribute, not its value');
}
for (const paramName of Object.keys(params)) {
if (!this._paramSchemas[paramName]) {
throw new Error(`extra param '${paramName}' on ${this.constructor.name} is not defined in schema`);
}
}
if (attrSchema.required) {
// Early return because a required attribute has no explicit value
return;
}
attrValue = attrSchema.default;
} else if (attrType === 'string') {
attrValue = this.getAttribute(attr);
var enumSet = attrSchema.enumSet;
for (const [paramName, paramSchema] of Object.entries(this._paramSchemas)) {
// if param defined on schema and marked required, the key must be presented on the params
if (!params.hasOwnProperty(paramName) && paramSchema.required) {
throw new Error(`param '${paramName}' on ${this.constructor.name} is defined as required param in schema but absent on component definition`);
}
if (enumSet && !enumSet.has(attrValue)) {
throw new Error('Invalid value: \'' + attrValue + '\' for attr: ' + attr + '. ' + ('Only (' + Array.from(enumSet).map(function (v) {
return '\'' + v + '\'';
}).join(' | ') + ') is valid.'));
}
} else if (attrType === 'boolean') {
attrValue = this.isAttributeEnabled(attr);
} else if (attrType === 'number') {
attrValue = this.getNumberAttribute(attr);
} else if (attrType === 'json') {
attrValue = this.getJSONAttribute(attr);
}
const paramValue = params[paramName]; // set default value if undefined value passed in
this._attrs[attr] = attrValue;
if (paramSchema.default && paramValue === undefined) {
params[paramName] = paramSchema.default;
}
}
/**
* gets the parsed value of an attribute
* @param {string} attr - attribute name
*/
const newParams = Object.freeze(Object.assign({}, params));
}, {
key: 'attr',
value: function attr(_attr) {
if (_attr in this._attrs) {
return this._attrs[_attr];
} else {
throw new TypeError(this + ': attr \'' + _attr + '\' is not defined in attrsSchema');
}
if (this.initialized && shouldComponentUpdate) {
this.runHook(`preUpdate`, updateOptions, null, newParams);
}
/**
* Returns the parsed attrs as a key-value POJO
* @returns {object} parsed attribute values from attrsSchema
*/
this._params = newParams;
}, {
key: 'attrs',
value: function attrs() {
return this._attrs;
if (this.initialized && shouldComponentUpdate) {
this.domPatcher.update(this.state);
this.runHook(`postUpdate`, updateOptions, null, newParams);
}
} // update helpers
// Update a given state store (this.state or this.appState), with option
// to 'cascade' the update across other linked components
// update helpers
// Update a given state store (this.state or this.appState), with option
// to 'cascade' the update across other linked components
_updateStore(stateUpdate) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
const {
cascade,
store
} = options;
}, {
key: '_updateStore',
value: function _updateStore(stateUpdate) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var cascade = options.cascade,
store = options.store;
if (!this.initialized) {
// just update store without patching DOM etc
Object.assign(this[store], stateUpdate);
} else {
// update DOM, router, descendants etc.
const updateHash = `$fragment` in stateUpdate && stateUpdate.$fragment !== this[store].$fragment;
const cascadeFromRoot = cascade && !this.isPanelRoot;
const updateOptions = {
cascade,
store
};
const rootOptions = {
exclude: this,
cascade,
store
};
this.runHook(`preUpdate`, updateOptions, stateUpdate);
if (!this.initialized) {
// just update store without patching DOM etc
Object.assign(this[store], stateUpdate);
} else {
// update DOM, router, descendants etc.
var updateHash = '$fragment' in stateUpdate && stateUpdate.$fragment !== this[store].$fragment;
var cascadeFromRoot = cascade && !this.isPanelRoot;
var updateOptions = { cascade: cascade, store: store };
var rootOptions = { exclude: this, cascade: cascade, store: store };
if (cascadeFromRoot) {
this.$panelRoot.runHook(`preUpdate`, rootOptions, stateUpdate);
}
this.runHook('preUpdate', updateOptions, stateUpdate);
if (cascadeFromRoot) {
this.$panelRoot.runHook('preUpdate', rootOptions, stateUpdate);
}
this.updateSelfAndChildren(stateUpdate, updateOptions);
this.updateSelfAndChildren(stateUpdate, updateOptions);
if (cascadeFromRoot) {
this.$panelRoot.updateSelfAndChildren(stateUpdate, rootOptions);
}
if (updateHash) {
this.router.replaceHash(this[store].$fragment);
}
if (cascadeFromRoot) {
this.$panelRoot.updateSelfAndChildren(stateUpdate, rootOptions);
}
this.runHook('postUpdate', updateOptions, stateUpdate);
if (cascadeFromRoot) {
this.$panelRoot.runHook('postUpdate', rootOptions, stateUpdate);
}
if (updateHash) {
this.router.replaceHash(this[store].$fragment);
}
}
// Apply the given update down the component hierarchy from this node,
// optionally excluding one node's subtree. This is useful for applying
// a full state update to one component while sending only "shared" state
// updates to the app root.
this.runHook(`postUpdate`, updateOptions, stateUpdate);
}, {
key: 'updateSelfAndChildren',
value: function updateSelfAndChildren(stateUpdate) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (!this.initialized) {
return;
if (cascadeFromRoot) {
this.$panelRoot.runHook(`postUpdate`, rootOptions, stateUpdate);
}
}
} // Apply the given update down the component hierarchy from this node,
// optionally excluding one node's subtree. This is useful for applying
// a full state update to one component while sending only "shared" state
// updates to the app root.
var store = options.store,
cascade = options.cascade;
Object.assign(this[store], stateUpdate);
if (store !== 'state' || this.shouldUpdate(this[store])) {
this.domPatcher.update(this.state);
updateSelfAndChildren(stateUpdate) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (cascade) {
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
if (!this.initialized) {
return;
}
try {
for (var _iterator6 = this.$panelChildren[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var child = _step6.value;
const {
store,
cascade
} = options;
Object.assign(this[store], stateUpdate);
if (options.exclude !== child) {
child.updateSelfAndChildren(stateUpdate, options);
}
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
if (store !== `state` || this.shouldComponentUpdate(null, this[store])) {
this.domPatcher.update(this.state);
if (cascade) {
for (const child of this.$panelChildren) {
if (options.exclude !== child) {
child.updateSelfAndChildren(stateUpdate, options);
}

@@ -1102,87 +1063,78 @@ }

}
}, {
key: '_findNearestContextAncestor',
value: function _findNearestContextAncestor() {
if (!this.isConnected) {
throw new Error('Cannot determine context before component is connected to the DOM');
}
}
var node = this.parentNode;
while (node) {
if (node._getAvailableContexts) {
return node;
}
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
// handle shadow-root
node = node.host;
} else {
node = node.parentNode;
}
}
return null;
_findNearestContextAncestor() {
if (!this.isConnected) {
throw new Error(`Cannot determine context before component is connected to the DOM`);
}
}, {
key: '_findAndMergeContextsFromAncestors',
value: function _findAndMergeContextsFromAncestors() {
var contextAncestor = this._findNearestContextAncestor();
var defaultContexts = Object.assign({}, this.getConfig('defaultContexts'));
if (contextAncestor) {
// ancestor contexts must override locally defined defaults
return Object.assign(defaultContexts, contextAncestor._getAvailableContexts());
let node = this.parentNode;
while (node) {
if (node._getAvailableContexts) {
return node;
}
return defaultContexts;
}
}, {
key: '_getAvailableContexts',
value: function _getAvailableContexts() {
if (!this._cachedContexts) {
this._cachedContexts = this._findAndMergeContextsFromAncestors();
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
// handle shadow-root
node = node.host;
} else {
node = node.parentNode;
}
return this._cachedContexts;
}
/**
* Returns the default context of the highest (ie. closest to the document root) ancestor component
* that has configured a default context for the context name. If no ancestor context is found, it will
* return the component's own default context.
*
* @param {string} contextName - name of context
* @returns {object} context object
*/
return null;
}
}, {
key: 'getContext',
value: function getContext(contextName) {
if (!contextName) {
throw new Error('@contextName is null or empty');
}
_findAndMergeContextsFromAncestors() {
const contextAncestor = this._findNearestContextAncestor();
if (!this._contexts.has(contextName)) {
throw new Error('@contextName must be declared in the "contexts" config array');
}
const defaultContexts = Object.assign({}, this.getConfig(`defaultContexts`));
var availableContexts = this._getAvailableContexts();
if (!(contextName in availableContexts)) {
throw new Error('A "' + contextName + '" context is not available. Check that this component or a DOM ancestor has provided this context in its "defaultContexts" Panel config.');
}
if (contextAncestor) {
// ancestor contexts must override locally defined defaults
return Object.assign(defaultContexts, contextAncestor._getAvailableContexts());
}
return availableContexts[contextName];
return defaultContexts;
}
_getAvailableContexts() {
if (!this._cachedContexts) {
this._cachedContexts = this._findAndMergeContextsFromAncestors();
}
}], [{
key: 'attrsSchema',
get: function get() {
return {};
return this._cachedContexts;
}
/**
* Returns the default context of the highest (ie. closest to the document root) ancestor component
* that has configured a default context for the context name. If no ancestor context is found, it will
* return the component's own default context.
*
* @param {string} contextName - name of context
* @returns {object} context object
*/
getContext(contextName) {
if (!contextName) {
throw new Error(`@contextName is null or empty`);
}
}, {
key: 'observedAttributes',
get: function get() {
return ['style-override'].concat(Object.keys(this.attrsSchema));
if (!this._contexts.has(contextName)) {
throw new Error(`@contextName must be declared in the "contexts" config array`);
}
}]);
return Component;
}(_webcomponent2.default);
const availableContexts = this._getAvailableContexts();
exports.default = Component;
if (!(contextName in availableContexts)) {
throw new Error(`A "${contextName}" context is not available. Check that this component or a DOM ancestor has provided this context in its "defaultContexts" Panel config.`);
}
return availableContexts[contextName];
}
}
var _default = Component;
exports.default = _default;

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

'use strict';
"use strict";

@@ -6,53 +6,50 @@ Object.defineProperty(exports, "__esModule", {

});
exports.DOMPatcher = exports.h = exports.EMPTY_DIV = undefined;
exports.EMPTY_DIV = exports.DOMPatcher = void 0;
Object.defineProperty(exports, "h", {
enumerable: true,
get: function () {
return _snabbdom.h;
}
});
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; }; }(); /**
* Manages Virtual DOM -> DOM rendering cycle
* @module dom-patcher
* @private
*/
var _snabbdom = require("snabbdom");
var _snabbdom = require('snabbdom');
var _snabbdomDelayedClass = _interopRequireDefault(require("snabbdom-delayed-class"));
var _snabbdomDelayedClass = require('snabbdom-delayed-class');
var _perf = require("./component-utils/perf");
var _snabbdomDelayedClass2 = _interopRequireDefault(_snabbdomDelayedClass);
var _snabbdomParamsModule = require("./component-utils/snabbdom-params-module");
var _perf = require('./component-utils/perf');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Manages Virtual DOM -> DOM rendering cycle
* @module dom-patcher
* @private
*/
const patch = (0, _snabbdom.init)([_snabbdom.datasetModule, _snabbdom.attributesModule, _snabbdom.classModule, _snabbdom.propsModule, _snabbdom.styleModule, _snabbdom.eventListenersModule, _snabbdomDelayedClass.default, _snabbdomParamsModule.paramsModule]);
const EMPTY_DIV = (0, _snabbdom.h)(`div`);
exports.EMPTY_DIV = EMPTY_DIV;
var patch = (0, _snabbdom.init)([_snabbdom.datasetModule, _snabbdom.attributesModule, _snabbdom.classModule, _snabbdom.propsModule, _snabbdom.styleModule, _snabbdom.eventListenersModule, _snabbdomDelayedClass2.default]);
var EMPTY_DIV = exports.EMPTY_DIV = (0, _snabbdom.h)('div');
exports.h = _snabbdom.h;
var DOMPatcher = exports.DOMPatcher = function () {
function DOMPatcher(initialState, renderFunc) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
_classCallCheck(this, DOMPatcher);
this.updateMode = options.updateMode || 'async';
class DOMPatcher {
constructor(initialState, renderFunc) {
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
this.updateMode = options.updateMode || `async`;
this.state = Object.assign({}, initialState);
this.renderFunc = renderFunc;
this.vnode = this.renderFunc(this.state);
this.postRenderCallback = options.postRenderCallback;
this.postRenderCallback = options.postRenderCallback; // prepare root element
// prepare root element
var tagName = this.vnode.sel.split(/[#.]/)[0];
var classMatches = this.vnode.sel.match(/\.[^.#]+/g);
var idMatch = this.vnode.sel.match(/#[^.#]+/);
const tagName = this.vnode.sel.split(/[#.]/)[0];
const classMatches = this.vnode.sel.match(/\.[^.#]+/g);
const idMatch = this.vnode.sel.match(/#[^.#]+/);
this.el = document.createElement(tagName);
if (classMatches) {
this.el.className = classMatches.map(function (c) {
return c.slice(1);
}).join(' ');
// this attribute setting ensures that svg elements behave as expected and will ensure
this.el.className = classMatches.map(c => c.slice(1)).join(` `); // this attribute setting ensures that svg elements behave as expected and will ensure
// compatibility with different snabbdom versions
this.el.setAttribute('class', this.el.className);
this.el.setAttribute(`class`, this.el.className);
}
if (idMatch) {

@@ -63,4 +60,6 @@ this.el.id = idMatch[0].slice(1);

patch(this.el, this.vnode);
if (this.el === this.vnode.elm) {
var insertHook = this.vnode.data.hook && this.vnode.data.hook.insert;
const insertHook = this.vnode.data.hook && this.vnode.data.hook.insert;
if (insertHook) {

@@ -73,67 +72,67 @@ // since Snabbdom recycled our newly-created root element (this.el) rather

}
this.el = this.vnode.elm;
}
_createClass(DOMPatcher, [{
key: 'update',
value: function update(newState) {
var _this = this;
update(newState) {
if (this.rendering) {
console.error(`Applying new DOM update while render is already in progress!`);
}
if (this.rendering) {
console.error('Applying new DOM update while render is already in progress!');
}
this.pendingState = newState;
this.pendingState = newState;
switch (this.updateMode) {
case 'async':
if (!this.pending) {
this.pending = true;
requestAnimationFrame(function () {
return _this.render();
});
}
break;
case 'sync':
this.render();
break;
}
}
}, {
key: 'render',
value: function render() {
// if disconnected, don't render
if (!this.renderFunc) {
return;
}
switch (this.updateMode) {
case `async`:
if (!this.pending) {
this.pending = true;
requestAnimationFrame(() => this.render());
}
var startedAt = (0, _perf.getNow)();
this.rendering = true;
this.pending = false;
this.state = this.pendingState;
var newVnode = this.renderFunc(this.state);
this.rendering = false;
break;
patch(this.vnode, newVnode);
this.vnode = newVnode;
this.el = this.vnode.elm;
if (this.postRenderCallback) {
this.postRenderCallback((0, _perf.getNow)() - startedAt);
}
case `sync`:
this.render();
break;
}
}, {
key: 'disconnect',
value: function disconnect() {
var vnode = this.vnode;
this.renderFunc = null;
this.state = null;
this.vnode = null;
this.el = null;
this.postRenderCallback = null;
// patch with empty vnode to clear out listeners in tree
// this ensures we don't leave dangling DetachedHTMLElements blocking GC
patch(vnode, { sel: vnode.sel, key: vnode.key });
}
render() {
// if disconnected, don't render
if (!this.renderFunc) {
return;
}
}]);
return DOMPatcher;
}();
const startedAt = _perf.Perf.getNow();
this.rendering = true;
this.pending = false;
this.state = this.pendingState;
const newVnode = this.renderFunc(this.state);
this.rendering = false;
patch(this.vnode, newVnode);
this.vnode = newVnode;
this.el = this.vnode.elm;
if (this.postRenderCallback) {
this.postRenderCallback(_perf.Perf.getNow() - startedAt);
}
}
disconnect() {
const vnode = this.vnode;
this.renderFunc = null;
this.state = null;
this.vnode = null;
this.el = null;
this.postRenderCallback = null; // patch with empty vnode to clear out listeners in tree
// this ensures we don't leave dangling DetachedHTMLElements blocking GC
patch(vnode, {
sel: vnode.sel,
key: vnode.key
});
}
}
exports.DOMPatcher = DOMPatcher;

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

'use strict';
"use strict";

@@ -6,16 +6,44 @@ Object.defineProperty(exports, "__esModule", {

});
exports.jsx = exports.h = exports.StateStore = exports.StateController = exports.ComponentUtils = exports.Component = undefined;
Object.defineProperty(exports, "Component", {
enumerable: true,
get: function () {
return _component.default;
}
});
Object.defineProperty(exports, "ComponentUtils", {
enumerable: true,
get: function () {
return _componentUtils.default;
}
});
exports.StateStore = exports.StateController = exports.ParamComponent = void 0;
Object.defineProperty(exports, "h", {
enumerable: true,
get: function () {
return _domPatcher.h;
}
});
Object.defineProperty(exports, "jsx", {
enumerable: true,
get: function () {
return _snabbdomJsxLite.jsx;
}
});
Object.defineProperty(exports, "shallowEqual", {
enumerable: true,
get: function () {
return _shallowEqual.default;
}
});
var _component = require('./component');
var _component = _interopRequireDefault(require("./component"));
var _component2 = _interopRequireDefault(_component);
var _componentUtils = _interopRequireDefault(require("./component-utils"));
var _componentUtils = require('./component-utils');
var _shallowEqual = _interopRequireDefault(require("./component-utils/shallowEqual"));
var _componentUtils2 = _interopRequireDefault(_componentUtils);
var _domPatcher = require("./dom-patcher");
var _domPatcher = require('./dom-patcher');
var _snabbdomJsxLite = require("snabbdom-jsx-lite");
var _snabbdomJsxLite = require('snabbdom-jsx-lite');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -32,10 +60,9 @@

*/
var StateController = _componentUtils2.default.StateController,
StateStore = _componentUtils2.default.StateStore;
exports.Component = _component2.default;
exports.ComponentUtils = _componentUtils2.default;
const {
StateController,
StateStore
} = _componentUtils.default;
exports.StateStore = StateStore;
exports.StateController = StateController;
exports.StateStore = StateStore;
exports.h = _domPatcher.h;
exports.jsx = _snabbdomJsxLite.jsx;
const ParamComponent = _component.default;
exports.ParamComponent = ParamComponent;

@@ -1,20 +0,11 @@

'use strict';
"use strict";
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; }; }();
require("html-element/global-shim");
var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _raf = _interopRequireDefault(require("raf"));
require('html-element/global-shim');
var _raf = require('raf');
var _raf2 = _interopRequireDefault(_raf);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/* eslint-env node */
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint-env node */
/**

@@ -40,73 +31,33 @@ * Node.js polyfill for rendering Panel components without a browser.

*/
// make raf globally available unless a requestAnimationFrame implementation
// is already there
global.requestAnimationFrame = global.requestAnimationFrame || _raf2.default;
global.requestAnimationFrame = global.requestAnimationFrame || _raf.default; // run a callback for every node of the DOM (sub)tree rooted at the given root node
// run a callback for every node of the DOM (sub)tree rooted at the given root node
function walkDomTree(root, callback) {
// basic breadth-first tree traversal (non-recursive)
var breadthQueue = [root];
const breadthQueue = [root];
while (breadthQueue.length > 0) {
var node = breadthQueue.shift();
const node = breadthQueue.shift();
callback(node);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = Array.from(node.childNodes || [])[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _child = _step.value;
breadthQueue.push(_child);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
for (const child of Array.from(node.childNodes || [])) {
breadthQueue.push(child);
}
if (node.shadowRoot) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = Array.from(node.shadowRoot.childNodes || [])[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var child = _step2.value;
breadthQueue.push(child);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
for (const child of Array.from(node.shadowRoot.childNodes || [])) {
breadthQueue.push(child);
}
}
}
}
} // patch DOM insertion functions to call connectedCallback on Custom Elements
// patch DOM insertion functions to call connectedCallback on Custom Elements
['appendChild', 'insertBefore', 'replaceChild'].forEach(function (funcName) {
var origFunc = Element.prototype[funcName];
[`appendChild`, `insertBefore`, `replaceChild`].forEach(funcName => {
const origFunc = Element.prototype[funcName];
Element.prototype[funcName] = function () {
var child = origFunc.apply(this, arguments);
const child = origFunc.apply(this, arguments);
if (this.isConnected) {

@@ -116,2 +67,3 @@ walkDomTree(child, function (node) {

node.isConnected = true;
if (node.connectedCallback) {

@@ -124,8 +76,9 @@ node.connectedCallback();

};
});
}); // patch removeChild to call disconnectedCallback
// patch removeChild to call disconnectedCallback
var origRemoveChild = Element.prototype.removeChild;
const origRemoveChild = Element.prototype.removeChild;
Element.prototype.removeChild = function (child) {
origRemoveChild.call(this, child);
if (this.isConnected) {

@@ -135,2 +88,3 @@ walkDomTree(child, function (node) {

node.isConnected = false;
if (node.disconnectedCallback) {

@@ -142,80 +96,67 @@ node.disconnectedCallback();

}
return child;
};
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.DOCUMENT_FRAGMENT_NODE = 11; // html-element does not provide hasAttribute
// html-element does not provide hasAttribute
Element.prototype.hasAttribute = function (name) {
return this.getAttribute(name) !== null;
};
// html-element only provides Element (with a lot of the HTMLElement API baked in).
}; // html-element only provides Element (with a lot of the HTMLElement API baked in).
// Use HTMLElement as our Web Components-ready extension.
var HTMLElement = function (_Element) {
_inherits(HTMLElement, _Element);
function HTMLElement() {
_classCallCheck(this, HTMLElement);
class HTMLElement extends Element {
setAttribute(name, value) {
const oldValue = this.getAttribute(name);
super.setAttribute(...arguments);
return _possibleConstructorReturn(this, (HTMLElement.__proto__ || Object.getPrototypeOf(HTMLElement)).apply(this, arguments));
this.__onAttrChanged(name, oldValue, value);
}
_createClass(HTMLElement, [{
key: 'setAttribute',
value: function setAttribute(name, value) {
var oldValue = this.getAttribute(name);
_get(HTMLElement.prototype.__proto__ || Object.getPrototypeOf(HTMLElement.prototype), 'setAttribute', this).apply(this, arguments);
this.__onAttrChanged(name, oldValue, value);
removeAttribute(name) {
const oldValue = this.getAttribute(name);
super.removeAttribute(...arguments);
this.__onAttrChanged(name, oldValue, null);
}
attachShadow() {
this.shadowRoot = document.createElement(`shadow-root`);
this.shadowRoot.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
this.shadowRoot.host = this;
if (this.isConnected) {
this.shadowRoot.isConnected = true;
}
}, {
key: 'removeAttribute',
value: function removeAttribute(name) {
var oldValue = this.getAttribute(name);
_get(HTMLElement.prototype.__proto__ || Object.getPrototypeOf(HTMLElement.prototype), 'removeAttribute', this).apply(this, arguments);
this.__onAttrChanged(name, oldValue, null);
return this.shadowRoot;
}
__attrIsObserved(name) {
if (!this.__observedAttrs) {
this.__observedAttrs = this.constructor.observedAttributes || [];
}
}, {
key: 'attachShadow',
value: function attachShadow() {
this.shadowRoot = document.createElement('shadow-root');
this.shadowRoot.nodeType = Node.DOCUMENT_FRAGMENT_NODE;
this.shadowRoot.host = this;
if (this.isConnected) {
this.shadowRoot.isConnected = true;
}
return this.shadowRoot;
return this.__observedAttrs.includes(name);
}
__onAttrChanged(name, oldValue, newValue) {
if (this.attributeChangedCallback && this.__attrIsObserved(name)) {
this.attributeChangedCallback && this.attributeChangedCallback(name, oldValue, newValue);
}
}, {
key: '__attrIsObserved',
value: function __attrIsObserved(name) {
if (!this.__observedAttrs) {
this.__observedAttrs = this.constructor.observedAttributes || [];
}
return this.__observedAttrs.includes(name);
}
}, {
key: '__onAttrChanged',
value: function __onAttrChanged(name, oldValue, newValue) {
if (this.attributeChangedCallback && this.__attrIsObserved(name)) {
this.attributeChangedCallback && this.attributeChangedCallback(name, oldValue, newValue);
}
}
}]);
}
return HTMLElement;
}(Element);
}
global.HTMLElement = HTMLElement;
global.HTMLElement = HTMLElement; // Document patches for Custom Elements
// Document patches for Custom Elements
const customElementsRegistry = global._customElementsRegistry = global._customElementsRegistry || {};
const originalCreateElement = Document.prototype.createElement;
var customElementsRegistry = global._customElementsRegistry = global._customElementsRegistry || {};
var originalCreateElement = Document.prototype.createElement;
Document.prototype.createElement = function (tagName) {
tagName = tagName.toLowerCase();
var customElClass = customElementsRegistry[tagName];
var el = void 0;
const customElClass = customElementsRegistry[tagName];
let el;
if (customElClass) {

@@ -225,7 +166,9 @@ el = new customElClass();

} else {
el = originalCreateElement.apply(undefined, arguments);
el = originalCreateElement(...arguments);
}
if (tagName === 'body') {
if (tagName === `body`) {
el.isConnected = true;
}
return el;

@@ -235,9 +178,11 @@ };

global.customElements = global.customElements || {
get: function get(tagName) {
get(tagName) {
return customElementsRegistry[tagName];
},
define: function define(tagName, proto) {
define(tagName, proto) {
tagName = tagName.toLowerCase();
if (customElementsRegistry[tagName]) {
throw new Error('Registration failed for type \'' + tagName + '\'. A type with that name is already registered.');
throw new Error(`Registration failed for type '${tagName}'. A type with that name is already registered.`);
} else {

@@ -247,2 +192,3 @@ customElementsRegistry[tagName] = proto;

}
};

@@ -6,13 +6,6 @@ "use strict";

});
exports.default = void 0;
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; };
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; }; }();
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); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function stripHash(fragment) {
return fragment.replace(/^#*/, "");
return fragment.replace(/^#*/, ``);
}

@@ -25,35 +18,28 @@

return decodeURIComponent(currFragment) === decodeURIComponent(newFragment);
}
} // just the necessary bits of Backbone router+history
// just the necessary bits of Backbone router+history
var Router = function () {
function Router(app) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, Router);
class Router {
constructor(app) {
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// allow injecting window dep
this.window = options.window || window;
this.app = app;
var routeDefs = this.app.getConfig("routes");
// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1476-L1479
const routeDefs = this.app.getConfig(`routes`); // https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1476-L1479
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // eslint-disable-line no-useless-escape
this.compiledRoutes = Object.keys(routeDefs).map(function (routeExpr) {
const optionalParam = /\((.*?)\)/g;
const namedParam = /(\(\?)?:\w+/g;
const splatParam = /\*\w+/g;
const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // eslint-disable-line no-useless-escape
this.compiledRoutes = Object.keys(routeDefs).map(routeExpr => {
// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1537-L1547
var expr = routeExpr.replace(escapeRegExp, "\\$&").replace(optionalParam, "(?:$1)?").replace(namedParam, function (match, optional) {
return optional ? match : "([^/?]+)";
}).replace(splatParam, "([^?]*?)");
expr = new RegExp("^" + expr + "(?:\\?([\\s\\S]*))?$");
let expr = routeExpr.replace(escapeRegExp, `\\$&`).replace(optionalParam, `(?:$1)?`).replace(namedParam, (match, optional) => optional ? match : `([^/?]+)`).replace(splatParam, `([^?]*?)`);
expr = new RegExp(`^` + expr + `(?:\\?([\\s\\S]*))?$`); // hook up route handler function
// hook up route handler function
var handler = routeDefs[routeExpr];
if (typeof handler === "string") {
let handler = routeDefs[routeExpr];
if (typeof handler === `string`) {
// reference to another handler rather than its own function

@@ -63,128 +49,99 @@ handler = routeDefs[handler];

return { expr: expr, handler: handler };
return {
expr,
handler
};
});
this.registerListeners(options.historyMethod || "pushState");
this.registerListeners(options.historyMethod || `pushState`);
}
_createClass(Router, [{
key: "registerListeners",
value: function registerListeners(historyMethod) {
var _this = this;
registerListeners(historyMethod) {
var _this = this;
this.navigateToHash = function () {
return _this.navigate(_this.window.location.hash);
};
this.window.addEventListener("popstate", this.navigateToHash);
this.navigateToHash = () => this.navigate(this.window.location.hash);
this.historyMethod = historyMethod;
this.origChangeStateMethod = this.window.history[this.historyMethod];
this.window.addEventListener(`popstate`, this.navigateToHash);
this.historyMethod = historyMethod;
this.origChangeStateMethod = this.window.history[this.historyMethod];
this.window.history[this.historyMethod] = function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
this.window.history[this.historyMethod] = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this.origChangeStateMethod.apply(_this.window.history, args);
_this.navigateToHash();
// fire "pushstate" or "replacestate" event so external action can be taken on url change
// these events are meant to be congruent with native "popstate" event
_this.app.dispatchEvent(new CustomEvent(_this.historyMethod.toLowerCase()));
};
}
}, {
key: "unregisterListeners",
value: function unregisterListeners() {
this.window.removeEventListener("popstate", this.navigateToHash);
this.window.history[this.historyMethod] = this.origChangeStateMethod;
}
}, {
key: "navigate",
value: function navigate(fragment) {
var _this2 = this;
_this.origChangeStateMethod.apply(_this.window.history, args);
var stateUpdate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_this.navigateToHash(); // fire "pushstate" or "replacestate" event so external action can be taken on url change
// these events are meant to be congruent with native "popstate" event
fragment = stripHash(fragment);
if (decodedFragmentsEqual(this.app.state.$fragment, fragment) && !Object.keys(stateUpdate).length) {
return;
}
stateUpdate.$fragment = fragment;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
_this.app.dispatchEvent(new CustomEvent(_this.historyMethod.toLowerCase()));
};
}
try {
for (var _iterator = this.compiledRoutes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var route = _step.value;
unregisterListeners() {
this.window.removeEventListener(`popstate`, this.navigateToHash);
this.window.history[this.historyMethod] = this.origChangeStateMethod;
}
var matches = route.expr.exec(fragment);
if (matches) {
var _ret = function () {
// extract params
// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1553-L1558
var params = matches.slice(1);
params = params.map(function (param, i) {
// Don't decode the search params.
if (i === params.length - 1) {
return param || null;
}
return param ? decodeURIComponent(param) : null;
});
navigate(fragment) {
let stateUpdate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
fragment = stripHash(fragment);
var routeHandler = route.handler;
if (!routeHandler) {
throw "No route handler defined for #" + fragment;
}
var routeStateUpdate = routeHandler.call.apply(routeHandler, [_this2.app, stateUpdate].concat(_toConsumableArray(params)));
if (routeStateUpdate) {
// don't update if route handler returned a falsey result
_this2.app.update(Object.assign({}, stateUpdate, routeStateUpdate));
}
return {
v: void 0
};
}();
if (decodedFragmentsEqual(this.app.state.$fragment, fragment) && !Object.keys(stateUpdate).length) {
return;
}
if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;
stateUpdate.$fragment = fragment;
for (const route of this.compiledRoutes) {
const matches = route.expr.exec(fragment);
if (matches) {
// extract params
// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1553-L1558
let params = matches.slice(1);
params = params.map((param, i) => {
// Don't decode the search params.
if (i === params.length - 1) {
return param || null;
}
return param ? decodeURIComponent(param) : null;
});
const routeHandler = route.handler;
if (!routeHandler) {
throw `No route handler defined for #${fragment}`;
}
// no route matched
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
const routeStateUpdate = routeHandler.call(this.app, stateUpdate, ...params);
if (routeStateUpdate) {
// don't update if route handler returned a falsey result
this.app.update(Object.assign({}, stateUpdate, routeStateUpdate));
}
return;
}
} // no route matched
console.error("No route found matching #" + fragment);
}
}, {
key: "replaceHash",
value: function replaceHash(fragment) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$historyMethod = _ref.historyMethod,
historyMethod = _ref$historyMethod === undefined ? null : _ref$historyMethod;
historyMethod = historyMethod || this.historyMethod;
fragment = stripHash(fragment);
if (!decodedFragmentsEqual(stripHash(this.window.location.hash), fragment)) {
this.window.history[historyMethod](null, null, "#" + fragment);
}
console.error(`No route found matching #${fragment}`);
}
replaceHash(fragment) {
let {
historyMethod = null
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
historyMethod = historyMethod || this.historyMethod;
fragment = stripHash(fragment);
if (!decodedFragmentsEqual(stripHash(this.window.location.hash), fragment)) {
this.window.history[historyMethod](null, null, `#${fragment}`);
}
}]);
}
return Router;
}();
}
exports.default = Router;

@@ -5,3 +5,7 @@ /**

*/
export function getNow() {
export const Perf = {
getNow,
};
function getNow() {
if (typeof performance !== `undefined`) {

@@ -8,0 +12,0 @@ return performance.now();

@@ -7,3 +7,4 @@ import cuid from 'cuid';

import * as hookHelpers from './component-utils/hook-helpers';
import {getNow} from './component-utils/perf';
import {Perf} from './component-utils/perf';
import shallowEqual from './component-utils/shallowEqual';

@@ -17,2 +18,3 @@ const DOCUMENT_FRAGMENT_NODE = 11;

};
const PARAM_TYPES = new Set([Array, String, Boolean, Number, Object, Function, Map, Set]);
const stylesheetCache = new Map(); // key is the component constructor, value is a CSSStyleSheet instance

@@ -227,2 +229,4 @@

* performance when dealing with very many DOM elements.
*
* @deprecated use shouldComponentUpdate instead
* @param {object} state - state object to be used when rendering

@@ -242,2 +246,29 @@ * @returns {boolean} whether or not to render/update this component

/**
*
* Same API as react's `shouldComponentUpdate` usage
* if child component implements this method, parent implmentation wil be discarded
* NOTE: never call `super` in child `shouldComponentUpdate`
*
* there a slight difference with react: `params` or `state` could sometimes be null indicating that
* the update is not related to `params` or `state`
*
* @param {object} params - new params object to be used when rendering
* @param {object} state - state object to be used when rendering
* @return {boolean}
* @example
* shouldComponentUpdate(params, state) {
* if (params.bookmark.id === this.params.bookmark.id) {
* return false;
* }
* return !shallowEqual(params, this.params);
* }
*/
shouldComponentUpdate(params, state) {
if (params) {
return !shallowEqual(params, this.params);
}
return this.shouldUpdate(state);
}
/**
* Applies a state update, triggering a re-render check of the component as

@@ -252,3 +283,3 @@ * well as any other components sharing the same state. This is the primary

update(stateUpdate = {}) {
this.timings.lastUpdateAt = getNow();
this.timings.lastUpdateAt = Perf.getNow();

@@ -279,3 +310,3 @@ const stateUpdateResult = typeof stateUpdate === `function` ? stateUpdate(this.state) : stateUpdate;

this.timings = {
createdAt: getNow(),
createdAt: Perf.getNow(),
};

@@ -295,2 +326,4 @@

css: ``,
params: {},
defaultParams: {},
defaultContexts: {},

@@ -310,2 +343,4 @@ contexts: [],

this._initializeParams();
this._contexts = new Set(this.getConfig(`contexts`));

@@ -336,7 +371,7 @@

this.postRenderCallback = (elapsedMs) => {
this.timings.lastRenderAt = getNow();
this.timings.lastRenderAt = Perf.getNow();
if (elapsedMs > this.getConfig(`slowThreshold`)) {
const shouldBroadcast =
!this.lastSlowRender || // SHOULD because we've never slow rendered
this.lastSlowRender.time - getNow() > 3000 || // SHOULD because last time was more than three seconds ago
this.lastSlowRender.time - Perf.getNow() > 3000 || // SHOULD because last time was more than three seconds ago
elapsedMs > (this.slowestRenderMs || 0); // SHOULD because this time is slower

@@ -356,3 +391,3 @@

this.lastSlowRender = {
time: getNow(),
time: Perf.getNow(),
elapsedMs,

@@ -384,3 +419,3 @@ };

this.initializing = true;
this.timings.initializingStartedAt = getNow();
this.timings.initializingStartedAt = Perf.getNow();

@@ -466,3 +501,3 @@ for (const attrsSchemaKey of Object.keys(this._attrsSchema)) {

this.initializing = false;
this.timings.initializingCompletedAt = getNow();
this.timings.initializingCompletedAt = Perf.getNow();
this.dispatchEvent(

@@ -559,3 +594,3 @@ new CustomEvent(`componentInitialized`, {

attributeChangedCallback(attr, oldVal, newVal) {
this.timings.lastAttributeChangedAt = getNow();
this.timings.lastAttributeChangedAt = Perf.getNow();
this._updateAttr(attr);

@@ -631,3 +666,3 @@

_render(state) {
if (this.shouldUpdate(state)) {
if (this.shouldComponentUpdate(null, state)) {
try {

@@ -822,2 +857,88 @@ this._rendered = this.getConfig(`template`).call(

/**
* parse and validate config.params and create a param schema on the component
*/
_initializeParams() {
// the real value for the params
this._params = {};
// maintain local validated map where all schema keys are defined
this._paramSchemas = {};
const paramSchemas = this.getConfig(`params`);
const defaultParams = this.getConfig(`defaultParams`);
for (let [paramName, paramSchema] of Object.entries(paramSchemas)) {
// convert type shorthand to object
if (!paramSchema.type) {
paramSchema = {type: paramSchema};
}
// Ensure param type is valid
const type = paramSchema.type;
if (!PARAM_TYPES.has(type)) {
const typeString = typeof type === `function` ? type.name : String(type);
throw new Error(
`Invalid type: ${typeString} for param: ${paramName} in paramSchema. ` +
`Only (${Array.from(PARAM_TYPES.keys())
.map((v) => `'${v.name}'`)
.join(` | `)}) is valid.`,
);
}
const paramSchemaObj = {
type,
required: Boolean(paramSchema.required),
default: defaultParams[paramName],
};
// set default value for the params
this._params[paramName] = paramSchemaObj.default;
this._paramSchemas[paramName] = paramSchemaObj;
}
Object.freeze(this._params);
return this._paramSchemas;
}
get params() {
return this._params;
}
setParams(params) {
const shouldComponentUpdate = this.shouldComponentUpdate(params, this.state);
const updateOptions = {
cascade: false,
};
// no extra params allowed if not defined in schema
for (const paramName of Object.keys(params)) {
if (!this._paramSchemas[paramName]) {
throw new Error(`extra param '${paramName}' on ${this.constructor.name} is not defined in schema`);
}
}
for (const [paramName, paramSchema] of Object.entries(this._paramSchemas)) {
// if param defined on schema and marked required, the key must be presented on the params
if (!params.hasOwnProperty(paramName) && paramSchema.required) {
throw new Error(
`param '${paramName}' on ${this.constructor.name} is defined as required param in schema but absent on component definition`,
);
}
const paramValue = params[paramName];
// set default value if undefined value passed in
if (paramSchema.default && paramValue === undefined) {
params[paramName] = paramSchema.default;
}
}
const newParams = Object.freeze(Object.assign({}, params));
if (this.initialized && shouldComponentUpdate) {
this.runHook(`preUpdate`, updateOptions, null, newParams);
}
this._params = newParams;
if (this.initialized && shouldComponentUpdate) {
this.domPatcher.update(this.state);
this.runHook(`postUpdate`, updateOptions, null, newParams);
}
}
// update helpers

@@ -870,3 +991,3 @@

Object.assign(this[store], stateUpdate);
if (store !== `state` || this.shouldUpdate(this[store])) {
if (store !== `state` || this.shouldComponentUpdate(null, this[store])) {
this.domPatcher.update(this.state);

@@ -873,0 +994,0 @@

@@ -19,3 +19,4 @@ /**

import delayedClassModule from 'snabbdom-delayed-class';
import {getNow} from './component-utils/perf';
import {Perf} from './component-utils/perf';
import {paramsModule} from './component-utils/snabbdom-params-module';

@@ -30,2 +31,3 @@ const patch = initSnabbdom([

delayedClassModule,
paramsModule,
]);

@@ -98,3 +100,3 @@

const startedAt = getNow();
const startedAt = Perf.getNow();
this.rendering = true;

@@ -110,3 +112,3 @@ this.pending = false;

if (this.postRenderCallback) {
this.postRenderCallback(getNow() - startedAt);
this.postRenderCallback(Perf.getNow() - startedAt);
}

@@ -113,0 +115,0 @@ }

@@ -10,2 +10,4 @@ // Type definitions for panel

import {JsxVNode, JsxVNodeProps} from 'snabbdom-jsx-lite';
export class StateStore<State> {

@@ -75,9 +77,14 @@ constructor(options: {store?: StateStore<State>});

export interface ConfigOptions<StateT, AppStateT = unknown, ContextRegistryT = unknown> {
export interface ConfigOptions<StateT, AppStateT = unknown, ContextRegistryT = unknown, ParamT = unknown> {
/** Function transforming state object to virtual dom tree */
template(scope?: StateT): VNode;
params?: {[param in keyof ParamT]: InferType<ParamT[param]> | ParamType<ParamT[param]>};
/** Component-specific Shadow DOM stylesheet */
css?: string;
/** object to provide default value for params */
defaultParams?: Partial<ParamT>;
/** An initial default value for the component's state property */

@@ -153,2 +160,23 @@ defaultState?: StateT;

type InferType<T> = T extends string
? StringConstructor
: T extends number
? NumberConstructor
: T extends boolean
? BooleanConstructor
: T extends unknown[]
? ArrayConstructor
: T extends Map<unknown, unknown>
? MapConstructor
: T extends Set<unknown>
? SetConstructor
: T extends (...args: any[]) => any
? FunctionConstructor
: ObjectConstructor;
interface ParamType<T> {
type: InferType<T>;
required?: boolean;
}
export class Component<

@@ -159,3 +187,4 @@ StateT,

AppT = unknown,
ContextRegistryT = unknown
ContextRegistryT = unknown,
ParamT extends Record<string, any> = unknown
> extends WebComponent {

@@ -171,2 +200,5 @@ /** The first Panel Component ancestor in the DOM tree; null if this component is the root */

/** New panel params */
params: Readonly<ParamT>;
/** A reference to the top-level component */

@@ -206,3 +238,3 @@ app: AppT;

/** Defines standard component configuration */
get config(): ConfigOptions<StateT, AppStateT, ContextRegistryT>;
get config(): ConfigOptions<StateT, AppStateT, ContextRegistryT, ParamT>;

@@ -237,6 +269,6 @@ /**

*/
getConfig<K extends keyof ConfigOptions<StateT, AppStateT, ContextRegistryT>>(key: K): this['config'][K];
getConfig<K extends keyof ConfigOptions<StateT, AppStateT, ContextRegistryT, ParamT>>(key: K): this['config'][K];
/** Sets a value in the component's configuration map after element initialization */
setConfig<K extends keyof ConfigOptions<StateT, AppStateT, ContextRegistryT>>(
setConfig<K extends keyof ConfigOptions<StateT, AppStateT, ContextRegistryT, ParamT>>(
key: K,

@@ -247,2 +279,44 @@ val: ConfigOptions<StateT, AppStateT, ContextRegistryT>[K],

/**
* set the params for the this component
* triggers a component update
* if shouldComponentUpdate callback returns true
*/
setParams(params: Partial<ParamT>): void;
/**
* Same API as react's `shouldComponentUpdate` usage
* if child component implements this method, parent implmentation wil be discarded
* only difference is the `params` or `state` could sometimes be null indicating that
* the update is not related to `params` or `state`
*
* To be overridden by subclasses, defining conditional logic for whether
* a component should rerender its template given the state and params to be applied.
* In most cases this method can be left untouched, but can provide improved
* performance when dealing with very many DOM elements.
*
* @example
* shouldComponentUpdate(params, state) {
* // don't need to rerender if result set ID hasn't changed
* if (state && state.largeResultSetID === this._cachedResultID) {
* return false
* }
* if (params && params.bookmark.id === this.params.bookmark.id) {
* return false;
* }
* return !shallowEqual(params, this.params);
* }
*/
shouldComponentUpdate(params: ParamT | null, state: StateT | null): boolean;
/**
* To be overridden by subclasses, defining conditional logic for whether
* a component should rerender its template given the state and params to be applied.
* In most cases this method can be left untouched, but can provide improved
* performance when dealing with very many DOM elements.
*
* @deprecated use shouldComponentUpdate instead
*/
shouldUpdate(state: StateT): boolean;
/**
* Executes the route handler matching the given URL fragment, and updates

@@ -261,10 +335,2 @@ * the URL, as though the user had navigated explicitly to that address.

/**
* To be overridden by subclasses, defining conditional logic for whether
* a component should rerender its template given the state to be applied.
* In most cases this method can be left untouched, but can provide improved
* performance when dealing with very many DOM elements.
*/
shouldUpdate(state: StateT): boolean;
/**
* Applies a state update specifically to app state shared across components.

@@ -307,1 +373,29 @@ * In apps which don't specify `appState` in the root component config, all

}
/**
* Panel component that only accepts 3 generic types
*/
export class ParamComponent<ParamT = unknown, StateT = unknown, ContextRegistryT = unknown> extends Component<
StateT,
unknown,
unknown,
unknown,
ContextRegistryT,
ParamT
> {}
// define jsx IntrinsicElement inside namespace jsx to play well with react
declare global {
/**
* opt-in jsx intrinsic global interfaces
* see: https://www.typescriptlang.org/docs/handbook/jsx.html#type-checking
*/
namespace jsx {
namespace JSX {
type Element = JsxVNode;
interface IntrinsicElements {
[elemName: string]: JsxVNodeProps;
}
}
}
}

@@ -13,2 +13,3 @@ /**

import ComponentUtils from './component-utils';
import shallowEqual from './component-utils/shallowEqual';
import {h} from './dom-patcher';

@@ -19,3 +20,7 @@ import {jsx} from 'snabbdom-jsx-lite';

const ParamComponent = Component;
export {
/** {@link ParamComponent} newer class with modified generic types */
ParamComponent,
/** {@link Component} class, to be subclassed by apps */

@@ -29,2 +34,4 @@ Component,

StateStore,
/** helper function for `shouldComponentUpdate` callback */
shallowEqual,
/**

@@ -31,0 +38,0 @@ * [snabbdom]{@link https://github.com/snabbdom/snabbdom} function to create Hyperscript nodes,

{
"name": "panel",
"version": "5.13.0",
"version": "6.0.0",
"description": "Web Components with Virtual DOM: lightweight composable web apps",

@@ -23,6 +23,5 @@ "main": "build/index.js",

"test": "npm run build-test && npm run test-server && npm run test-browser-local",
"test-browser-local": "wct --plugin local test/browser/index.html",
"test-browser-p": "wct --plugin local --persistent test/browser/index.html",
"test-browser-sauce": "wct --plugin sauce test/browser/index.html",
"test-server": "NODE_ENV=test mocha --require babel-core/register test/server",
"test-browser-local": "SAUCE=0 karma start karma.config.js",
"test-browser-sauce": "SAUCE=1 karma start karma.config.js",
"test-server": "NODE_ENV=test nyc mocha --require @babel/register test/server",
"tslint": "tslint -c tslint.json -t stylish 'lib/index.d.ts'",

@@ -48,5 +47,6 @@ "type-check": "tsc"

"dependencies": {
"cuid": "1.3.8",
"cuid": "2.1.6",
"html-element": "2.3.0",
"loader-utils": "1.1.0",
"lodash-es": "4.17.21",
"lodash.pick": "4.4.0",

@@ -61,14 +61,12 @@ "raf": "3.2.0",

"devDependencies": {
"@babel/cli": "7.17.6",
"@babel/core": "7.17.9",
"@babel/preset-env": "7.16.11",
"@babel/register": "7.17.7",
"@webcomponents/custom-elements": "1.0.6",
"@webcomponents/shadydom": "1.0.8",
"babel-cli": "6.26.0",
"babel-core": "6.26.3",
"babel-loader": "6.2.4",
"babel-plugin-syntax-async-functions": "6.13.0",
"babel-plugin-transform-regenerator": "6.26.0",
"babel-polyfill": "6.26.0",
"babel-preset-es2015": "6.24.1",
"babel-loader": "8.2.5",
"chai": "4.2.0",
"chrome-store-api": "1.0.5",
"domsuite": "0.2.0",
"domsuite": "0.6.0",
"eslint": "6.8.0",

@@ -78,7 +76,17 @@ "eslint-config-mixpanel": "4.0.0",

"jsdoc": "3.6.5",
"karma": "6.4.0",
"karma-chrome-launcher": "3.1.1",
"karma-firefox-launcher": "2.1.2",
"karma-mocha": "2.0.1",
"karma-sauce-launcher": "4.3.6",
"karma-sourcemap-loader": "0.3.8",
"karma-spec-reporter": "0.0.34",
"lint-staged": "10.1.1",
"minami": "1.1.1",
"mocha": "2.5.3",
"mocha": "9.2.2",
"nyc": "15.1.0",
"playwright": "1.24.2",
"prettier": "2.0.2",
"promisify-node": "0.4.0",
"puppeteer": "16.1.0",
"readline-sync": "1.4.7",

@@ -89,5 +97,5 @@ "sinon": "9.2.3",

"typescript": "4.0.2",
"wct-browser-legacy": "1.0.2",
"web-component-tester": "6.9.2",
"webpack": "1.13.0",
"util": "0.12.4",
"webpack": "5.72.0",
"webpack-cli": "4.9.2",
"zip-folder": "1.0.0"

@@ -94,0 +102,0 @@ },

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