mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 14:10:00 +02:00
Add splitview drag and drop functionality
This commit is contained in:
parent
694cbc9d89
commit
a5db255ab7
1 changed files with 93 additions and 24 deletions
|
@ -67,6 +67,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
_tabToSplitNode = new Map();
|
_tabToSplitNode = new Map();
|
||||||
dropZone;
|
dropZone;
|
||||||
_edgeHoverSize = 20;
|
_edgeHoverSize = 20;
|
||||||
|
_minResizeWidth;
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
XPCOMUtils.defineLazyPreferenceGetter(this, 'canChangeTabOnHover', 'zen.splitView.change-on-hover', false);
|
XPCOMUtils.defineLazyPreferenceGetter(this, 'canChangeTabOnHover', 'zen.splitView.change-on-hover', false);
|
||||||
|
@ -133,15 +134,17 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
this.removeGroup(groupIndex);
|
this.removeGroup(groupIndex);
|
||||||
} else {
|
} else {
|
||||||
const node = this.getSplitNodeFromTab(tab);
|
const node = this.getSplitNodeFromTab(tab);
|
||||||
this.applyRemoveNode(node);
|
const toUpdate = this.removeNode(node);
|
||||||
|
this.applyGridLayout(toUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a SplitNode from its tree and the view
|
* Remove a SplitNode from its tree and the view
|
||||||
* @param {SplitNode} toRemove
|
* @param {SplitNode} toRemove
|
||||||
|
* @return {SplitNode} that has to be updated
|
||||||
*/
|
*/
|
||||||
applyRemoveNode(toRemove) {
|
removeNode(toRemove) {
|
||||||
this._removeNodeSplitters(toRemove, true);
|
this._removeNodeSplitters(toRemove, true);
|
||||||
const parent = toRemove.parent;
|
const parent = toRemove.parent;
|
||||||
const childIndex = parent.children.indexOf(toRemove);
|
const childIndex = parent.children.indexOf(toRemove);
|
||||||
|
@ -149,8 +152,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
if (parent.children.length !== 1) {
|
if (parent.children.length !== 1) {
|
||||||
const nodeToResize = parent.children[Math.max(0, childIndex - 1)];
|
const nodeToResize = parent.children[Math.max(0, childIndex - 1)];
|
||||||
nodeToResize.sizeInParent += toRemove.sizeInParent;
|
nodeToResize.sizeInParent += toRemove.sizeInParent;
|
||||||
this.applyGridLayout(parent);
|
return parent;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// node that is not a leaf cannot have less than 2 children, this makes for better resizing
|
// node that is not a leaf cannot have less than 2 children, this makes for better resizing
|
||||||
// node takes place of parent
|
// node takes place of parent
|
||||||
|
@ -160,12 +162,13 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
leftOverChild.parent = parent.parent;
|
leftOverChild.parent = parent.parent;
|
||||||
parent.parent.children[parent.parent.children.indexOf(parent)] = leftOverChild;
|
parent.parent.children[parent.parent.children.indexOf(parent)] = leftOverChild;
|
||||||
this._removeNodeSplitters(parent, false);
|
this._removeNodeSplitters(parent, false);
|
||||||
this.applyGridLayout(parent.parent);
|
return parent.parent;
|
||||||
} else {
|
} else {
|
||||||
const viewData = Object.values(this._data).find(s => s.layoutTree === parent);
|
const viewData = Object.values(this._data).find(s => s.layoutTree === parent);
|
||||||
viewData.layoutTree = parent;
|
viewData.layoutTree = leftOverChild;
|
||||||
parent.positionToRoot = null;
|
leftOverChild.positionToRoot = null;
|
||||||
this.applyGridLayout(parent);
|
leftOverChild.parent = null;
|
||||||
|
return leftOverChild;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +199,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
|
this.tabBrowserPanel.addEventListener('dragstart', this.onBrowserDragStart);
|
||||||
this.tabBrowserPanel.addEventListener('dragover', this.onBrowserDragOver);
|
this.tabBrowserPanel.addEventListener('dragover', this.onBrowserDragOver);
|
||||||
this.tabBrowserPanel.addEventListener('drop', this.onBrowserDrop);
|
this.tabBrowserPanel.addEventListener('drop', this.onBrowserDrop);
|
||||||
|
this.tabBrowserPanel.addEventListener('dragend', this.onBrowserDragEnd)
|
||||||
|
document.addEventListener('click', this.disableTabSwitchView, {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
disableTabSwitchView = () => {
|
disableTabSwitchView = () => {
|
||||||
|
@ -217,6 +222,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
if (!browser) {
|
if (!browser) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.dropZone.setAttribute('enabled', true);
|
||||||
const browserContainer = browser.closest('.browserSidebarContainer');
|
const browserContainer = browser.closest('.browserSidebarContainer');
|
||||||
event.dataTransfer.setData('text/plain', browserContainer.id);
|
event.dataTransfer.setData('text/plain', browserContainer.id);
|
||||||
|
|
||||||
|
@ -288,7 +294,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBrowserDragOver = (event) => {
|
onBrowserDragOver = (event) => {
|
||||||
if (!this.splitViewActive) return;
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const browser = event.target.querySelector('browser');
|
const browser = event.target.querySelector('browser');
|
||||||
if (!browser) return;
|
if (!browser) return;
|
||||||
|
@ -313,6 +318,10 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBrowserDragEnd = (event) => {
|
||||||
|
this.dropZone.removeAttribute('enabled');
|
||||||
|
}
|
||||||
|
|
||||||
_oppositeSide(side) {
|
_oppositeSide(side) {
|
||||||
if (side === 'top') return 'bottom';
|
if (side === 'top') return 'bottom';
|
||||||
if (side === 'bottom') return 'top';
|
if (side === 'bottom') return 'top';
|
||||||
|
@ -331,29 +340,89 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBrowserDrop = (event) => {
|
onBrowserDrop = (event) => {
|
||||||
if (!this.splitViewActive) return;
|
const browserDroppedOn = event.target.querySelector('browser');
|
||||||
console.log(event);
|
if (!browserDroppedOn) return;
|
||||||
const containerId = event.dataTransfer.getData('text/plain');
|
|
||||||
|
|
||||||
const startTab = gBrowser.getTabForBrowser(
|
const droppedContainerId= event.dataTransfer.getData('text/plain');
|
||||||
document.getElementById(containerId).querySelector('browser')
|
|
||||||
|
const droppedTab = gBrowser.getTabForBrowser(
|
||||||
|
document.getElementById(droppedContainerId).querySelector('browser')
|
||||||
);
|
);
|
||||||
const endTab = gBrowser.getTabForBrowser(
|
if (!droppedTab) return;
|
||||||
|
const droppedOnTab = gBrowser.getTabForBrowser(
|
||||||
event.target.querySelector('browser')
|
event.target.querySelector('browser')
|
||||||
);
|
);
|
||||||
if (!startTab || !endTab) {
|
|
||||||
|
const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserDroppedOn.getBoundingClientRect());
|
||||||
|
const droppedSplitNode = this.getSplitNodeFromTab(droppedTab);
|
||||||
|
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||||
|
if (hoverSide === 'center') {
|
||||||
|
this.swapNodes(droppedSplitNode, droppedOnSplitNode);
|
||||||
|
this.applyGridLayout(this._data[this.currentView].layoutTree);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.removeNode(droppedSplitNode);
|
||||||
|
this.splitIntoNode(droppedOnSplitNode, droppedSplitNode, hoverSide, .5);
|
||||||
|
this.applyGridLayout(this._data[this.currentView].layoutTree);
|
||||||
|
}
|
||||||
|
|
||||||
const currentData = this._data[this.currentView];
|
/**
|
||||||
|
*
|
||||||
|
* @param node1
|
||||||
|
* @param node2
|
||||||
|
*/
|
||||||
|
swapNodes(node1, node2) {
|
||||||
|
this._swapField('sizeInParent', node1, node2);
|
||||||
|
|
||||||
const startIdx = currentData.tabs.indexOf(startTab);
|
const node1Idx = node1.parent.children.indexOf(node1);
|
||||||
const endIdx = currentData.tabs.indexOf(endTab);
|
const node2Idx = node2.parent.children.indexOf(node2);
|
||||||
|
node1.parent.children[node1Idx] = node2;
|
||||||
|
node2.parent.children[node2Idx] = node1;
|
||||||
|
|
||||||
currentData.tabs[startIdx] = endTab;
|
this._swapField('parent', node1, node2);
|
||||||
currentData.tabs[endIdx] = startTab;
|
}
|
||||||
this.dropZone.style.inset =
|
|
||||||
`${nodeRootPosition.top}% ${nodeRootPosition.right}% ${nodeRootPosition.bottom}% ${nodeRootPosition.left}%`;
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @param nodeToInsert
|
||||||
|
* @param side
|
||||||
|
* @param sizeOfInsertedNode percentage of node width or height that nodeToInsert will take
|
||||||
|
*/
|
||||||
|
splitIntoNode(node, nodeToInsert, side, sizeOfInsertedNode) {
|
||||||
|
const splitDirection = side === 'left' || side === 'right' ? 'row' : 'column';
|
||||||
|
const splitPosition = side === 'left' || side === 'top' ? 0 : 1;
|
||||||
|
|
||||||
|
let nodeSize;
|
||||||
|
let newParent;
|
||||||
|
if (splitDirection === node.parent.direction) {
|
||||||
|
newParent = node.parent;
|
||||||
|
nodeSize = node.sizeInParent;
|
||||||
|
} else {
|
||||||
|
nodeSize = 100;
|
||||||
|
const nodeIndex = node.parent.children.indexOf(node);
|
||||||
|
newParent = new SplitNode(splitDirection, node.sizeInParent);
|
||||||
|
if (node.parent) {
|
||||||
|
newParent.parent = node.parent;
|
||||||
|
} else {
|
||||||
|
const viewData = Object.values(this._data).find(s => s.layoutTree === node.parent);
|
||||||
|
viewData.layoutTree = newParent;
|
||||||
|
}
|
||||||
|
node.parent.children[nodeIndex] = newParent;
|
||||||
|
newParent.addChild(node);
|
||||||
|
}
|
||||||
|
node.sizeInParent = 100 - (nodeSize * sizeOfInsertedNode);
|
||||||
|
nodeToInsert.sizeInParent = nodeSize * sizeOfInsertedNode;
|
||||||
|
|
||||||
|
const index = newParent.children.indexOf(node);
|
||||||
|
newParent.children.splice(index + splitPosition, 0, nodeToInsert);
|
||||||
|
nodeToInsert.parent = newParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapField(fieldName, obj1, obj2) {
|
||||||
|
const swap = obj1[fieldName];
|
||||||
|
obj1[fieldName] = obj2[fieldName];
|
||||||
|
obj2[fieldName] = swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue