зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ead45e9c58
Коммит
0b825184cf
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче