From 156b144d91d551793bcef4a9bebcae1661b55224 Mon Sep 17 00:00:00 2001 From: Erik Sombroek Date: Fri, 13 Oct 2023 09:12:30 +0200 Subject: [PATCH 1/5] Allow dropping Tabs on TabBar --- es/DockLayout.js | 14 +++++++++ es/DockPanel.d.ts | 2 ++ es/DockPanel.js | 69 ++++++++++++++++++++++++++++++++++++++++++- es/DockTabBar.d.ts | 2 ++ es/DockTabBar.js | 4 +-- es/DockTabs.d.ts | 2 ++ es/DockTabs.js | 4 +-- lib/DockLayout.js | 14 +++++++++ lib/DockPanel.d.ts | 2 ++ lib/DockPanel.js | 69 ++++++++++++++++++++++++++++++++++++++++++- lib/DockTabBar.d.ts | 2 ++ lib/DockTabBar.js | 4 +-- lib/DockTabs.d.ts | 2 ++ lib/DockTabs.js | 4 +-- src/DockLayout.tsx | 18 ++++++++++++ src/DockPanel.tsx | 72 +++++++++++++++++++++++++++++++++++++++++++-- src/DockTabBar.tsx | 6 +++- src/DockTabs.tsx | 6 ++-- 18 files changed, 281 insertions(+), 15 deletions(-) diff --git a/es/DockLayout.js b/es/DockLayout.js index d6f11ed4..4a99d319 100644 --- a/es/DockLayout.js +++ b/es/DockLayout.js @@ -341,6 +341,20 @@ export class DockLayout extends DockPortalManager { if (element.classList.contains('dock-box')) { ratio = 0.3; } + if (direction === 'after-tab') { + const navWidth = 30; + const extraContentWidth = 30; + // search parents until we find .dock-nav + let parent = element.parentElement; + while (parent && !parent.classList.contains('dock-nav')) { + parent = parent.parentElement; + } + if (parent) { + const parentRect = parent.getBoundingClientRect(); + // make sure left is lower then parent right + left = Math.min(left, parentRect.right - navWidth - extraContentWidth - width); + } + } switch (direction) { case 'float': { let x = (event.clientX - layoutRect.left) * scaleX; diff --git a/es/DockPanel.d.ts b/es/DockPanel.d.ts index bfa9153f..411b02d1 100644 --- a/es/DockPanel.d.ts +++ b/es/DockPanel.d.ts @@ -40,6 +40,8 @@ export declare class DockPanel extends React.PureComponent { onPanelCornerDragEnd: (e: DragState) => void; onFloatPointerDown: () => void; onPanelClicked: (e: React.MouseEvent) => void; + onPanelDragOver: (e: DragState) => void; + onPanelDrop: (e: DragState) => void; render(): React.ReactNode; _unmounted: boolean; componentWillUnmount(): void; diff --git a/es/DockPanel.js b/es/DockPanel.js index c1843c90..e378d74e 100644 --- a/es/DockPanel.js +++ b/es/DockPanel.js @@ -7,6 +7,7 @@ import { DockDropLayer } from "./DockDropLayer"; import { getFloatPanelSize, nextZIndex } from "./Algorithm"; import { DockDropEdge } from "./DockDropEdge"; import { groupClassNames } from "./Utils"; +import * as DragManager from "./dragdrop/DragManager"; import classNames from "classnames"; export class DockPanel extends React.PureComponent { constructor() { @@ -204,6 +205,72 @@ export class DockPanel extends React.PureComponent { this._ref.querySelector('.dock-bar').focus(); } }; + this.onPanelDragOver = (e) => { + var _a, _b; + let dockId = this.context.getDockId(); + let tab = DragManager.DragState.getData('tab', dockId); + let panel = DragManager.DragState.getData('panel', dockId); + let group; + if (tab) { + panel = tab.parent; + group = tab.group; + } + else { + // drag whole panel + if (!panel) { + return; + } + if (panel === null || panel === void 0 ? void 0 : panel.panelLock) { + e.reject(); + return; + } + group = panel.group; + } + const tabGroup = this.context.getGroup(group); + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + const direction = 'after-tab'; + const dockTabElements = this._ref.querySelectorAll('.dock-tab'); + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + const dockTabLastRect = dockTabLastElement.querySelector('.dock-tab-hit-area'); + if (e.clientX - this._ref.offsetLeft < 30) { + // do not allow drop on the left side of the tab + } + else if (group !== lastTab.group) { + e.reject(); + } + else if ((tabGroup === null || tabGroup === void 0 ? void 0 : tabGroup.floatable) === 'singleTab' && ((_b = (_a = lastTab.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.mode) === 'float') { + e.reject(); + } + else if (tab && tab !== lastTab) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } + else if (panel && panel !== lastTab.parent) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } + }; + this.onPanelDrop = (e) => { + let dockId = this.context.getDockId(); + let panel; + let tab = DragManager.DragState.getData('tab', dockId); + if (tab) { + panel = tab.parent; + } + else { + panel = DragManager.DragState.getData('panel', dockId); + } + const direction = 'after-tab'; + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + if (tab && tab !== lastTab) { + this.context.dockMove(tab, lastTab, direction); + } + else if (panel && panel !== lastTab.parent) { + this.context.dockMove(panel, lastTab, direction); + } + }; this._unmounted = false; } static set droppingPanel(panel) { @@ -290,7 +357,7 @@ export class DockPanel extends React.PureComponent { } } return (React.createElement(DragDropDiv, { getRef: this.getRef, className: cls, style: style, "data-dockid": id, onDragOverT: isFloat ? null : this.onDragOver, onClick: this.onPanelClicked }, - React.createElement(DockTabs, { panelData: panelData, onPanelDragStart: onPanelHeaderDragStart, onPanelDragMove: this.onPanelHeaderDragMove, onPanelDragEnd: this.onPanelHeaderDragEnd }), + React.createElement(DockTabs, { panelData: panelData, onPanelDragStart: onPanelHeaderDragStart, onPanelDragMove: this.onPanelHeaderDragMove, onPanelDragEnd: this.onPanelHeaderDragEnd, onPanelDragOver: this.onPanelDragOver, onPanelDrop: this.onPanelDrop }), isFloat ? [ React.createElement(DragDropDiv, { key: "drag-size-t", className: "dock-panel-drag-size dock-panel-drag-size-t", onDragStartT: this.onPanelCornerDragT, onDragMoveT: this.onPanelCornerDragMove, onDragEndT: this.onPanelCornerDragEnd }), diff --git a/es/DockTabBar.d.ts b/es/DockTabBar.d.ts index 01ff9fff..4a3678b9 100644 --- a/es/DockTabBar.d.ts +++ b/es/DockTabBar.d.ts @@ -6,6 +6,8 @@ interface DockTabBarProps extends TabNavListProps { onDragStart?: DragManager.DragHandler; onDragMove?: DragManager.DragHandler; onDragEnd?: DragManager.DragHandler; + onDragOver?: DragManager.DragHandler; + onDrop?: DragManager.DropHandler; TabNavList: React.ComponentType; } export declare function DockTabBar(props: DockTabBarProps): JSX.Element; diff --git a/es/DockTabBar.js b/es/DockTabBar.js index 6e504693..f4c5b92b 100644 --- a/es/DockTabBar.js +++ b/es/DockTabBar.js @@ -40,7 +40,7 @@ function checkLocalTabMove(key, tabbar) { return false; } export function DockTabBar(props) { - const { onDragStart, onDragMove, onDragEnd, TabNavList, isMaximized } = props, restProps = __rest(props, ["onDragStart", "onDragMove", "onDragEnd", "TabNavList", "isMaximized"]); + const { onDragStart, onDragMove, onDragEnd, onDragOver, onDrop, TabNavList, isMaximized } = props, restProps = __rest(props, ["onDragStart", "onDragMove", "onDragEnd", "onDragOver", "onDrop", "TabNavList", "isMaximized"]); const layout = React.useContext(DockContextType); const ref = React.useRef(); const getRef = (div) => { @@ -55,6 +55,6 @@ export function DockTabBar(props) { e.preventDefault(); } }; - return (React.createElement(DragDropDiv, { onDragStartT: onDragStart, onDragMoveT: onDragMove, onDragEndT: onDragEnd, role: "tablist", className: "dock-bar", onKeyDown: onKeyDown, getRef: getRef, tabIndex: -1 }, + return (React.createElement(DragDropDiv, { onDragStartT: onDragStart, onDragMoveT: onDragMove, onDragEndT: onDragEnd, onDragOverT: onDragOver, onDropT: onDrop, role: "tablist", className: "dock-bar", onKeyDown: onKeyDown, getRef: getRef, tabIndex: -1 }, React.createElement(TabNavList, Object.assign({}, restProps)))); } diff --git a/es/DockTabs.d.ts b/es/DockTabs.d.ts index f485b7e9..f6c6e1a0 100644 --- a/es/DockTabs.d.ts +++ b/es/DockTabs.d.ts @@ -25,6 +25,8 @@ interface Props { onPanelDragStart: DragManager.DragHandler; onPanelDragMove: DragManager.DragHandler; onPanelDragEnd: DragManager.DragHandler; + onPanelDragOver: DragManager.DragHandler; + onPanelDrop: DragManager.DragHandler; } export declare class DockTabs extends React.PureComponent { static contextType: React.Context; diff --git a/es/DockTabs.js b/es/DockTabs.js index e67422ba..c5800730 100644 --- a/es/DockTabs.js +++ b/es/DockTabs.js @@ -172,7 +172,7 @@ export class DockTabs extends React.PureComponent { this.context.dockMove(panelData, null, 'new-window'); }; this.renderTabBar = (props, TabNavList) => { - let { panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd } = this.props; + let { panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd, onPanelDragOver, onPanelDrop } = this.props; let { group: groupName, panelLock } = panelData; let group = this.context.getGroup(groupName); let { panelExtra } = group; @@ -197,7 +197,7 @@ export class DockTabs extends React.PureComponent { panelExtraContent = this.addNewWindowMenu(panelExtraContent, !maximizable); } } - return (React.createElement(DockTabBar, Object.assign({ onDragStart: onPanelDragStart, onDragMove: onPanelDragMove, onDragEnd: onPanelDragEnd, TabNavList: TabNavList, isMaximized: panelData.parent.mode === 'maximize' }, props, { extra: panelExtraContent }))); + return (React.createElement(DockTabBar, Object.assign({ onDragStart: onPanelDragStart, onDragMove: onPanelDragMove, onDragEnd: onPanelDragEnd, onDragOver: onPanelDragOver, onDrop: onPanelDrop, TabNavList: TabNavList, isMaximized: panelData.parent.mode === 'maximize' }, props, { extra: panelExtraContent }))); }; this.onTabChange = (activeId) => { this.props.panelData.activeId = activeId; diff --git a/lib/DockLayout.js b/lib/DockLayout.js index f1108c24..a088ae59 100644 --- a/lib/DockLayout.js +++ b/lib/DockLayout.js @@ -366,6 +366,20 @@ class DockLayout extends DockPortalManager { if (element.classList.contains('dock-box')) { ratio = 0.3; } + if (direction === 'after-tab') { + const navWidth = 30; + const extraContentWidth = 30; + // search parents until we find .dock-nav + let parent = element.parentElement; + while (parent && !parent.classList.contains('dock-nav')) { + parent = parent.parentElement; + } + if (parent) { + const parentRect = parent.getBoundingClientRect(); + // make sure left is lower then parent right + left = Math.min(left, parentRect.right - navWidth - extraContentWidth - width); + } + } switch (direction) { case 'float': { let x = (event.clientX - layoutRect.left) * scaleX; diff --git a/lib/DockPanel.d.ts b/lib/DockPanel.d.ts index bfa9153f..411b02d1 100644 --- a/lib/DockPanel.d.ts +++ b/lib/DockPanel.d.ts @@ -40,6 +40,8 @@ export declare class DockPanel extends React.PureComponent { onPanelCornerDragEnd: (e: DragState) => void; onFloatPointerDown: () => void; onPanelClicked: (e: React.MouseEvent) => void; + onPanelDragOver: (e: DragState) => void; + onPanelDrop: (e: DragState) => void; render(): React.ReactNode; _unmounted: boolean; componentWillUnmount(): void; diff --git a/lib/DockPanel.js b/lib/DockPanel.js index 4f254957..e6bda36c 100644 --- a/lib/DockPanel.js +++ b/lib/DockPanel.js @@ -32,6 +32,7 @@ const DockDropLayer_1 = require("./DockDropLayer"); const Algorithm_1 = require("./Algorithm"); const DockDropEdge_1 = require("./DockDropEdge"); const Utils_1 = require("./Utils"); +const DragManager = __importStar(require("./dragdrop/DragManager")); const classnames_1 = __importDefault(require("classnames")); class DockPanel extends React.PureComponent { constructor() { @@ -229,6 +230,72 @@ class DockPanel extends React.PureComponent { this._ref.querySelector('.dock-bar').focus(); } }; + this.onPanelDragOver = (e) => { + var _a, _b; + let dockId = this.context.getDockId(); + let tab = DragManager.DragState.getData('tab', dockId); + let panel = DragManager.DragState.getData('panel', dockId); + let group; + if (tab) { + panel = tab.parent; + group = tab.group; + } + else { + // drag whole panel + if (!panel) { + return; + } + if (panel === null || panel === void 0 ? void 0 : panel.panelLock) { + e.reject(); + return; + } + group = panel.group; + } + const tabGroup = this.context.getGroup(group); + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + const direction = 'after-tab'; + const dockTabElements = this._ref.querySelectorAll('.dock-tab'); + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + const dockTabLastRect = dockTabLastElement.querySelector('.dock-tab-hit-area'); + if (e.clientX - this._ref.offsetLeft < 30) { + // do not allow drop on the left side of the tab + } + else if (group !== lastTab.group) { + e.reject(); + } + else if ((tabGroup === null || tabGroup === void 0 ? void 0 : tabGroup.floatable) === 'singleTab' && ((_b = (_a = lastTab.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.mode) === 'float') { + e.reject(); + } + else if (tab && tab !== lastTab) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } + else if (panel && panel !== lastTab.parent) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } + }; + this.onPanelDrop = (e) => { + let dockId = this.context.getDockId(); + let panel; + let tab = DragManager.DragState.getData('tab', dockId); + if (tab) { + panel = tab.parent; + } + else { + panel = DragManager.DragState.getData('panel', dockId); + } + const direction = 'after-tab'; + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + if (tab && tab !== lastTab) { + this.context.dockMove(tab, lastTab, direction); + } + else if (panel && panel !== lastTab.parent) { + this.context.dockMove(panel, lastTab, direction); + } + }; this._unmounted = false; } static set droppingPanel(panel) { @@ -315,7 +382,7 @@ class DockPanel extends React.PureComponent { } } return (React.createElement(DragDropDiv_1.DragDropDiv, { getRef: this.getRef, className: cls, style: style, "data-dockid": id, onDragOverT: isFloat ? null : this.onDragOver, onClick: this.onPanelClicked }, - React.createElement(DockTabs_1.DockTabs, { panelData: panelData, onPanelDragStart: onPanelHeaderDragStart, onPanelDragMove: this.onPanelHeaderDragMove, onPanelDragEnd: this.onPanelHeaderDragEnd }), + React.createElement(DockTabs_1.DockTabs, { panelData: panelData, onPanelDragStart: onPanelHeaderDragStart, onPanelDragMove: this.onPanelHeaderDragMove, onPanelDragEnd: this.onPanelHeaderDragEnd, onPanelDragOver: this.onPanelDragOver, onPanelDrop: this.onPanelDrop }), isFloat ? [ React.createElement(DragDropDiv_1.DragDropDiv, { key: "drag-size-t", className: "dock-panel-drag-size dock-panel-drag-size-t", onDragStartT: this.onPanelCornerDragT, onDragMoveT: this.onPanelCornerDragMove, onDragEndT: this.onPanelCornerDragEnd }), diff --git a/lib/DockTabBar.d.ts b/lib/DockTabBar.d.ts index 01ff9fff..4a3678b9 100644 --- a/lib/DockTabBar.d.ts +++ b/lib/DockTabBar.d.ts @@ -6,6 +6,8 @@ interface DockTabBarProps extends TabNavListProps { onDragStart?: DragManager.DragHandler; onDragMove?: DragManager.DragHandler; onDragEnd?: DragManager.DragHandler; + onDragOver?: DragManager.DragHandler; + onDrop?: DragManager.DropHandler; TabNavList: React.ComponentType; } export declare function DockTabBar(props: DockTabBarProps): JSX.Element; diff --git a/lib/DockTabBar.js b/lib/DockTabBar.js index 0969223f..00fb4b7d 100644 --- a/lib/DockTabBar.js +++ b/lib/DockTabBar.js @@ -62,7 +62,7 @@ function checkLocalTabMove(key, tabbar) { return false; } function DockTabBar(props) { - const { onDragStart, onDragMove, onDragEnd, TabNavList, isMaximized } = props, restProps = __rest(props, ["onDragStart", "onDragMove", "onDragEnd", "TabNavList", "isMaximized"]); + const { onDragStart, onDragMove, onDragEnd, onDragOver, onDrop, TabNavList, isMaximized } = props, restProps = __rest(props, ["onDragStart", "onDragMove", "onDragEnd", "onDragOver", "onDrop", "TabNavList", "isMaximized"]); const layout = React.useContext(DockData_1.DockContextType); const ref = React.useRef(); const getRef = (div) => { @@ -77,7 +77,7 @@ function DockTabBar(props) { e.preventDefault(); } }; - return (React.createElement(DragDropDiv_1.DragDropDiv, { onDragStartT: onDragStart, onDragMoveT: onDragMove, onDragEndT: onDragEnd, role: "tablist", className: "dock-bar", onKeyDown: onKeyDown, getRef: getRef, tabIndex: -1 }, + return (React.createElement(DragDropDiv_1.DragDropDiv, { onDragStartT: onDragStart, onDragMoveT: onDragMove, onDragEndT: onDragEnd, onDragOverT: onDragOver, onDropT: onDrop, role: "tablist", className: "dock-bar", onKeyDown: onKeyDown, getRef: getRef, tabIndex: -1 }, React.createElement(TabNavList, Object.assign({}, restProps)))); } exports.DockTabBar = DockTabBar; diff --git a/lib/DockTabs.d.ts b/lib/DockTabs.d.ts index f485b7e9..f6c6e1a0 100644 --- a/lib/DockTabs.d.ts +++ b/lib/DockTabs.d.ts @@ -25,6 +25,8 @@ interface Props { onPanelDragStart: DragManager.DragHandler; onPanelDragMove: DragManager.DragHandler; onPanelDragEnd: DragManager.DragHandler; + onPanelDragOver: DragManager.DragHandler; + onPanelDrop: DragManager.DragHandler; } export declare class DockTabs extends React.PureComponent { static contextType: React.Context; diff --git a/lib/DockTabs.js b/lib/DockTabs.js index cac3060d..55392276 100644 --- a/lib/DockTabs.js +++ b/lib/DockTabs.js @@ -198,7 +198,7 @@ class DockTabs extends React.PureComponent { this.context.dockMove(panelData, null, 'new-window'); }; this.renderTabBar = (props, TabNavList) => { - let { panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd } = this.props; + let { panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd, onPanelDragOver, onPanelDrop } = this.props; let { group: groupName, panelLock } = panelData; let group = this.context.getGroup(groupName); let { panelExtra } = group; @@ -223,7 +223,7 @@ class DockTabs extends React.PureComponent { panelExtraContent = this.addNewWindowMenu(panelExtraContent, !maximizable); } } - return (React.createElement(DockTabBar_1.DockTabBar, Object.assign({ onDragStart: onPanelDragStart, onDragMove: onPanelDragMove, onDragEnd: onPanelDragEnd, TabNavList: TabNavList, isMaximized: panelData.parent.mode === 'maximize' }, props, { extra: panelExtraContent }))); + return (React.createElement(DockTabBar_1.DockTabBar, Object.assign({ onDragStart: onPanelDragStart, onDragMove: onPanelDragMove, onDragEnd: onPanelDragEnd, onDragOver: onPanelDragOver, onDrop: onPanelDrop, TabNavList: TabNavList, isMaximized: panelData.parent.mode === 'maximize' }, props, { extra: panelExtraContent }))); }; this.onTabChange = (activeId) => { this.props.panelData.activeId = activeId; diff --git a/src/DockLayout.tsx b/src/DockLayout.tsx index fcaea363..1b6a6c69 100644 --- a/src/DockLayout.tsx +++ b/src/DockLayout.tsx @@ -438,6 +438,24 @@ export class DockLayout extends DockPortalManager implements DockContext { if (element.classList.contains('dock-box')) { ratio = 0.3; } + + if(direction === 'after-tab'){ + const navWidth = 30; + const extraContentWidth = 30; + + // search parents until we find .dock-nav + let parent = element.parentElement; + while(parent && !parent.classList.contains('dock-nav')){ + parent = parent.parentElement; + } + + if(parent){ + const parentRect = parent.getBoundingClientRect(); + // make sure left is lower then parent right + left = Math.min(left, parentRect.right - navWidth - extraContentWidth - width); + } + } + switch (direction) { case 'float': { let x = (event.clientX - layoutRect.left) * scaleX; diff --git a/src/DockPanel.tsx b/src/DockPanel.tsx index d3e58e34..0169d53e 100644 --- a/src/DockPanel.tsx +++ b/src/DockPanel.tsx @@ -7,6 +7,7 @@ import {DockDropLayer} from "./DockDropLayer"; import {getFloatPanelSize, nextZIndex} from "./Algorithm"; import {DockDropEdge} from "./DockDropEdge"; import {groupClassNames} from "./Utils"; +import * as DragManager from "./dragdrop/DragManager"; import classNames from "classnames"; interface Props { @@ -132,7 +133,6 @@ export class DockPanel extends React.PureComponent { } }; - _movingW: number; _movingH: number; _movingCorner: string; @@ -258,6 +258,74 @@ export class DockPanel extends React.PureComponent { } }; + onPanelDragOver = (e: DragState) => { + let dockId = this.context.getDockId(); + let tab: TabData = DragManager.DragState.getData('tab', dockId); + let panel: PanelData = DragManager.DragState.getData('panel', dockId); + + let group: string; + if (tab) { + panel = tab.parent; + group = tab.group; + } else { + // drag whole panel + if (!panel) { + return; + } + if (panel?.panelLock) { + e.reject(); + return; + } + group = panel.group; + } + + const tabGroup = this.context.getGroup(group); + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + + const direction = 'after-tab'; + + const dockTabElements = this._ref.querySelectorAll('.dock-tab'); + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + const dockTabLastRect:HTMLElement = dockTabLastElement.querySelector('.dock-tab-hit-area'); + + if(e.clientX - this._ref.offsetLeft < 30) { + // do not allow drop on the left side of the tab + } else if (group !== lastTab.group) { + e.reject(); + } else if (tabGroup?.floatable === 'singleTab' && lastTab.parent?.parent?.mode === 'float') { + e.reject(); + } else if (tab && tab !== lastTab) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } else if (panel && panel !== lastTab.parent) { + this.context.setDropRect(dockTabLastRect, direction, this); + e.accept(''); + } + } + + onPanelDrop = (e: DragState) => { + let dockId = this.context.getDockId(); + let panel: PanelData; + let tab: TabData = DragManager.DragState.getData('tab', dockId); + if (tab) { + panel = tab.parent; + } else { + panel = DragManager.DragState.getData('panel', dockId); + } + + const direction = 'after-tab'; + + const thisPanelData = this.props.panelData; + const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + + if (tab && tab !== lastTab) { + this.context.dockMove(tab, lastTab, direction); + } else if (panel && panel !== lastTab.parent) { + this.context.dockMove(panel, lastTab, direction); + } + } + render(): React.ReactNode { let {dropFromPanel, draggingHeader} = this.state; let {panelData, size} = this.props; @@ -328,7 +396,7 @@ export class DockPanel extends React.PureComponent { + onPanelDragMove={this.onPanelHeaderDragMove} onPanelDragEnd={this.onPanelHeaderDragEnd} onPanelDragOver={this.onPanelDragOver} onPanelDrop={this.onPanelDrop}/> {isFloat ? [ ; } export function DockTabBar(props: DockTabBarProps) { const { - onDragStart, onDragMove, onDragEnd, TabNavList, isMaximized, + onDragStart, onDragMove, onDragEnd, onDragOver, onDrop, TabNavList, isMaximized, ...restProps } = props; @@ -68,6 +70,8 @@ export function DockTabBar(props: DockTabBarProps) { { @@ -261,7 +263,7 @@ export class DockTabs extends React.PureComponent { } renderTabBar = (props: any, TabNavList: React.ComponentType) => { - let {panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd} = this.props; + let {panelData, onPanelDragStart, onPanelDragMove, onPanelDragEnd, onPanelDragOver, onPanelDrop} = this.props; let {group: groupName, panelLock} = panelData; let group = this.context.getGroup(groupName); let {panelExtra} = group; @@ -293,7 +295,7 @@ export class DockTabs extends React.PureComponent { } } return ( - ); From a95435329a8bf6f9e394586e9ba4356b2726508d Mon Sep 17 00:00:00 2001 From: Erik Sombroek Date: Fri, 13 Oct 2023 09:13:00 +0200 Subject: [PATCH 2/5] Added watch script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ddc45f14..56de1c7a 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "build-doc": "typedoc", "build-www": "ts-node tool/build-www", "build-es": "tsc --module es2020 --outDir ./es", - "build-lib": "tsc --outDir ./lib" + "build-lib": "tsc --outDir ./lib", + "watch-lib": "tsc --watch --outDir ./lib" } } From fe65481c7d7a422cd19aa05d6546f1df6a369b58 Mon Sep 17 00:00:00 2001 From: Erik Sombroek Date: Fri, 13 Oct 2023 17:45:01 +0200 Subject: [PATCH 3/5] Allow dragging tabs on empty panel --- es/DockPanel.js | 28 ++++++++++++++++++---------- lib/DockPanel.js | 28 ++++++++++++++++++---------- src/DockPanel.tsx | 30 ++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/es/DockPanel.js b/es/DockPanel.js index e378d74e..a07d936c 100644 --- a/es/DockPanel.js +++ b/es/DockPanel.js @@ -231,23 +231,30 @@ export class DockPanel extends React.PureComponent { const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; const direction = 'after-tab'; const dockTabElements = this._ref.querySelectorAll('.dock-tab'); - const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; - const dockTabLastRect = dockTabLastElement.querySelector('.dock-tab-hit-area'); - if (e.clientX - this._ref.offsetLeft < 30) { + let rectElement = this._ref.querySelector('.dock-nav-list'); + if (dockTabElements.length) { + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + rectElement = dockTabLastElement.querySelector('.dock-tab-hit-area'); + } + if (!lastTab) { + e.accept(); + this.context.setDropRect(rectElement, direction, this); + } + else if (e.clientX - this._ref.offsetLeft < 30) { // do not allow drop on the left side of the tab } - else if (group !== lastTab.group) { + else if (group !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.group)) { e.reject(); } else if ((tabGroup === null || tabGroup === void 0 ? void 0 : tabGroup.floatable) === 'singleTab' && ((_b = (_a = lastTab.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.mode) === 'float') { e.reject(); } else if (tab && tab !== lastTab) { - this.context.setDropRect(dockTabLastRect, direction, this); + this.context.setDropRect(rectElement, direction, this); e.accept(''); } - else if (panel && panel !== lastTab.parent) { - this.context.setDropRect(dockTabLastRect, direction, this); + else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + this.context.setDropRect(rectElement, direction, this); e.accept(''); } }; @@ -264,11 +271,12 @@ export class DockPanel extends React.PureComponent { const direction = 'after-tab'; const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + const target = lastTab ? lastTab : thisPanelData; if (tab && tab !== lastTab) { - this.context.dockMove(tab, lastTab, direction); + this.context.dockMove(tab, target, direction); } - else if (panel && panel !== lastTab.parent) { - this.context.dockMove(panel, lastTab, direction); + else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + this.context.dockMove(panel, target, direction); } }; this._unmounted = false; diff --git a/lib/DockPanel.js b/lib/DockPanel.js index e6bda36c..82da490f 100644 --- a/lib/DockPanel.js +++ b/lib/DockPanel.js @@ -256,23 +256,30 @@ class DockPanel extends React.PureComponent { const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; const direction = 'after-tab'; const dockTabElements = this._ref.querySelectorAll('.dock-tab'); - const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; - const dockTabLastRect = dockTabLastElement.querySelector('.dock-tab-hit-area'); - if (e.clientX - this._ref.offsetLeft < 30) { + let rectElement = this._ref.querySelector('.dock-nav-list'); + if (dockTabElements.length) { + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + rectElement = dockTabLastElement.querySelector('.dock-tab-hit-area'); + } + if (!lastTab) { + e.accept(); + this.context.setDropRect(rectElement, direction, this); + } + else if (e.clientX - this._ref.offsetLeft < 30) { // do not allow drop on the left side of the tab } - else if (group !== lastTab.group) { + else if (group !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.group)) { e.reject(); } else if ((tabGroup === null || tabGroup === void 0 ? void 0 : tabGroup.floatable) === 'singleTab' && ((_b = (_a = lastTab.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.mode) === 'float') { e.reject(); } else if (tab && tab !== lastTab) { - this.context.setDropRect(dockTabLastRect, direction, this); + this.context.setDropRect(rectElement, direction, this); e.accept(''); } - else if (panel && panel !== lastTab.parent) { - this.context.setDropRect(dockTabLastRect, direction, this); + else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + this.context.setDropRect(rectElement, direction, this); e.accept(''); } }; @@ -289,11 +296,12 @@ class DockPanel extends React.PureComponent { const direction = 'after-tab'; const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + const target = lastTab ? lastTab : thisPanelData; if (tab && tab !== lastTab) { - this.context.dockMove(tab, lastTab, direction); + this.context.dockMove(tab, target, direction); } - else if (panel && panel !== lastTab.parent) { - this.context.dockMove(panel, lastTab, direction); + else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + this.context.dockMove(panel, target, direction); } }; this._unmounted = false; diff --git a/src/DockPanel.tsx b/src/DockPanel.tsx index 0169d53e..9525f9ad 100644 --- a/src/DockPanel.tsx +++ b/src/DockPanel.tsx @@ -286,20 +286,28 @@ export class DockPanel extends React.PureComponent { const direction = 'after-tab'; const dockTabElements = this._ref.querySelectorAll('.dock-tab'); - const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; - const dockTabLastRect:HTMLElement = dockTabLastElement.querySelector('.dock-tab-hit-area'); - if(e.clientX - this._ref.offsetLeft < 30) { + let rectElement:HTMLElement = this._ref.querySelector('.dock-nav-list'); + + if (dockTabElements.length) { + const dockTabLastElement = dockTabElements[dockTabElements.length - 1]; + rectElement = dockTabLastElement.querySelector('.dock-tab-hit-area'); + } + + if(!lastTab){ + e.accept(); + this.context.setDropRect(rectElement, direction, this); + }else if(e.clientX - this._ref.offsetLeft < 30) { // do not allow drop on the left side of the tab - } else if (group !== lastTab.group) { + }else if (group !== lastTab?.group) { e.reject(); } else if (tabGroup?.floatable === 'singleTab' && lastTab.parent?.parent?.mode === 'float') { e.reject(); } else if (tab && tab !== lastTab) { - this.context.setDropRect(dockTabLastRect, direction, this); + this.context.setDropRect(rectElement, direction, this); e.accept(''); - } else if (panel && panel !== lastTab.parent) { - this.context.setDropRect(dockTabLastRect, direction, this); + } else if (panel && panel !== lastTab?.parent) { + this.context.setDropRect(rectElement, direction, this); e.accept(''); } } @@ -319,10 +327,12 @@ export class DockPanel extends React.PureComponent { const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; + const target = lastTab ? lastTab : thisPanelData; + if (tab && tab !== lastTab) { - this.context.dockMove(tab, lastTab, direction); - } else if (panel && panel !== lastTab.parent) { - this.context.dockMove(panel, lastTab, direction); + this.context.dockMove(tab, target, direction); + } else if (panel && panel !== lastTab?.parent) { + this.context.dockMove(panel, target, direction); } } From 1e975eca77790e7f5b22a9c051366c262633d246 Mon Sep 17 00:00:00 2001 From: Erik Sombroek Date: Sat, 14 Oct 2023 10:04:08 +0200 Subject: [PATCH 4/5] Fixed memory leak --- es/DockPanel.js | 3 +++ lib/DockPanel.js | 3 +++ src/DockPanel.tsx | 3 +++ 3 files changed, 9 insertions(+) diff --git a/es/DockPanel.js b/es/DockPanel.js index a07d936c..4327e94a 100644 --- a/es/DockPanel.js +++ b/es/DockPanel.js @@ -93,6 +93,9 @@ export class DockPanel extends React.PureComponent { }; this.onPanelHeaderDragEnd = (e) => { var _a; + if (this._unmounted) { + return; + } this.setState({ draggingHeader: false }); if (e.dropped === false) { let { panelData } = this.props; diff --git a/lib/DockPanel.js b/lib/DockPanel.js index 82da490f..bcdb4875 100644 --- a/lib/DockPanel.js +++ b/lib/DockPanel.js @@ -118,6 +118,9 @@ class DockPanel extends React.PureComponent { }; this.onPanelHeaderDragEnd = (e) => { var _a; + if (this._unmounted) { + return; + } this.setState({ draggingHeader: false }); if (e.dropped === false) { let { panelData } = this.props; diff --git a/src/DockPanel.tsx b/src/DockPanel.tsx index 9525f9ad..3a450b78 100644 --- a/src/DockPanel.tsx +++ b/src/DockPanel.tsx @@ -123,6 +123,9 @@ export class DockPanel extends React.PureComponent { this.forceUpdate(); }; onPanelHeaderDragEnd = (e: DragState) => { + if (this._unmounted) { + return; + } this.setState({draggingHeader: false}); if (e.dropped === false) { let {panelData} = this.props; From 8de2be5d20511b3637460618c6f10e835756d03d Mon Sep 17 00:00:00 2001 From: Erik Sombroek Date: Sat, 14 Oct 2023 12:51:37 +0200 Subject: [PATCH 5/5] Fixed panel to empty panel docking --- es/DockLayout.js | 2 ++ es/DockPanel.js | 6 +++++- lib/DockLayout.js | 2 ++ lib/DockPanel.js | 6 +++++- src/DockLayout.tsx | 2 ++ src/DockPanel.tsx | 7 +++++-- 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/es/DockLayout.js b/es/DockLayout.js index 4a99d319..87154ef7 100644 --- a/es/DockLayout.js +++ b/es/DockLayout.js @@ -200,8 +200,10 @@ export class DockLayout extends DockPortalManager { // panel target if (direction === 'middle') { layout = Algorithm.addTabToPanel(layout, source, target); + console.log("Panel A"); } else { + console.log("Panel B"); let newPanel = Algorithm.converToPanel(source); layout = Algorithm.dockPanelToPanel(layout, newPanel, target, direction); } diff --git a/es/DockPanel.js b/es/DockPanel.js index 4327e94a..7de40b8d 100644 --- a/es/DockPanel.js +++ b/es/DockPanel.js @@ -271,14 +271,18 @@ export class DockPanel extends React.PureComponent { else { panel = DragManager.DragState.getData('panel', dockId); } - const direction = 'after-tab'; + let direction = 'after-tab'; const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; const target = lastTab ? lastTab : thisPanelData; + if (!lastTab) { + direction = 'middle'; + } if (tab && tab !== lastTab) { this.context.dockMove(tab, target, direction); } else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + console.log("dock move panel", panel, target, direction); this.context.dockMove(panel, target, direction); } }; diff --git a/lib/DockLayout.js b/lib/DockLayout.js index a088ae59..a6ba8ffe 100644 --- a/lib/DockLayout.js +++ b/lib/DockLayout.js @@ -225,8 +225,10 @@ class DockLayout extends DockPortalManager { // panel target if (direction === 'middle') { layout = Algorithm.addTabToPanel(layout, source, target); + console.log("Panel A"); } else { + console.log("Panel B"); let newPanel = Algorithm.converToPanel(source); layout = Algorithm.dockPanelToPanel(layout, newPanel, target, direction); } diff --git a/lib/DockPanel.js b/lib/DockPanel.js index bcdb4875..166e6492 100644 --- a/lib/DockPanel.js +++ b/lib/DockPanel.js @@ -296,14 +296,18 @@ class DockPanel extends React.PureComponent { else { panel = DragManager.DragState.getData('panel', dockId); } - const direction = 'after-tab'; + let direction = 'after-tab'; const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; const target = lastTab ? lastTab : thisPanelData; + if (!lastTab) { + direction = 'middle'; + } if (tab && tab !== lastTab) { this.context.dockMove(tab, target, direction); } else if (panel && panel !== (lastTab === null || lastTab === void 0 ? void 0 : lastTab.parent)) { + console.log("dock move panel", panel, target, direction); this.context.dockMove(panel, target, direction); } }; diff --git a/src/DockLayout.tsx b/src/DockLayout.tsx index 1b6a6c69..f02da5b4 100644 --- a/src/DockLayout.tsx +++ b/src/DockLayout.tsx @@ -254,7 +254,9 @@ export class DockLayout extends DockPortalManager implements DockContext { // panel target if (direction === 'middle') { layout = Algorithm.addTabToPanel(layout, source, target as PanelData); + console.log("Panel A"); } else { + console.log("Panel B"); let newPanel = Algorithm.converToPanel(source); layout = Algorithm.dockPanelToPanel(layout, newPanel, target as PanelData, direction); } diff --git a/src/DockPanel.tsx b/src/DockPanel.tsx index 3a450b78..8fce332c 100644 --- a/src/DockPanel.tsx +++ b/src/DockPanel.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import {DockContext, DockContextType, PanelData, TabData} from "./DockData"; +import {DockContext, DockContextType, DropDirection, PanelData, TabData} from "./DockData"; import {DockTabs} from "./DockTabs"; import {DragDropDiv} from "./dragdrop/DragDropDiv"; import {DragState} from "./dragdrop/DragManager"; @@ -325,13 +325,16 @@ export class DockPanel extends React.PureComponent { panel = DragManager.DragState.getData('panel', dockId); } - const direction = 'after-tab'; + let direction:DropDirection = 'after-tab'; const thisPanelData = this.props.panelData; const lastTab = thisPanelData.tabs[thisPanelData.tabs.length - 1]; const target = lastTab ? lastTab : thisPanelData; + if(!lastTab){ + direction = 'middle'; + } if (tab && tab !== lastTab) { this.context.dockMove(tab, target, direction); } else if (panel && panel !== lastTab?.parent) {