Bug 1568404 - Drag/drop to reorder file tabs in editor panel r=jlast

Differential Revision: https://phabricator.services.mozilla.com/D50755

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Bryan Kok 2020-02-10 20:54:30 +00:00
Родитель ead45e9c58
Коммит 0b825184cf
5 изменённых файлов: 131 добавлений и 3 удалений

Просмотреть файл

@ -54,6 +54,14 @@ export function moveTab(url: string, tabIndex: number): Action {
};
}
export function moveTabBySourceId(sourceId: string, tabIndex: number): Action {
return {
type: "MOVE_TAB_BY_SOURCE_ID",
sourceId,
tabIndex,
};
}
/**
* @memberof actions/tabs
* @static

Просмотреть файл

@ -61,6 +61,11 @@ export type SourceAction =
+url: string,
+tabIndex: number,
|}
| {|
+type: "MOVE_TAB_BY_SOURCE_ID",
+sourceId: string,
+tabIndex: number,
|}
| {|
+type: "CLOSE_TAB",
+source: Source,

Просмотреть файл

@ -42,12 +42,18 @@ import classnames from "classnames";
type OwnProps = {|
source: Source,
onDragOver: Function,
onDragStart: Function,
onDragEnd: Function,
|};
type Props = {
cx: Context,
tabSources: TabsSources,
selectedSource: ?Source,
source: Source,
onDragOver: Function,
onDragStart: Function,
onDragEnd: Function,
activeSearch: ?ActiveSearchType,
hasSiblingOfSameName: boolean,
selectSource: typeof actions.selectSource,
@ -186,6 +192,9 @@ class Tab extends PureComponent<Props> {
source,
tabSources,
hasSiblingOfSameName,
onDragOver,
onDragStart,
onDragEnd,
} = this.props;
const sourceId = source.id;
const active =
@ -215,6 +224,10 @@ class Tab extends PureComponent<Props> {
return (
<div
draggable
onDragOver={onDragOver}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
className={className}
key={sourceId}
onClick={handleTabClick}
@ -262,5 +275,9 @@ export default connect<Props, OwnProps, _, _, _, _>(
togglePrettyPrint: actions.togglePrettyPrint,
showSource: actions.showSource,
toggleBlackBox: actions.toggleBlackBox,
},
null,
{
withRef: true,
}
)(Tab);

Просмотреть файл

@ -5,6 +5,7 @@
// @flow
import React, { PureComponent } from "react";
import ReactDOM from "react-dom";
import { connect } from "../../utils/connect";
import {
@ -45,6 +46,7 @@ type Props = {
startPanelCollapsed: boolean,
endPanelCollapsed: boolean,
moveTab: typeof actions.moveTab,
moveTabBySourceId: typeof actions.moveTabBySourceId,
closeTab: typeof actions.closeTab,
togglePaneCollapse: typeof actions.togglePaneCollapse,
showSource: typeof actions.showSource,
@ -85,6 +87,8 @@ class Tabs extends PureComponent<Props, State> {
renderStartPanelToggleButton: Function;
renderEndPanelToggleButton: Function;
onResize: Function;
_draggedSource: ?Source;
_draggedSourceIndex: ?number;
constructor(props: Props) {
super(props);
@ -98,6 +102,24 @@ class Tabs extends PureComponent<Props, State> {
});
}
get draggedSource() {
return this._draggedSource == null
? { url: null, id: null }
: this._draggedSource;
}
set draggedSource(source: ?Source) {
this._draggedSource = source;
}
get draggedSourceIndex() {
return this._draggedSourceIndex == null ? -1 : this._draggedSourceIndex;
}
set draggedSourceIndex(index: ?number) {
this._draggedSourceIndex = index;
}
componentDidUpdate(prevProps: Props) {
if (
this.props.selectedSource !== prevProps.selectedSource ||
@ -176,6 +198,57 @@ class Tabs extends PureComponent<Props, State> {
);
};
onTabDragStart = (source: Source, index: number) => {
this.draggedSource = source;
this.draggedSourceIndex = index;
};
onTabDragEnd = () => {
this.draggedSource = null;
this.draggedSourceIndex = null;
};
onTabDragOver = (e: any, source: Source, hoveredTabIndex: number) => {
const { moveTabBySourceId } = this.props;
if (hoveredTabIndex === this.draggedSourceIndex) {
return;
}
const tabDOM = ReactDOM.findDOMNode(
this.refs[`tab_${source.id}`].getWrappedInstance()
);
/* $FlowIgnore: tabDOM.nodeType will always be of Node.ELEMENT_NODE since it comes from a ref;
however; the return type of findDOMNode is null | Element | Text */
const tabDOMRect = tabDOM.getBoundingClientRect();
const { pageX: mouseCursorX } = e;
if (
/* Case: the mouse cursor moves into the left half of any target tab */
mouseCursorX - tabDOMRect.left <
tabDOMRect.width / 2
) {
// The current tab goes to the left of the target tab
const targetTab =
hoveredTabIndex > this.draggedSourceIndex
? hoveredTabIndex - 1
: hoveredTabIndex;
moveTabBySourceId(this.draggedSource.id, targetTab);
this.draggedSourceIndex = targetTab;
} else if (
/* Case: the mouse cursor moves into the right half of any target tab */
mouseCursorX - tabDOMRect.left >=
tabDOMRect.width / 2
) {
// The current tab goes to the right of the target tab
const targetTab =
hoveredTabIndex < this.draggedSourceIndex
? hoveredTabIndex + 1
: hoveredTabIndex;
moveTabBySourceId(this.draggedSource.id, targetTab);
this.draggedSourceIndex = targetTab;
}
};
renderTabs() {
const { tabSources } = this.props;
if (!tabSources) {
@ -184,9 +257,21 @@ class Tabs extends PureComponent<Props, State> {
return (
<div className="source-tabs" ref="sourceTabs">
{tabSources.map((source, index) => (
<Tab key={index} source={source} />
))}
{tabSources.map((source, index) => {
return (
<Tab
onDragStart={_ => this.onTabDragStart(source, index)}
onDragOver={e => {
this.onTabDragOver(e, source, index);
e.preventDefault();
}}
onDragEnd={this.onTabDragEnd}
key={index}
source={source}
ref={`tab_${source.id}`}
/>
);
})}
</div>
);
}
@ -263,6 +348,7 @@ export default connect<Props, OwnProps, _, _, _, _>(
{
selectSource: actions.selectSource,
moveTab: actions.moveTab,
moveTabBySourceId: actions.moveTabBySourceId,
closeTab: actions.closeTab,
togglePaneCollapse: actions.togglePaneCollapse,
showSource: actions.showSource,

Просмотреть файл

@ -66,6 +66,8 @@ function update(
case "MOVE_TAB":
return moveTabInList(state, action);
case "MOVE_TAB_BY_SOURCE_ID":
return moveTabInListBySourceId(state, action);
case "CLOSE_TAB":
return removeSourceFromTabList(state, action);
@ -258,6 +260,16 @@ function moveTabInList(state: TabsState, { url, tabIndex: newIndex }) {
return { tabs };
}
function moveTabInListBySourceId(
state: TabsState,
{ sourceId, tabIndex: newIndex }
) {
let { tabs } = state;
const currentIndex = tabs.findIndex(tab => tab.sourceId == sourceId);
tabs = move(tabs, currentIndex, newIndex);
return { tabs };
}
// Selectors
export const getTabs = (state: State): TabList => state.tabs.tabs;