diff --git a/packages/xod-client/src/core/styles/components/Sidebar.scss b/packages/xod-client/src/core/styles/components/Sidebar.scss index 90e0c8bd3..9ce55ccff 100644 --- a/packages/xod-client/src/core/styles/components/Sidebar.scss +++ b/packages/xod-client/src/core/styles/components/Sidebar.scss @@ -27,7 +27,7 @@ } .Sidebar-title { - display: block; + display: flex; height: 28px; margin: 0; background: $sidebar-color-bg; @@ -43,11 +43,9 @@ height: 28px; &.Sidebar-title--left { - float: left; border-right: 2px solid $chrome-outlines; } &.Sidebar-title--right { - float: right; border-left: 2px solid $chrome-outlines; } } diff --git a/packages/xod-client/src/core/styles/components/Tabs.scss b/packages/xod-client/src/core/styles/components/Tabs.scss index d5f213cc8..44da9d1fa 100644 --- a/packages/xod-client/src/core/styles/components/Tabs.scss +++ b/packages/xod-client/src/core/styles/components/Tabs.scss @@ -1,6 +1,8 @@ .Tabs { background: $chrome-outlines; - flex-direction: column; + display: flex; + flex-direction: row; + height: 30px; .Sidebar-title { display: flex; @@ -11,4 +13,17 @@ display: flex; flex-grow: 1; } + + .ScrollTabs { + padding: 4px 8px 0 8px; + border: none; + background: $chrome-outlines; + color: $color-tabs-unactive-text; + font-weight: bold; + + &:hover { + color: $color-tabs-active-text; + cursor: pointer; + } + } } diff --git a/packages/xod-client/src/core/styles/components/TabsContainer.scss b/packages/xod-client/src/core/styles/components/TabsContainer.scss index 15d3229ba..7823d1d55 100644 --- a/packages/xod-client/src/core/styles/components/TabsContainer.scss +++ b/packages/xod-client/src/core/styles/components/TabsContainer.scss @@ -5,8 +5,31 @@ position: relative; height: 30px; - list-style: none; margin: 0; - padding: 0 4px; + + // bottom padding to push horizontal scrollbar away + padding: 0 4px 30px 4px; + overflow-x: scroll; + overflow-y: hidden; + scroll-behavior: smooth; + // to prevent flashes of scrollbar in firefox when adding tabs + scrollbar-width: none; + + &.isOverflowed { + padding: 0 20px; + + .tabsScrollLeft { + position: absolute; + left: 0; + top: 0; + bottom: 0; + } + .tabsScrollRight { + position: absolute; + right: 0; + top: 0; + bottom: 0; + } + } } diff --git a/packages/xod-client/src/core/styles/components/Workarea.scss b/packages/xod-client/src/core/styles/components/Workarea.scss index 8d442ad5a..aa38ad310 100644 --- a/packages/xod-client/src/core/styles/components/Workarea.scss +++ b/packages/xod-client/src/core/styles/components/Workarea.scss @@ -3,6 +3,7 @@ display: flex; flex-direction: column; flex-grow: 1; + max-width: 100%; } .Workarea-inner { diff --git a/packages/xod-client/src/editor/components/TabsContainer.jsx b/packages/xod-client/src/editor/components/TabsContainer.jsx index 04939b9e8..3231fbc34 100644 --- a/packages/xod-client/src/editor/components/TabsContainer.jsx +++ b/packages/xod-client/src/editor/components/TabsContainer.jsx @@ -1,11 +1,75 @@ import React from 'react'; +import normalizeWheel from 'normalize-wheel'; import PropTypes from 'prop-types'; -const TabsContainer = ({ children }) => ( - -); +import ReactResizeDetector from 'react-resize-detector'; + +class TabsContainer extends React.Component { + constructor(props) { + super(props); + + this.isOverflowed = false; + this.domElement = null; + + this.setRef = this.setRef.bind(this); + this.checkOverflow = this.checkOverflow.bind(this); + this.handleScroll = this.handleScroll.bind(this); + } + + componentDidMount() { + this.checkOverflow(); + } + + componentDidUpdate() { + this.checkOverflow(); + } + + setRef(domElement) { + this.domElement = domElement; + this.props.forwardedRef(domElement); + } + + checkOverflow() { + if (!this.domElement) return; + + const isOverflowed = + this.domElement.scrollWidth > this.domElement.clientWidth; + + if (isOverflowed !== this.isOverflowed) { + this.isOverflowed = isOverflowed; + this.props.onOverflowChange(isOverflowed); + } + } + + handleScroll(event) { + const normalizedWheel = normalizeWheel(event); + + if (normalizedWheel.pixelX === 0) { + this.domElement.scrollLeft += normalizedWheel.pixelY; + } + } + + render() { + return ( + + ); + } +} TabsContainer.propTypes = { + forwardedRef: PropTypes.func.isRequired, + onOverflowChange: PropTypes.func.isRequired, children: PropTypes.oneOfType([ PropTypes.element, PropTypes.arrayOf(PropTypes.element), diff --git a/packages/xod-client/src/editor/containers/Tabs.jsx b/packages/xod-client/src/editor/containers/Tabs.jsx index 83fb5471d..48682a2a5 100644 --- a/packages/xod-client/src/editor/containers/Tabs.jsx +++ b/packages/xod-client/src/editor/containers/Tabs.jsx @@ -1,6 +1,7 @@ import * as R from 'ramda'; import React from 'react'; import PropTypes from 'prop-types'; +import { Icon } from 'react-fa'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { sortableContainer, sortableElement } from 'react-sortable-hoc'; @@ -25,26 +26,31 @@ const SortableItem = sortableElement(({ value }) => ( /> )); -const SortableList = sortableContainer(({ items, onClick, onClose }) => ( - - {items.map((value, index) => { - const item = R.merge(value, { - onClick, - onClose, - }); - - return ( - - ); - })} - -)); +const SortableList = sortableContainer( + ({ items, onClick, onClose, forwardedRef, onOverflowChange }) => ( + + {items.map((value, index) => { + const item = R.merge(value, { + onClick, + onClose, + }); + + return ( + + ); + })} + + ) +); class Tabs extends React.Component { constructor(props) { @@ -54,6 +60,13 @@ class Tabs extends React.Component { this.onCloseTab = this.onCloseTab.bind(this); this.onSortEnd = this.onSortEnd.bind(this); + this.tabsListRef = null; + this.state = { isTabsListOverflown: false }; + this.setTabsRef = this.setTabsRef.bind(this); + this.onTabsListOverflowChange = this.onTabsListOverflowChange.bind(this); + this.scrollTabsLeft = this.scrollTabsLeft.bind(this); + this.scrollTabsRight = this.scrollTabsRight.bind(this); + this.shouldComponentUpdate = deepSCU.bind(this); } @@ -82,11 +95,28 @@ class Tabs extends React.Component { )(tabs); } + onTabsListOverflowChange(isTabsListOverflown) { + this.setState({ isTabsListOverflown }); + } + + setTabsRef(ref) { + this.tabsListRef = ref; + } + getTabs() { return R.sortBy(R.prop('index'))(R.values(this.props.tabs)); } + scrollTabsLeft() { + this.tabsListRef.scrollLeft -= 100; + } + + scrollTabsRight() { + this.tabsListRef.scrollLeft += 100; + } + render() { + const { isTabsListOverflown } = this.state; const tabs = this.getTabs(); return (
@@ -97,13 +127,14 @@ class Tabs extends React.Component { onTogglePanel={this.props.actions.togglePanel} isLoggedIn={this.props.userAuthorised} /> - + {isTabsListOverflown ? ( + + ) : null} + {isTabsListOverflown ? ( + + ) : null} +
);