UNPKG

dash-core-components

Version:

Core component suite for Dash

295 lines (292 loc) 16.7 kB
"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;