dash-core-components
Version:
Core component suite for Dash
295 lines (292 loc) • 16.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _ramda = require("ramda");
var _style = _interopRequireDefault(require("styled-jsx/style"));
var _LoadingElement = _interopRequireDefault(require("../utils/LoadingElement"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable react/prop-types */ // some weird interaction btwn styled-jsx 3.4 and babel
// see https://github.com/vercel/styled-jsx/pull/716
// eslint-disable-line no-unused-vars
// EnhancedTab is defined here instead of in Tab.react.js because if exported there,
// it will mess up the Python imports and metadata.json
var EnhancedTab = _ref => {
var id = _ref.id,
label = _ref.label,
selected = _ref.selected,
className = _ref.className,
style = _ref.style,
selectedClassName = _ref.selectedClassName,
selected_style = _ref.selected_style,
selectHandler = _ref.selectHandler,
value = _ref.value,
_ref$disabled = _ref.disabled,
disabled = _ref$disabled === void 0 ? false : _ref$disabled,
_ref$disabled_style = _ref.disabled_style,
disabled_style = _ref$disabled_style === void 0 ? {
color: '#d6d6d6'
} : _ref$disabled_style,
disabled_className = _ref.disabled_className,
mobile_breakpoint = _ref.mobile_breakpoint,
amountOfTabs = _ref.amountOfTabs,
colors = _ref.colors,
vertical = _ref.vertical,
componentPath = _ref.componentPath;
var ctx = window.dash_component_api.useDashContext();
// We use the raw path here since it's up one level from
// the tabs child.
var isLoading = ctx.useLoading({
rawPath: componentPath
});
var tabStyle = style;
if (disabled) {
tabStyle = _objectSpread({
tabStyle
}, disabled_style);
}
if (selected) {
tabStyle = _objectSpread({
tabStyle
}, selected_style);
}
var tabClassName = "tab ".concat(className || '');
if (disabled) {
tabClassName += " tab--disabled ".concat(disabled_className || '');
}
if (selected) {
tabClassName += " tab--selected ".concat(selectedClassName || '');
}
var labelDisplay;
if ((0, _ramda.is)(Array, label)) {
// label is an array, so it has children that we want to render
labelDisplay = label[0].props.children;
} else {
// else it is a string, so we just want to render that
labelDisplay = label;
}
return /*#__PURE__*/_react.default.createElement("div", {
"data-dash-is-loading": isLoading,
className: tabClassName,
id: id,
style: tabStyle,
onClick: () => {
if (!disabled) {
selectHandler(value);
}
}
}, /*#__PURE__*/_react.default.createElement("span", null, labelDisplay), /*#__PURE__*/_react.default.createElement("style", {
jsx: true
}, "\n .tab {\n display: inline-block;\n background-color: ".concat(colors.background, ";\n border: 1px solid ").concat(colors.border, ";\n border-bottom: none;\n padding: 20px 25px;\n transition: background-color, color 200ms;\n width: 100%;\n text-align: center;\n box-sizing: border-box;\n }\n .tab:last-of-type {\n border-right: 1px solid ").concat(colors.border, ";\n border-bottom: 1px solid ").concat(colors.border, ";\n }\n .tab:hover {\n cursor: pointer;\n }\n .tab--selected {\n border-top: 2px solid ").concat(colors.primary, ";\n color: black;\n background-color: white;\n }\n .tab--selected:hover {\n background-color: white;\n }\n .tab--disabled {\n color: #d6d6d6;\n }\n\n @media screen and (min-width: ").concat(mobile_breakpoint, "px) {\n .tab {\n border: 1px solid ").concat(colors.border, ";\n border-right: none;\n ").concat(vertical ? '' : "width: calc(100% / ".concat(amountOfTabs, ");"), ";\n }\n .tab--selected,\n .tab:last-of-type.tab--selected {\n border-bottom: none;\n ").concat(vertical ? "border-left: 2px solid ".concat(colors.primary, ";") : "border-top: 2px solid ".concat(colors.primary, ";"), ";\n }\n }\n ")));
};
/**
* A Dash component that lets you render pages with tabs - the Tabs component's children
* can be dcc.Tab components, which can hold a label that will be displayed as a tab, and can in turn hold
* children components that will be that tab's content.
*/
class Tabs extends _react.Component {
constructor(props) {
super(props);
this.selectHandler = this.selectHandler.bind(this);
if (!(0, _ramda.has)('value', this.props)) {
this.props.setProps({
value: this.valueOrDefault()
});
}
}
valueOrDefault() {
if ((0, _ramda.has)('value', this.props)) {
return this.props.value;
}
var children = this.parseChildrenToArray();
if (children && children.length) {
var firstChildren = window.dash_component_api.getLayout([...children[0].props.componentPath, 'props', 'value']);
return firstChildren || 'tab-1';
}
return 'tab-1';
}
parseChildrenToArray() {
if (this.props.children && !(0, _ramda.is)(Array, this.props.children)) {
// if dcc.Tabs.children contains just one single element, it gets passed as an object
// instead of an array - so we put it in an array ourselves!
return [this.props.children];
}
return this.props.children;
}
selectHandler(value) {
this.props.setProps({
value: value
});
}
render() {
var EnhancedTabs;
var selectedTab;
var value = this.valueOrDefault();
if (this.props.children) {
var children = this.parseChildrenToArray();
var amountOfTabs = children.length;
EnhancedTabs = children.map((child, index) => {
// TODO: handle components that are not dcc.Tab components (throw error)
// enhance Tab components coming from Dash (as dcc.Tab) with methods needed for handling logic
var childProps;
if (/*#__PURE__*/_react.default.isValidElement(child)) {
childProps = window.dash_component_api.getLayout([...child.props.componentPath, 'props']);
} else {
// In case the selected tab is a string.
childProps = {};
}
if (!childProps.value) {
childProps = _objectSpread(_objectSpread({}, childProps), {}, {
value: "tab-".concat(index + 1)
});
}
// check if this child/Tab is currently selected
if (childProps.value === value) {
selectedTab = child;
}
return /*#__PURE__*/_react.default.createElement(EnhancedTab, {
key: index,
id: childProps.id,
label: childProps.label,
selected: value === childProps.value,
selectHandler: this.selectHandler,
className: childProps.className,
style: childProps.style,
selectedClassName: childProps.selected_className,
selected_style: childProps.selected_style,
value: childProps.value,
disabled: childProps.disabled,
disabled_style: childProps.disabled_style,
disabled_className: childProps.disabled_className,
mobile_breakpoint: this.props.mobile_breakpoint,
vertical: this.props.vertical,
amountOfTabs: amountOfTabs,
colors: this.props.colors,
componentPath: child.componentPath
});
});
}
var selectedTabContent = !(0, _ramda.isNil)(selectedTab) ? selectedTab : '';
var tabContainerClass = this.props.vertical ? 'tab-container tab-container--vert' : 'tab-container';
var tabContentClass = this.props.vertical ? 'tab-content tab-content--vert' : 'tab-content';
var tabParentClass = this.props.vertical ? 'tab-parent tab-parent--vert' : 'tab-parent';
return /*#__PURE__*/_react.default.createElement(_LoadingElement.default, {
className: "".concat(tabParentClass, " ").concat(this.props.parent_className || ''),
style: this.props.parent_style,
id: "".concat(this.props.id, "-parent")
}, /*#__PURE__*/_react.default.createElement("div", {
className: "".concat(tabContainerClass, " ").concat(this.props.className || ''),
style: this.props.style,
id: this.props.id
}, EnhancedTabs), /*#__PURE__*/_react.default.createElement("div", {
className: "".concat(tabContentClass, " ").concat(this.props.content_className || ''),
style: this.props.content_style
}, selectedTabContent || ''), /*#__PURE__*/_react.default.createElement("style", {
jsx: true
}, "\n .tab-parent {\n display: flex;\n flex-direction: column;\n }\n .tab-container {\n display: flex;\n flex-direction: column;\n }\n .tab-container--vert {\n display: inline-flex;\n }\n .tab-content--vert {\n display: inline-flex;\n flex-direction: column;\n }\n @media screen and (min-width: ".concat(this.props.mobile_breakpoint, "px) {\n :global(.tab-container--vert .tab) {\n width: auto;\n border-right: none !important;\n border-bottom: none !important;\n }\n :global(.tab-container--vert .tab:last-of-type) {\n border-bottom: 1px solid ").concat(this.props.colors.border, " !important;\n }\n :global(.tab-container--vert .tab--selected) {\n border-top: 1px solid ").concat(this.props.colors.border, ";\n border-left: 2px solid ").concat(this.props.colors.primary, ";\n border-right: none;\n }\n .tab-container {\n flex-direction: row;\n }\n .tab-container--vert {\n flex-direction: column;\n }\n .tab-parent--vert {\n display: inline-flex;\n flex-direction: row;\n }\n }\n ")));
}
}
exports.default = Tabs;
Tabs.defaultProps = {
mobile_breakpoint: 800,
colors: {
border: '#d6d6d6',
primary: '#1975FA',
background: '#f9f9f9'
},
vertical: false,
persisted_props: ['value'],
persistence_type: 'local'
};
Tabs.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,
/**
* The value of the currently selected Tab
*/
value: _propTypes.default.string,
/**
* Appends a class to the Tabs container holding the individual Tab components.
*/
className: _propTypes.default.string,
/**
* Appends a class to the Tab content container holding the children of the Tab that is selected.
*/
content_className: _propTypes.default.string,
/**
* Appends a class to the top-level parent container holding both the Tabs container and the content container.
*/
parent_className: _propTypes.default.string,
/**
* Appends (inline) styles to the Tabs container holding the individual Tab components.
*/
style: _propTypes.default.object,
/**
* Appends (inline) styles to the top-level parent container holding both the Tabs container and the content container.
*/
parent_style: _propTypes.default.object,
/**
* Appends (inline) styles to the tab content container holding the children of the Tab that is selected.
*/
content_style: _propTypes.default.object,
/**
* Renders the tabs vertically (on the side)
*/
vertical: _propTypes.default.bool,
/**
* Breakpoint at which tabs are rendered full width (can be 0 if you don't want full width tabs on mobile)
*/
mobile_breakpoint: _propTypes.default.number,
/**
* Array that holds Tab components
*/
children: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.node), _propTypes.default.node]),
/**
* Holds the colors used by the Tabs and Tab components. If you set these, you should specify colors for all properties, so:
* colors: {
* border: '#d6d6d6',
* primary: '#1975FA',
* background: '#f9f9f9'
* }
*/
colors: _propTypes.default.exact({
border: _propTypes.default.string,
primary: _propTypes.default.string,
background: _propTypes.default.string
}),
/**
* Used to allow user interactions in this component to be persisted when
* the component - or the page - is refreshed. If `persisted` is truthy and
* hasn't changed from its previous value, a `value` that the user has
* changed while using the app will keep that change, as long as
* the new `value` also matches what was given originally.
* Used in conjunction with `persistence_type`.
*/
persistence: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.string, _propTypes.default.number]),
/**
* Properties whose user interactions will persist after refreshing the
* component or the page. Since only `value` is allowed this prop can
* normally be ignored.
*/
persisted_props: _propTypes.default.arrayOf(_propTypes.default.oneOf(['value'])),
/**
* Where persisted user changes will be stored:
* memory: only kept in memory, reset on page refresh.
* local: window.localStorage, data is kept after the browser quit.
* session: window.sessionStorage, data is cleared once the browser quit.
*/
persistence_type: _propTypes.default.oneOf(['local', 'session', 'memory'])
};
Tabs.dashChildrenUpdate = true;