dash-core-components
Version:
Core component suite for Dash
159 lines (153 loc) • 6.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _propTypes = _interopRequireDefault(require("prop-types"));
var _ramda = require("ramda");
var _dashComponentPlugins = require("@plotly/dash-component-plugins");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Update and track the current window.location object through the window.history state.
* Use in conjunction with the `dash_core_components.Link` component to make apps with multiple pages.
*/
class Location extends _react.Component {
constructor(props) {
super(props);
this.updateLocation = this.updateLocation.bind(this);
this.onLocationChange = this.onLocationChange.bind(this);
}
updateLocation(props) {
var hash = props.hash,
href = props.href,
pathname = props.pathname,
refresh = props.refresh,
search = props.search,
setProps = props.setProps;
// Keep track of props relating to window.location that may need to be updated via setProps
var propsToSet = {};
/**
* Check if the field exists in props. If the prop with "fieldName" is not defined,
* then it was not set by the user and needs to be equal to the value in window.location.
* This only happens on page load (since props will no longer be undefined after componentDidMount).
*
* @param {string} fieldName
* The name of the prop in window.location and in the component's prop
*
* @returns {boolean}
* Returns true if the prop with fieldName is different and the window state needs to be updated
*/
var checkExistsUpdateWindowLocation = fieldName => {
var propVal = props[fieldName];
if (((0, _ramda.type)(propVal) === 'Undefined' || propVal === null) && (0, _ramda.type)(window.location[fieldName]) !== 'Undefined') {
// propVal is undefined or null, but window.location has this fieldName defined
propsToSet[fieldName] = window.location[fieldName];
} else if (propVal !== window.location[fieldName]) {
// Prop has changed?
if (refresh === true) {
// Refresh the page?
window.location[fieldName] = propVal;
} else if (this.props[fieldName] !== propVal) {
// If this prop has changed, need to setProps
propsToSet[fieldName] = propVal;
// This (`${fieldName}`: propVal) needs to be pushed in the window.history
return true;
}
}
// This (`${fieldName}`: propVal) DOES NOT need to be pushed in the window.history
return false;
};
// Check if the prop value needs to be updated (note that this mutates propsToSet)
var pathnameUpdated = checkExistsUpdateWindowLocation('pathname');
var hrefUpdated = checkExistsUpdateWindowLocation('href');
var hashUpdated = checkExistsUpdateWindowLocation('hash');
var searchUpdated = checkExistsUpdateWindowLocation('search');
// propsToSet has been updated -- batch update to Dash
if (Object.keys(propsToSet).length > 0) {
setProps(propsToSet);
}
// Special case -- overrides everything!
if (hrefUpdated) {
window.history.pushState({}, '', href);
if (refresh === 'callback-nav') {
window.dispatchEvent(new CustomEvent('_dashprivate_pushstate'));
}
} else if (pathnameUpdated || hashUpdated || searchUpdated) {
// Otherwise, we can mash everything together
var searchVal = (0, _ramda.type)(search) !== 'Undefined' ? search : '';
var hashVal = (0, _ramda.type)(hash) !== 'Undefined' ? hash : '';
window.history.pushState({}, '', "".concat(pathname).concat(searchVal).concat(hashVal));
if (refresh === 'callback-nav') {
window.dispatchEvent(new CustomEvent('_dashprivate_pushstate'));
}
}
}
onLocationChange() {
var setProps = this.props.setProps;
var propsToChange = {};
if (this.props.pathname !== window.location.pathname) {
propsToChange.pathname = window.location.pathname;
}
if (this.props.href !== window.location.href) {
propsToChange.href = window.location.href;
}
if (this.props.hash !== window.location.hash) {
propsToChange.hash = window.location.hash;
}
if (this.props.search !== window.location.search) {
propsToChange.search = window.location.search;
}
setProps(propsToChange);
_dashComponentPlugins.History.dispatchChangeEvent();
}
componentDidMount() {
window.addEventListener('popstate', this.onLocationChange);
window.addEventListener('_dashprivate_pushstate', this.onLocationChange);
this.updateLocation(this.props);
}
componentWillUnmount() {
window.removeEventListener('popstate', this.onLocationChange);
window.removeEventListener('_dashprivate_pushstate', this.onLocationChange);
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.updateLocation(nextProps);
}
render() {
return null;
}
}
exports.default = Location;
Location.propTypes = {
/**
* The ID of this component, used to identify dash components
* in callbacks. The ID needs to be unique across all of the
* components in an app.
*/
id: _propTypes.default.string.isRequired,
/** pathname in window.location - e.g., "/my/full/pathname" */
pathname: _propTypes.default.string,
/** search in window.location - e.g., "?myargument=1" */
search: _propTypes.default.string,
/** hash in window.location - e.g., "#myhash" */
hash: _propTypes.default.string,
/** href in window.location - e.g., "/my/full/pathname?myargument=1#myhash" */
href: _propTypes.default.string,
/**
* Use `True` to navigate outside the Dash app or to manually refresh a page.
* Use `False` if the same callback that updates the Location component is also
* updating the page content - typically used in multi-page apps that do not use Pages.
* Use 'callback-nav' if you are updating the URL in a callback, or a different
* callback will respond to the new Location with updated content. This is
* typical with multi-page apps that use Pages. This will allow for
* navigating to a new page without refreshing the page.
*/
refresh: _propTypes.default.oneOfType([_propTypes.default.oneOf(['callback-nav']), _propTypes.default.bool]),
/**
* Dash-assigned callback that gets fired when the value changes.
*/
setProps: _propTypes.default.func
};
Location.defaultProps = {
refresh: true
};