From 5517f473689e3954dcee8b034042bc931a185093 Mon Sep 17 00:00:00 2001 From: ndom91 Date: Wed, 22 Jan 2025 15:19:10 +0100 Subject: [PATCH 1/7] fix: rm unused app/desktop/ ContextMenu components --- .../desktop/src/components/ContextMenu.svelte | 236 ------------------ .../src/components/ContextMenuItem.svelte | 62 ----- .../src/components/ContextMenuSection.svelte | 32 --- 3 files changed, 330 deletions(-) delete mode 100644 apps/desktop/src/components/ContextMenu.svelte delete mode 100644 apps/desktop/src/components/ContextMenuItem.svelte delete mode 100644 apps/desktop/src/components/ContextMenuSection.svelte diff --git a/apps/desktop/src/components/ContextMenu.svelte b/apps/desktop/src/components/ContextMenu.svelte deleted file mode 100644 index 99da65e2f3..0000000000 --- a/apps/desktop/src/components/ContextMenu.svelte +++ /dev/null @@ -1,236 +0,0 @@ - - - - -{#snippet contextMenu()} -
close() - }} - bind:clientHeight={contextMenuHeight} - bind:clientWidth={contextMenuWidth} - class="context-menu" - class:top-oriented={verticalAlign === 'top'} - class:bottom-oriented={verticalAlign === 'bottom'} - style:top="{menuPosition.y}px" - style:left="{menuPosition.x}px" - style:transform-origin={setTransformOrigin()} - style:--animation-transform-shift={verticalAlign === 'top' ? '6px' : '-6px'} - > - {@render children(item)} -
-{/snippet} - -{#if isVisible} -
- {@render contextMenu()} -
-{/if} - - diff --git a/apps/desktop/src/components/ContextMenuItem.svelte b/apps/desktop/src/components/ContextMenuItem.svelte deleted file mode 100644 index 813ee058ad..0000000000 --- a/apps/desktop/src/components/ContextMenuItem.svelte +++ /dev/null @@ -1,62 +0,0 @@ - - - - - diff --git a/apps/desktop/src/components/ContextMenuSection.svelte b/apps/desktop/src/components/ContextMenuSection.svelte deleted file mode 100644 index 73ad63dc07..0000000000 --- a/apps/desktop/src/components/ContextMenuSection.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - -
- {#if title} -
{title}
- {/if} - {@render children()} -
- - From 2fd4dfee623eb14a77f5b3782ccd901c5f67fdf7 Mon Sep 17 00:00:00 2001 From: ndom91 Date: Fri, 24 Jan 2025 13:32:00 +0100 Subject: [PATCH 2/7] wip: sidebar left/right side calculations --- .../src/components/ChromeSidebar.svelte | 56 +++++++++-------- packages/ui/src/lib/ContextMenu.svelte | 63 ++++++++++++++----- 2 files changed, 76 insertions(+), 43 deletions(-) diff --git a/apps/desktop/src/components/ChromeSidebar.svelte b/apps/desktop/src/components/ChromeSidebar.svelte index 9771337fb2..60890b95de 100644 --- a/apps/desktop/src/components/ChromeSidebar.svelte +++ b/apps/desktop/src/components/ChromeSidebar.svelte @@ -164,7 +164,7 @@ -
+
{#if routes.isSettingsPath}
@@ -177,30 +177,32 @@ class={['btn-square', routes.isSettingsPath && 'btn-active']} />
- +
+ +
From 916b080fcf725ee30a53899da098d6cfd7b88fb8 Mon Sep 17 00:00:00 2001 From: ndom91 Date: Mon, 27 Jan 2025 18:43:57 +0100 Subject: [PATCH 3/7] feat: allow ContextMenu on left/right/top/bottom of target element --- packages/ui/src/lib/ContextMenu.svelte | 145 ++++++++++++------------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/packages/ui/src/lib/ContextMenu.svelte b/packages/ui/src/lib/ContextMenu.svelte index 89ce17691f..6b2bb8831a 100644 --- a/packages/ui/src/lib/ContextMenu.svelte +++ b/packages/ui/src/lib/ContextMenu.svelte @@ -4,20 +4,29 @@ import { portal } from '@gitbutler/ui/utils/portal'; import { type Snippet } from 'svelte'; - interface Props { + interface BaseProps { + children: Snippet<[item: any]>; leftClickTrigger?: HTMLElement; rightClickTrigger?: HTMLElement; - side?: 'top' | 'bottom' | 'left' | 'right'; - /** Only valid if side = 'left' | 'right' */ - verticalAlign?: 'top' | 'bottom'; - /** Only valid if side = 'top' | 'bottom' */ - horizontalAlign?: 'left' | 'right'; - children: Snippet<[item: any]>; onclose?: () => void; onopen?: () => void; ontoggle?: (isOpen: boolean, isLeftClick: boolean) => void; } + type HorizontalProps = BaseProps & { + side: 'top' | 'bottom'; + horizontalAlign?: 'left' | 'right'; + verticalAlign?: never; + }; + + type VerticalProps = BaseProps & { + side: 'left' | 'right'; + verticalAlign?: 'top' | 'bottom'; + horizontalAlign?: never; + }; + + type Props = HorizontalProps | VerticalProps; + let { leftClickTrigger, rightClickTrigger, @@ -49,35 +58,26 @@ : 0; } else if (['left', 'right'].includes(side)) { if (verticalAlign === 'top') { - console.log('right/top'); - return targetBoundingRect?.bottom + targetBoundingRect?.height; + return targetBoundingRect?.bottom - targetBoundingRect?.height; } else if (verticalAlign === 'bottom') { - console.log('right/bottom'); - return targetBoundingRect?.bottom + targetBoundingRect?.height; + return targetBoundingRect?.bottom; } - // return side === 'left' - // ? targetBoundingRect?.top - // ? targetBoundingRect.top - contextMenuHeight - // : 0 - // : targetBoundingRect?.top - // ? targetBoundingRect.top + targetBoundingRect.height - // : 0; } } function setHorizontalAlign(targetBoundingRect: DOMRect) { - const correction = 2; - // Context Menu should appear above or below the target element + const padding = 2; + if (['top', 'bottom'].includes(side)) { return horizontalAlign === 'left' ? targetBoundingRect?.left - : targetBoundingRect?.left + targetBoundingRect.width - contextMenuWidth - correction; + : targetBoundingRect?.left + targetBoundingRect.width - contextMenuWidth - padding; } else if (['left', 'right'].includes(side)) { - console.log('targetBoundingRect', targetBoundingRect); - // Context Menu should appear left or right of the target element - return verticalAlign === 'top' - ? targetBoundingRect?.left - : targetBoundingRect?.left + targetBoundingRect.width + correction; + if (side === 'left') { + return targetBoundingRect?.x - contextMenuWidth - padding * 2; + } else { + return targetBoundingRect?.right + padding; + } } } @@ -89,26 +89,9 @@ } } - function setAlignByMouse( - e?: MouseEvent, - contextMenuWidth: number = 0, - contextMenuHeight: number = 0 - ) { + function setAlignByMouse(e?: MouseEvent) { if (!e) return; - let newMenuPosition = { x: e.clientX, y: e.clientY }; - const menuWindowEdgesOffset = 20; - - // Adjust menu position to stay within the window - if (newMenuPosition.x + contextMenuWidth > window.innerWidth) { - newMenuPosition.x = window.innerWidth - contextMenuWidth - menuWindowEdgesOffset; - } - if (newMenuPosition.x < 0) newMenuPosition.x = 0; - if (newMenuPosition.y + contextMenuHeight > window.innerHeight) { - newMenuPosition.y = window.innerHeight - contextMenuHeight - menuWindowEdgesOffset; - } - if (newMenuPosition.y < 0) newMenuPosition.y = 0; - - menuPosition = newMenuPosition; + menuPosition = { x: e.clientX, y: e.clientY }; } function setAlignByTarget(target: HTMLElement) { @@ -117,25 +100,6 @@ x: setHorizontalAlign(targetBoundingRect), y: setVerticalAlign(targetBoundingRect) }; - console.log('newMenuPosition', newMenuPosition); - - // Adjust alignment to stay within the window - if (newMenuPosition.x + contextMenuWidth > window.innerWidth) { - horizontalAlign = horizontalAlign === 'right' ? 'left' : 'right'; - newMenuPosition.x = setHorizontalAlign(targetBoundingRect); - } - if (newMenuPosition.x < 0) { - horizontalAlign = 'right'; - newMenuPosition.x = setHorizontalAlign(targetBoundingRect); - } - if (newMenuPosition.y + contextMenuHeight > window.innerHeight) { - side = side === 'bottom' ? 'top' : 'bottom'; - newMenuPosition.y = setVerticalAlign(targetBoundingRect); - } - if (newMenuPosition.y < 0) { - side = 'bottom'; - newMenuPosition.y = setVerticalAlign(targetBoundingRect); - } menuPosition = newMenuPosition; } @@ -166,20 +130,53 @@ } } + function setAlignment() { + if (savedMouseEvent && rightClickTrigger) { + setAlignByMouse(savedMouseEvent); + } else if (leftClickTrigger) { + setAlignByTarget(leftClickTrigger); + } + } + $effect(() => { - if (isVisible && contextMenuHeight > 0 && contextMenuWidth > 0) { - menuContainer?.focus(); + if (!isVisible || !menuContainer) return; + + setAlignment(); - if (savedMouseEvent && rightClickTrigger) { - setAlignByMouse(savedMouseEvent, contextMenuWidth, contextMenuHeight); - } else if (leftClickTrigger) { - setAlignByTarget(leftClickTrigger); + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (!entry.isIntersecting) { + const rect = entry.boundingClientRect; + const viewport = entry.rootBounds; + + if (rect.right > viewport?.right) { + horizontalAlign = 'right'; + setAlignment(); + } + if (rect.left < viewport.left) { + horizontalAlign = 'left'; + setAlignment(); + } + if (rect.bottom > viewport.bottom) { + side = 'top'; + setAlignment(); + } + if (rect.top < viewport.top) { + side = 'bottom'; + setAlignment(); + } + } + }, + { + root: null, + rootMargin: '0px', + threshold: 1.0 } - } + ); - if (!isVisible) { - savedMouseEvent = undefined; - } + observer.observe(menuContainer); + return () => observer.disconnect(); }); function setTransformOrigin() { From a255f33b8e7231ada69863681c5d168c96507c42 Mon Sep 17 00:00:00 2001 From: ndom91 Date: Mon, 27 Jan 2025 18:44:21 +0100 Subject: [PATCH 4/7] fix: ChromeSidebar contextMenu btn implementations --- apps/desktop/src/components/ChromeSidebar.svelte | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/components/ChromeSidebar.svelte b/apps/desktop/src/components/ChromeSidebar.svelte index 5657e38a00..cb2b499e78 100644 --- a/apps/desktop/src/components/ChromeSidebar.svelte +++ b/apps/desktop/src/components/ChromeSidebar.svelte @@ -4,6 +4,7 @@ import { Project } from '$lib/project/project'; import { DesktopRoutesService } from '$lib/routes/routes.svelte'; import { User } from '$lib/user/user'; + import { UserService } from '$lib/user/userService'; import { getContextStore } from '@gitbutler/shared/context'; import { getContext } from '@gitbutler/shared/context'; import Button from '@gitbutler/ui/Button.svelte'; @@ -22,6 +23,8 @@ let contextMenuEl = $state(); let shareIssueModal = $state(); let keyboardShortcutsModal = $state(); + + const userService = getContext(UserService);