Skip to content

Commit

Permalink
[wip] Tab sorting with drag and drop moves tabs with animation and cr…
Browse files Browse the repository at this point in the history
…eates space

Addresses brave#6242
  • Loading branch information
petemill committed Oct 5, 2017
1 parent e357db4 commit d745e5b
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 71 deletions.
107 changes: 65 additions & 42 deletions app/renderer/components/tabs/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ class Tab extends React.Component {

get isDragging () {
const sourceDragData = dnd.getInterBraveDragData()
return sourceDragData &&
return sourceDragData && this.props.dragData &&
sourceDragData.get('tabId') === this.props.tabId &&
sourceDragData.get('draggingOverWindowId') === getCurrentWindowId()
this.props.dragData.get('windowId') === getCurrentWindowId()
}

get isDraggingOverSelf () {
Expand Down Expand Up @@ -128,7 +128,7 @@ class Tab extends React.Component {
// this is added back to original size when onDrag event is happening
this.tabSentinel.style.width = 0

dnd.onDragStart(dragTypes.TAB, this.frame, e)
dnd.onDragStart(dragTypes.TAB, this.frame.set('displayIndex', this.props.displayIndex), e)
// cancel tab preview while dragging. see #10103
windowActions.setTabHoverState(this.props.frameKey, false, false)
}
Expand All @@ -146,7 +146,7 @@ class Tab extends React.Component {
}

onDragOver (e) {
dnd.onDragOver(dragTypes.TAB, this.tabNode.getBoundingClientRect(), this.props.tabId, this.draggingOverData, e)
dnd.onDragOver(dragTypes.TAB, this.tabNode.getBoundingClientRect(), this.props.tabId, this.draggingOverData, e, this.props.displayIndex)
}

onMouseLeave (e) {
Expand Down Expand Up @@ -266,13 +266,26 @@ class Tab extends React.Component {
props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, frameKey)
props.tabWidth = currentWindow.getIn(['ui', 'tabs', 'fixTabWidth'])
props.themeColor = tabUIState.getThemeColor(currentWindow, frameKey)
props.displayIndex = ownProps.displayIndex
props.title = frame.get('title')
props.partOfFullPageSet = partOfFullPageSet
props.showAudioTopBorder = audioState.showAudioTopBorder(currentWindow, frameKey, isPinned)
props.centralizeTabIcons = tabUIState.centralizeTabIcons(currentWindow, frameKey, isPinned)

// used in other functions
props.dragData = state.getIn(['dragData', 'type']) === dragTypes.TAB && state.get('dragData')
// select drag specifics
props.insertingPrevious = props.insertingNext = false
if (props.dragData) {
console.log('evaluated drag data', props.displayIndex)
const elIdx = props.displayIndex
const srcIdx = props.dragData.getIn(['data', 'displayIndex'])
const curIdx = props.dragData.getIn(['dragOverData', 'draggingOverIndex'])
const windowId = props.dragData.getIn(['dragOverData', 'draggingOverWindowId'])
const draggingToThisWindow = windowId === getCurrentWindowId()
props.insertingPrevious = (draggingToThisWindow && elIdx < srcIdx && elIdx >= curIdx)
props.insertingNext = (draggingToThisWindow && elIdx > srcIdx && elIdx <= curIdx)
}
props.tabId = tabId
props.previewMode = currentWindow.getIn(['ui', 'tabs', 'previewMode'])

Expand All @@ -291,31 +304,44 @@ class Tab extends React.Component {
}
}
})

