diff --git a/apps/content/src/browser/components/Tab.svelte b/apps/content/src/browser/components/Tab.svelte
index cf4b72a..e5bc863 100644
--- a/apps/content/src/browser/components/Tab.svelte
+++ b/apps/content/src/browser/components/Tab.svelte
@@ -13,9 +13,12 @@
} from '../windowApi/WindowTabs.js'
import { readable } from 'svelte/store'
import { dragTabIds, dragTabsTranslation } from './tabs__drag.js'
+ import { derived } from '@amadeus-it-group/tansu'
/** @type {WebsiteView} */
export let view
+ /** @type {number} */
+ export let index
const iconUrl = readable(view.iconUrl, (set) => {
view.events.on('changeIcon', set)
@@ -27,6 +30,8 @@
return () => view.events.off('changeTitle', set)
})
+ const transform = derived([dragTabIds, dragTabsTranslation], ([dragTabIds, dragTabsTranslation]) => dragTabIds.includes(view.windowBrowserId) ? `translateY(${dragTabsTranslation}px)` : '')
+
$: isActive = $activeTabId === view.windowBrowserId
$: isSelected = $selectedTabIds.includes(view.windowBrowserId)
@@ -89,12 +94,13 @@
role="tab"
id={`tab-${view.windowBrowserId}`}
aria-selected={isActive}
+ data-index={index}
data-not-active-selected={isSelected}
data-window-browser-id={view.windowBrowserId}
data-dragging={$dragTabIds.includes(view.windowBrowserId)}
aria-controls={`website-view-${view.windowBrowserId}`}
tabindex={isActive ? 0 : -1}
- style:transform={$dragTabIds.includes(view.windowBrowserId) ? `translateY(${$dragTabsTranslation}px)` : ''}
+ style:transform={$transform}
on:mousedown={(event) => {
if (event.ctrlKey) {
addSelectedTabs([view.windowBrowserId])
@@ -185,6 +191,7 @@
width: 100%;
border-radius: 1rem;
+ margin-bottom: 0.25rem;
display: flex;
align-items: center;
@@ -208,6 +215,10 @@
pointer-events: none;
}
+ button[role='tab'][data-dragging='false'] {
+ transition: transform 0.2s;
+ }
+
img {
width: 1.5rem;
height: 1.5rem;
diff --git a/apps/content/src/browser/components/Tabs.svelte b/apps/content/src/browser/components/Tabs.svelte
index bd6f706..6667af2 100644
--- a/apps/content/src/browser/components/Tabs.svelte
+++ b/apps/content/src/browser/components/Tabs.svelte
@@ -12,8 +12,8 @@
- {#each $windowTabs as tab (tab.view.windowBrowserId)}
-
+ {#each $windowTabs as tab, index (tab.view.windowBrowserId)}
+
{/each}
@@ -42,7 +42,6 @@
display: flex;
flex-direction: column;
- gap: 0.25rem;
}
.spacer {
diff --git a/apps/content/src/browser/components/tabs__drag.js b/apps/content/src/browser/components/tabs__drag.js
index d1d0dc0..cc4665a 100644
--- a/apps/content/src/browser/components/tabs__drag.js
+++ b/apps/content/src/browser/components/tabs__drag.js
@@ -1,5 +1,5 @@
// @ts-check
-import { batch, writable } from '@amadeus-it-group/tansu'
+import { writable } from '@amadeus-it-group/tansu'
import { browserImports } from '../browserImports.js'
import {
@@ -11,14 +11,14 @@ import {
const WINDOW_ID_TYPE = 'text/x-fushra-window-id'
const WINDOW_BROWSER_IDS_TYPE = 'text/x-fushra-window-browser-ids'
-const ANIM_SCREEN_Y_TYPE = 'number/x-fushra-screen-y'
-
/** @type {import('@amadeus-it-group/tansu').WritableSignal} */
export let dragTabIds = writable([])
export let dragTabsTranslation = writable(0)
let startEventRelativeY = 0
let lastScreenY = 0
+let startingIndex = 0
+let expectedIndex = 0
/** @type {HTMLCanvasElement | null} */
let dndCanvas = null
/** @type {XULPanel | null} */
@@ -48,6 +48,7 @@ export function dragStart(event) {
const rect = target.getBoundingClientRect()
startEventRelativeY = event.screenY - rect.y
+ startingIndex = Number(target.dataset.index)
}
if (dragTabIds().length != 1) {
@@ -118,11 +119,46 @@ export function dragOver(event) {
* @param {DragEvent} event
*/
export function dragEnd(event) {
+ windowTabs.update((tabs) => {
+ if (dragTabIds().length == 0) {
+ return tabs
+ }
+
+ const targetBrowserId =
+ tabs[Math.min(expectedIndex, tabs.length - 1)].view.windowBrowserId
+ if (dragTabIds()[0] == targetBrowserId) {
+ return tabs
+ }
+
+ const draggingTabs = tabs.filter((tab) =>
+ dragTabIds().includes(tab.view.windowBrowserId),
+ )
+ return tabs
+ .filter((tab) => !dragTabIds().includes(tab.view.windowBrowserId))
+ .flatMap((tab) => {
+ if (tab.view.windowBrowserId == targetBrowserId) {
+ if (expectedIndex < startingIndex) {
+ return [...draggingTabs, tab]
+ }
+
+ return [tab, ...draggingTabs]
+ }
+
+ return tab
+ })
+ })
+
dragTabIds.set([])
dragTabsTranslation.set(0)
startEventRelativeY = 0
lastScreenY = 0
+
+ document
+ .querySelectorAll("button[role='tab']")
+ .forEach(
+ (/** @type {HTMLButtonElement} */ tab) => void (tab.style.transform = ''),
+ )
}
function groupDraggedTabs() {
@@ -173,62 +209,29 @@ function animateTabMove(event) {
return
}
- batch(() => {
- let indexChange = 0
+ /** @type {HTMLButtonElement} */
+ const firstDraggingTab = document.getElementById(`tab-${dragTabIds()[0]}`)
+ /** @type {HTMLLIElement} */
+ const firstPresentation = firstDraggingTab?.parentElement
+ const presentationBox = firstPresentation.getBoundingClientRect()
- /** @type {HTMLElement} */
- const target = event.target
- const targetBrowserIdRaw = target.dataset.windowBrowserId
- if (targetBrowserIdRaw) {
- const targetBrowserId = Number(targetBrowserIdRaw)
-
- windowTabs.update((tabs) => {
- if (dragTabIds().length == 0) {
- return tabs
- }
-
- const draggingTabIndex = tabs.findIndex((tab) =>
- dragTabIds().includes(tab.view.windowBrowserId),
- )
- const dragTargetIndex = tabs.findIndex(
- (tab) => tab.view.windowBrowserId === targetBrowserId,
- )
-
- indexChange = dragTargetIndex - dragTargetIndex
-
- const draggingTabs = tabs.filter((tab) =>
- dragTabIds().includes(tab.view.windowBrowserId),
- )
- return tabs
- .filter((tab) => !dragTabIds().includes(tab.view.windowBrowserId))
- .flatMap((tab) => {
- if (tab.view.windowBrowserId == targetBrowserId) {
- if (dragTargetIndex == 0) {
- return [...draggingTabs, tab]
- }
-
- return [tab, ...draggingTabs]
- }
-
- return tab
- })
- })
- }
-
- /** @type {HTMLButtonElement} */
- const dragTrigger = document.getElementById(`tab-${dragTabIds()[0]}`)
- const presentationElement = dragTrigger.parentElement
-
- const presentationBox = presentationElement?.getBoundingClientRect()
- dragTabsTranslation.set(
- event.screenY -
- presentationBox?.y -
- startEventRelativeY -
- indexChange * presentationBox?.height,
- )
-
- dragTrigger.style.transform = `translateY(${dragTabsTranslation()}px)`
+ dragTabsTranslation.set(
+ event.screenY - presentationBox.y - startEventRelativeY,
+ )
- lastScreenY = event.screenY
- })
+ expectedIndex = Math.floor(event.screenY / presentationBox.height)
+ const translateAmount = presentationBox.height * dragTabIds().length
+
+ document
+ .querySelectorAll("button[role='tab'][data-dragging='false']")
+ .forEach((/** @type {HTMLButtonElement} */ tab) => {
+ const index = Number(tab.dataset.index)
+ if (index <= expectedIndex && index >= startingIndex) {
+ tab.style.transform = `translateY(-${translateAmount}px)`
+ } else if (index >= expectedIndex && index <= startingIndex) {
+ tab.style.transform = `translateY(${translateAmount}px)`
+ } else {
+ tab.style.transform = ''
+ }
+ })
}