Skip to content

Commit

Permalink
feat(xod-client): make tabline scrollable
Browse files Browse the repository at this point in the history
Closes #414
  • Loading branch information
evgenykochetkov committed Feb 19, 2019
1 parent e6c0dad commit e0a11db
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 36 deletions.
4 changes: 1 addition & 3 deletions packages/xod-client/src/core/styles/components/Sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
}

.Sidebar-title {
display: block;
display: flex;
height: 28px;
margin: 0;
background: $sidebar-color-bg;
Expand All @@ -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;
}
}
Expand Down
17 changes: 16 additions & 1 deletion packages/xod-client/src/core/styles/components/Tabs.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.Tabs {
background: $chrome-outlines;
flex-direction: column;
display: flex;
flex-direction: row;
height: 30px;

.Sidebar-title {
display: flex;
Expand All @@ -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;
}
}
}
27 changes: 25 additions & 2 deletions packages/xod-client/src/core/styles/components/TabsContainer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
70 changes: 67 additions & 3 deletions packages/xod-client/src/editor/components/TabsContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,75 @@
import React from 'react';
import normalizeWheel from 'normalize-wheel';
import PropTypes from 'prop-types';

const TabsContainer = ({ children }) => (
<ul className="TabsContainer">{children}</ul>
);
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 (
<ul
ref={this.setRef}
className="TabsContainer"
onWheel={this.handleScroll}
>
{this.props.children}
<ReactResizeDetector
handleWidth
handleHeight
onResize={this.checkOverflow}
/>
</ul>
);
}
}

TabsContainer.propTypes = {
forwardedRef: PropTypes.func.isRequired,
onOverflowChange: PropTypes.func.isRequired,
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
Expand Down
102 changes: 75 additions & 27 deletions packages/xod-client/src/editor/containers/Tabs.jsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -25,26 +26,31 @@ const SortableItem = sortableElement(({ value }) => (
/>
));

const SortableList = sortableContainer(({ items, onClick, onClose }) => (
<TabsContainer>
{items.map((value, index) => {
const item = R.merge(value, {
onClick,
onClose,
});

return (
<SortableItem
key={`item-${value.id}`}
index={index}
value={item}
onClick={onClick}
onClose={onClose}
/>
);
})}
</TabsContainer>
));
const SortableList = sortableContainer(
({ items, onClick, onClose, forwardedRef, onOverflowChange }) => (
<TabsContainer
forwardedRef={forwardedRef}
onOverflowChange={onOverflowChange}
>
{items.map((value, index) => {
const item = R.merge(value, {
onClick,
onClose,
});

return (
<SortableItem
key={`item-${value.id}`}
index={index}
value={item}
onClick={onClick}
onClose={onClose}
/>
);
})}
</TabsContainer>
)
);

class Tabs extends React.Component {
constructor(props) {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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 (
<div className="Tabs">
Expand All @@ -97,13 +127,14 @@ class Tabs extends React.Component {
onTogglePanel={this.props.actions.togglePanel}
isLoggedIn={this.props.userAuthorised}
/>
<SidebarSwitches
id={SIDEBAR_IDS.RIGHT}
isMinimized
panels={this.props.panels}
onTogglePanel={this.props.actions.togglePanel}
isLoggedIn={this.props.userAuthorised}
/>
{isTabsListOverflown ? (
<Icon
Component="button"
className="ScrollTabs"
name="angle-left"
onClickCapture={this.scrollTabsLeft}
/>
) : null}
<SortableList
items={tabs}
onSortEnd={this.onSortEnd}
Expand All @@ -114,6 +145,23 @@ class Tabs extends React.Component {
helperClass="is-sorting"
onClick={this.onTabClick}
onClose={this.onCloseTab}
forwardedRef={this.setTabsRef}
onOverflowChange={this.onTabsListOverflowChange}
/>
{isTabsListOverflown ? (
<Icon
Component="button"
className="ScrollTabs"
name="angle-right"
onClickCapture={this.scrollTabsRight}
/>
) : null}
<SidebarSwitches
id={SIDEBAR_IDS.RIGHT}
isMinimized
panels={this.props.panels}
onTogglePanel={this.props.actions.togglePanel}
isLoggedIn={this.props.userAuthorised}
/>
</div>
);
Expand Down

0 comments on commit e0a11db

Please sign in to comment.