Skip to content

Commit

Permalink
Merge pull request #1694 from xodio/fix-tabs-overflow-issues
Browse files Browse the repository at this point in the history
Make tabline scrollable
  • Loading branch information
evgenykochetkov authored Feb 26, 2019
2 parents c85fb72 + 0097631 commit 623435a
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 61 deletions.
1 change: 1 addition & 0 deletions packages/xod-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"reselect": "^2.5.4",
"sanctuary-def": "^0.14.0",
"shortid": "^2.2.8",
"smooth-scroll-into-view-if-needed": "^1.1.23",
"throttle-debounce": "^1.0.1",
"url-parse": "^1.1.9",
"url-search-params-polyfill": "^2.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ $color-tabs-hover-text: $white;
$color-tabs-debugger-background: #3d4931;
$color-tabs-debugger-text: #82b948;

$sidebar-width: 200px;
$sidebar-width: 202px;

$sidebar-color-bg: $chrome-bg;
$sidebar-color-bg-even: $chrome-bg;
Expand Down
12 changes: 12 additions & 0 deletions packages/xod-client/src/core/styles/components/Editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,16 @@
display: flex;
flex-direction: row;
align-items: stretch;

&.leftSidebarMaximized, &.rightSidebarMaximized {
.Workarea {
max-width: calc(100% - #{$sidebar-width});
}
}

&.leftSidebarMaximized.rightSidebarMaximized {
.Workarea {
max-width: calc(100% - #{$sidebar-width * 2});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
.ProjectBrowser-container {
display: flex;
flex-grow: 1;

width: $sidebar-width;
}
.ProjectBrowser {
flex-grow: 1;
Expand Down
7 changes: 3 additions & 4 deletions packages/xod-client/src/core/styles/components/Sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
flex-direction: column;
flex-wrap: nowrap;
flex-shrink: 0;
box-sizing: content-box;
box-sizing: border-box;

background: $sidebar-color-bg;

Expand All @@ -27,7 +27,8 @@
}

.Sidebar-title {
display: block;
display: flex;
flex-shrink: 0;
height: 28px;
margin: 0;
background: $sidebar-color-bg;
Expand All @@ -43,11 +44,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
18 changes: 17 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,9 @@
.Tabs {
background: $chrome-outlines;
flex-direction: column;
display: flex;
flex-direction: row;
height: 30px;
overflow: hidden;

.Sidebar-title {
display: flex;
Expand All @@ -11,4 +14,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;
}
}
}
29 changes: 27 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,33 @@
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 when adding tabs
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
&.isOverflowed {
padding: 0 20px;

.tabsScrollLeft {
position: absolute;
left: 0;
top: 0;
bottom: 0;
}
.tabsScrollRight {
position: absolute;
right: 0;
top: 0;
bottom: 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
display: flex;
flex-direction: column;
flex-grow: 1;
max-width: 100%;
}

.Workarea-inner {
Expand Down
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
91 changes: 71 additions & 20 deletions packages/xod-client/src/editor/components/TabsItem.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,79 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import cn from 'classnames';
import scrollIntoView from 'smooth-scroll-into-view-if-needed';

const TabsItem = ({ data, onClick, onClose }) => {
const classes = classNames('TabsItem', {
[`TabsItem--${data.type}`]: true,
'is-active': data.isActive,
});
class TabsItem extends React.Component {
constructor(props) {
super(props);

const handleClick = event => onClick(data.id, event);
const handleClose = event => {
this.ref = null;
this.setRef = this.setRef.bind(this);

this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleClose = this.handleClose.bind(this);
this.scrollIntoView = this.scrollIntoView.bind(this);
}

componentDidMount() {
if (this.props.data.isActive) {
setTimeout(this.scrollIntoView, 10);
}
}

componentDidUpdate(prevProps) {
if (this.props.data.isActive && !prevProps.data.isActive) {
setTimeout(this.scrollIntoView, 10);
}
}

setRef(ref) {
this.ref = ref;
}

scrollIntoView() {
if (!this.ref) return;

scrollIntoView(this.ref, {
scrollMode: 'if-needed',
behavior: 'smooth',
duration: 100,
block: 'nearest',
inline: 'nearest',
// don't scroll the whole window
boundary: this.ref.parentElement,
});
}

handleMouseDown(event) {
this.scrollIntoView();
this.props.onClick(this.props.data.id, event);
}

handleClose(event) {
event.stopPropagation();
onClose(data.id);
};

return (
<li className={classes} onMouseDown={handleClick}>
<span className="tab-name">{data.label}</span>
<span className="tab-close" onMouseDown={handleClose}>
&times;
</span>
</li>
);
};
this.props.onClose(this.props.data.id);
}

render() {
const { type, label, isActive } = this.props.data;

return (
<li
ref={this.setRef}
className={cn('TabsItem', `TabsItem--${type}`, {
'is-active': isActive,
})}
onMouseDown={this.handleMouseDown}
>
<span className="tab-name">{label}</span>
<span className="tab-close" onMouseDown={this.handleClose}>
&times;
</span>
</li>
);
}
}

const TabsDataPropType = PropTypes.shape({
id: PropTypes.string,
Expand Down
13 changes: 12 additions & 1 deletion packages/xod-client/src/editor/containers/Editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,19 @@ class Editor extends React.Component {
/>
) : null;

const areSidebarsMaximized = R.compose(
R.map(R.pipe(R.filter(R.prop('maximized')), R.isEmpty, R.not)),
R.groupBy(R.prop('sidebar')),
R.values
)(this.props.panelsSettings);

return (
<HotKeys
handlers={this.getHotkeyHandlers()}
className="Editor"
className={cn('Editor', {
leftSidebarMaximized: areSidebarsMaximized[SIDEBAR_IDS.LEFT],
rightSidebarMaximized: areSidebarsMaximized[SIDEBAR_IDS.RIGHT],
})}
id="Editor"
>
<Sidebar id={SIDEBAR_IDS.LEFT} windowSize={this.props.size} />
Expand Down Expand Up @@ -303,6 +312,7 @@ Editor.propTypes = {
project: PropTypes.object,
currentTab: sanctuaryPropType($Maybe($.Object)),
attachmentEditorTabs: PropTypes.array,
panelsSettings: PropTypes.object.isRequired,
searchPatches: PropTypes.func.isRequired,
isHelpboxVisible: PropTypes.bool,
isDebugSessionRunning: PropTypes.bool,
Expand Down Expand Up @@ -354,6 +364,7 @@ const mapStateToProps = R.applySpec({
isDebugSessionOutdated: DebuggerSelectors.isDebugSessionOutdated,
defaultNodePosition: EditorSelectors.getDefaultNodePlacePosition,
isTabtestRunning: EditorSelectors.isTabtestRunning,
panelsSettings: EditorSelectors.getAllPanelsSettings,
});

const mapDispatchToProps = dispatch => ({
Expand Down
Loading

0 comments on commit 623435a

Please sign in to comment.