return <div
data-tab-area
ref={(node) => { this.tabNode = node }}
data-index={this.props.displayIndex}
className={cx({
tabArea: true,
draggingOverLeft: this.isDraggingOverLeft && !this.isDraggingOverSelf,
draggingOverRight: this.isDraggingOverRight && !this.isDraggingOverSelf,
isDragging: this.isDragging,
tabDragArea: true,
isPinned: this.props.isPinnedTab,
partOfFullPageSet: this.props.partOfFullPageSet || !!this.props.tabWidth
})}
style={this.props.tabWidth ? { flex: `0 0 ${this.props.tabWidth}px` } : {}}
onMouseMove={this.onMouseMove}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onDragOver={this.onDragOver}
data-test-id='tab-area'
data-frame-key={this.props.frameKey}>
{
this.props.isActive && this.props.notificationBarActive
? <NotificationBarCaret />
data-frame-key={this.props.frameKey}
>
<div
className={cx({
tabArea: true,
insertingPrevious: this.props.insertingPrevious,
insertingNext: this.props.insertingNext,
isDragging: this.isDragging,
isPinned: this.props.isPinnedTab
})}
onMouseMove={this.onMouseMove}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onDragStart={this.onDragStart}
onDragEnd={this.onDragEnd}
onDrag={this.onDrag}
>
{
this.props.isActive && this.props.notificationBarActive
? <NotificationBarCaret />
: null
}
<div
data-tab
ref={(node) => { this.tabNode = node }}
className={css(
<div
data-tab
className={css(
styles.tab,
// Windows specific style
isWindows && styles.tab_forWindows,
Expand All @@ -328,35 +354,32 @@ class Tab extends React.Component {
this.props.isActive && this.props.isPrivateTab && styles.tab_active_private,
this.props.centralizeTabIcons && styles.tab__content_centered
)}
data-test-id='tab'
data-test-active-tab={this.props.isActive}
data-test-pinned-tab={this.props.isPinnedTab}
data-test-private-tab={this.props.isPrivateTab}
data-frame-key={this.props.frameKey}
draggable
title={this.props.title}
onDrag={this.onDrag}
onDragStart={this.onDragStart}
onDragEnd={this.onDragEnd}
onDragOver={this.onDragOver}
onClick={this.onClickTab}
onContextMenu={contextMenus.onTabContextMenu.bind(this, this.frame)}
data-test-id='tab'
data-test-active-tab={this.props.isActive}
data-test-pinned-tab={this.props.isPinnedTab}
data-test-private-tab={this.props.isPrivateTab}
data-frame-key={this.props.frameKey}
draggable
title={this.props.title}
onClick={this.onClickTab}
onContextMenu={contextMenus.onTabContextMenu.bind(this, this.frame)}
>
<div
ref={(node) => { this.tabSentinel = node }}
className={css(styles.tab__sentinel)}
<div
ref={(node) => { this.tabSentinel = node }}
className={css(styles.tab__sentinel)}
/>
<div className={css(
<div className={css(
styles.tab__identity,
this.props.centralizeTabIcons && styles.tab__content_centered
)}>
<Favicon tabId={this.props.tabId} />
<AudioTabIcon tabId={this.props.tabId} />
<TabTitle tabId={this.props.tabId} />
<Favicon tabId={this.props.tabId} />
<AudioTabIcon tabId={this.props.tabId} />
<TabTitle tabId={this.props.tabId} />
</div>
<PrivateIcon tabId={this.props.tabId} />
<NewSessionIcon tabId={this.props.tabId} />
<CloseTabIcon tabId={this.props.tabId} fixTabWidth={this.fixTabWidth} />
</div>
<PrivateIcon tabId={this.props.tabId} />
<NewSessionIcon tabId={this.props.tabId} />
<CloseTabIcon tabId={this.props.tabId} fixTabWidth={this.fixTabWidth} />
</div>
</div>
}
Expand Down
20 changes: 13 additions & 7 deletions app/renderer/components/tabs/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const React = require('react')
const ReactDOM = require('react-dom')
const Immutable = require('immutable')

// Components
Expand Down Expand Up @@ -82,12 +81,18 @@ class Tabs extends React.Component {
// This must be executed async because the state change that this causes
// will cause the onDragEnd to never run
setTimeout(() => {
// collect data
const key = sourceDragData.get('key')
let droppedOnTab = dnd.closestFromXOffset(this.tabRefs.filter((node) => node && node.props.frameKey !== key), clientX).selectedRef
if (droppedOnTab) {
const isLeftSide = dnd.isLeftSide(ReactDOM.findDOMNode(droppedOnTab), clientX)

windowActions.moveTab(key, droppedOnTab.props.frameKey, isLeftSide)
const srcTabIdx = sourceDragData.get('displayIndex')
const droppedOnTab = dnd.closestFromXOffset(this.tabRefs.filter((node) => node && node.props.frameKey !== key), clientX).selectedRef
const destTabKey = droppedOnTab.props.frameKey
const destTabIdx = droppedOnTab.props.displayIndex
// validate
if (Number.isInteger(srcTabIdx) && Number.isInteger(destTabIdx) && Number.isInteger(destTabKey)) {
// calculate if move to left or right
const prepend = (srcTabIdx > destTabIdx)
// perform move
windowActions.moveTab(key, destTabKey, prepend)
if (sourceDragData.get('pinnedLocation')) {
appActions.tabPinned(sourceDragData.get('tabId'), false)
}
Expand Down Expand Up @@ -177,9 +182,10 @@ class Tabs extends React.Component {
}
{
this.props.currentTabs
.map((frameKey) =>
.map((frameKey, index) =>
<Tab
key={'tab-' + frameKey}
displayIndex={index}
ref={(node) => this.tabRefs.push(node)}
frameKey={frameKey}
partOfFullPageSet={this.props.partOfFullPageSet} />
Expand Down
12 changes: 6 additions & 6 deletions js/dnd.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module.exports.onDragEnd = () => {
}, 100)
}

module.exports.onDragOver = (dragType, sourceBoundingRect, draggingOverKey, draggingOverDetail, e) => {
module.exports.onDragOver = (dragType, sourceBoundingRect, draggingOverKey, draggingOverDetail, e, draggingOverIndex = null) => {
if (module.exports.getInterBraveDragType() !== dragType) {
return
}
Expand All @@ -63,6 +63,7 @@ module.exports.onDragOver = (dragType, sourceBoundingRect, draggingOverKey, drag
draggingOverType: dragType,
draggingOverLeftHalf: false,
draggingOverRightHalf: false,
draggingOverIndex,
draggingOverWindowId: getCurrentWindowId()
})
return
Expand All @@ -71,24 +72,23 @@ module.exports.onDragOver = (dragType, sourceBoundingRect, draggingOverKey, drag
if (!sourceBoundingRect) {
return
}

if (e.clientX > sourceBoundingRect.left && e.clientX < sourceBoundingRect.left + (sourceBoundingRect.width / 5) &&
(!draggingOverDetail || !draggingOverDetail.get('draggingOverLeftHalf'))) {
if (e.clientX > sourceBoundingRect.left && e.clientX < sourceBoundingRect.left + (sourceBoundingRect.width / 5)) {
appActions.draggedOver({
draggingOverKey,
draggingOverType: dragType,
draggingOverLeftHalf: true,
draggingOverRightHalf: false,
draggingOverIndex,
draggingOverWindowId: getCurrentWindowId()
})
windowActions.setContextMenuDetail()
} else if (e.clientX < sourceBoundingRect.right && e.clientX >= sourceBoundingRect.left + (sourceBoundingRect.width / 5) &&
(!draggingOverDetail || !draggingOverDetail.get('draggingOverRightHalf'))) {
} else if (e.clientX < sourceBoundingRect.right && e.clientX >= sourceBoundingRect.left + (sourceBoundingRect.width / 5)) {
appActions.draggedOver({
draggingOverKey,
draggingOverType: dragType,
draggingOverLeftHalf: false,
draggingOverRightHalf: true,
draggingOverIndex,
draggingOverWindowId: getCurrentWindowId()
})
windowActions.setContextMenuDetail()
Expand Down
39 changes: 23 additions & 16 deletions less/tabs.less
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,10 @@
}
}

.tabArea {
box-sizing: border-box;
display: inline-block;
.tabDragArea {
overflow: visible;
position: relative;
vertical-align: top;
overflow: hidden;

height: -webkit-fill-available;
// There's a special case that tabs should span the full width
// if there are a full set of them.
Expand All @@ -68,32 +66,41 @@

&:not(.isPinned) {
flex: 1;
&:first-child {
//padding-left: 6px;
}
}

}

.tabArea {
box-sizing: border-box;
width: 100%;
display: inline-block;
position: absolute;
top: 0;right: 0;
left: 0; bottom: 0;
vertical-align: top;
overflow: hidden;
transition: transform .1s;

&.isPinned {
.tabIcon {
margin: 0;
padding: 0 4px;
}
}

&.draggingOverLeft {
&:not(.isDragging) {
padding-left: @dragSpacing;
}
&.insertingPrevious {
transform: translateX(100%);
pointer-events: none;
}

&.draggingOverRight {
&:not(.isDragging) {
padding-right: @dragSpacing;
}
&.insertingNext {
transform: translateX(-100%);
pointer-events: none;
}

&.isDragging {
opacity: 0.2;
visibility: hidden;
}

hr.dragIndicator {
Expand Down

0 comments on commit d745e5b

Please sign in to comment.