Skip to content

Commit 79e79d6

Browse files
committed
adjust sidebar transitions
1 parent a5131aa commit 79e79d6

5 files changed

Lines changed: 74 additions & 65 deletions

File tree

svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@invopop/popui",
33
"license": "MIT",
4-
"version": "0.1.100",
4+
"version": "0.1.101",
55
"repository": {
66
"url": "https://github.com/invopop/popui"
77
},

svelte/src/lib/sidebar/constants.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ export const SIDEBAR_WIDTH_STORAGE_KEY = 'sidebar_width'
33
export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
44
export const SIDEBAR_WIDTH = '16rem'
55
export const SIDEBAR_WIDTH_MOBILE = '18rem'
6-
export const SIDEBAR_WIDTH_ICON = '3rem'
6+
export const SIDEBAR_WIDTH_ICON = '3.5rem'
7+
export const SIDEBAR_WIDTH_ICON_PX = 56
78
export const SIDEBAR_KEYBOARD_SHORTCUT = '.'
8-
export const SIDEBAR_MIN_WIDTH_PX = 240
9+
export const SIDEBAR_MIN_WIDTH_PX = 180
910
export const SIDEBAR_MAX_WIDTH_PX = 384
1011
export const SIDEBAR_DRAG_THRESHOLD_PX = 4

svelte/src/lib/sidebar/sidebar-rail.svelte

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import ShortcutWrapper from '$lib/ShortcutWrapper.svelte'
55
import TooltipContent from '$lib/tooltip/tooltip-content.svelte'
66
import { cn, type WithElementRef } from '$lib/utils.js'
7-
import { SIDEBAR_DRAG_THRESHOLD_PX, SIDEBAR_MIN_WIDTH_PX } from './constants.js'
7+
import { SIDEBAR_DRAG_THRESHOLD_PX, SIDEBAR_WIDTH_ICON_PX } from './constants.js'
88
import { useSidebar } from './context.svelte.js'
99
1010
let {
@@ -20,6 +20,7 @@
2020
let dragStartWidthPx = 0
2121
let dragMoved = false
2222
let dragDirection: 1 | -1 = 1
23+
let activePointerId: number | null = null
2324
2425
let isDragging = $state(false)
2526
let tooltipOpen = $state(false)
@@ -29,6 +30,8 @@
2930
const TOOLTIP_HOVER_DELAY_MS = 700
3031
const TOOLTIP_CURSOR_OFFSET = 12
3132
33+
let tooltipDelay = $derived(isDragging ? Number.MAX_SAFE_INTEGER : TOOLTIP_HOVER_DELAY_MS)
34+
3235
let cursorAnchor = $derived.by(() => {
3336
const x = cursorX
3437
const y = cursorY
@@ -37,22 +40,43 @@
3740
}
3841
})
3942
43+
const POST_DRAG_CLICK_GUARD_MS = 250
44+
let dragEndTime = 0
45+
4046
function onPointerEnter(e: PointerEvent) {
4147
const sidebarRoot = (e.currentTarget as HTMLElement).closest('[data-slot="sidebar"]')
4248
dragDirection = sidebarRoot?.getAttribute('data-side') === 'right' ? -1 : 1
4349
cursorX = e.clientX
4450
cursorY = e.clientY
4551
}
4652
47-
const COLLAPSE_DRAG_OVERSHOOT_PX = 100
48-
const POST_DRAG_CLICK_GUARD_MS = 250
49-
let dragEndTime = 0
53+
function onPointerMoveOnTrigger(e: PointerEvent) {
54+
if (activePointerId !== null) return
55+
cursorX = e.clientX
56+
cursorY = e.clientY
57+
}
58+
59+
function endDrag() {
60+
if (activePointerId !== null) {
61+
window.removeEventListener('pointermove', onWindowPointerMove)
62+
window.removeEventListener('pointerup', onWindowPointerUp)
63+
window.removeEventListener('pointercancel', onWindowPointerUp)
64+
activePointerId = null
65+
}
66+
document.body.style.cursor = ''
67+
document.body.style.userSelect = ''
68+
isDragging = false
69+
sidebar.isResizing = false
70+
if (dragMoved) {
71+
dragEndTime = Date.now()
72+
dragMoved = false
73+
}
74+
}
5075
51-
function onPointerMove(e: PointerEvent) {
76+
function onWindowPointerMove(e: PointerEvent) {
77+
if (e.pointerId !== activePointerId) return
5278
cursorX = e.clientX
5379
cursorY = e.clientY
54-
const button = e.currentTarget as HTMLButtonElement
55-
if (!button.hasPointerCapture(e.pointerId)) return
5680
const delta = (e.clientX - dragStartX) * dragDirection
5781
if (Math.abs(delta) > SIDEBAR_DRAG_THRESHOLD_PX) {
5882
dragMoved = true
@@ -63,62 +87,46 @@
6387
if (!dragMoved) return
6488
if (sidebar.state === 'collapsed') {
6589
if (delta > 0) {
66-
button.releasePointerCapture(e.pointerId)
67-
document.body.style.cursor = ''
68-
document.body.style.userSelect = ''
69-
isDragging = false
70-
sidebar.isResizing = false
71-
dragMoved = false
72-
dragEndTime = Date.now()
90+
endDrag()
7391
sidebar.setOpen(true)
7492
}
7593
return
7694
}
7795
const targetWidth = dragStartWidthPx + delta
78-
if (targetWidth < SIDEBAR_MIN_WIDTH_PX - COLLAPSE_DRAG_OVERSHOOT_PX) {
79-
button.releasePointerCapture(e.pointerId)
80-
document.body.style.cursor = ''
81-
document.body.style.userSelect = ''
82-
isDragging = false
83-
sidebar.isResizing = false
84-
dragMoved = false
85-
dragEndTime = Date.now()
96+
if (targetWidth < SIDEBAR_WIDTH_ICON_PX) {
97+
endDrag()
8698
sidebar.resetWidth()
8799
sidebar.setOpen(false)
88100
return
89101
}
90102
sidebar.setWidth(targetWidth)
91103
}
92104
105+
function onWindowPointerUp(e: PointerEvent) {
106+
if (e.pointerId !== activePointerId) return
107+
endDrag()
108+
}
109+
93110
function onPointerDown(e: PointerEvent) {
94111
if (sidebar.isMobile) return
95-
const button = e.currentTarget as HTMLButtonElement
96-
const sidebarRoot = button.closest('[data-slot="sidebar"]')
112+
if (e.button !== 0) return
113+
const target = e.currentTarget as HTMLElement
114+
const sidebarRoot = target.closest('[data-slot="sidebar"]')
97115
dragDirection = sidebarRoot?.getAttribute('data-side') === 'right' ? -1 : 1
98116
99117
const container = sidebarRoot?.querySelector('[data-slot="sidebar-container"]')
100118
dragStartWidthPx = container instanceof HTMLElement ? container.offsetWidth : 256
101119
dragStartX = e.clientX
102120
dragMoved = false
103-
button.setPointerCapture(e.pointerId)
121+
activePointerId = e.pointerId
104122
document.body.style.cursor = 'col-resize'
105123
document.body.style.userSelect = 'none'
124+
window.addEventListener('pointermove', onWindowPointerMove)
125+
window.addEventListener('pointerup', onWindowPointerUp)
126+
window.addEventListener('pointercancel', onWindowPointerUp)
106127
}
107128
108-
function onPointerUp(e: PointerEvent) {
109-
const button = e.currentTarget as HTMLButtonElement
110-
if (button.hasPointerCapture(e.pointerId)) button.releasePointerCapture(e.pointerId)
111-
document.body.style.cursor = ''
112-
document.body.style.userSelect = ''
113-
isDragging = false
114-
sidebar.isResizing = false
115-
if (dragMoved) {
116-
dragEndTime = Date.now()
117-
dragMoved = false
118-
}
119-
}
120-
121-
const DOUBLE_CLICK_DELAY_MS = 300
129+
const DOUBLE_CLICK_DELAY_MS = 150
122130
let pendingClickTimer: ReturnType<typeof setTimeout> | undefined
123131
124132
function onClick(e: MouseEvent) {
@@ -145,10 +153,10 @@
145153

146154
<TooltipPrimitive.Root
147155
bind:open={tooltipOpen}
148-
delayDuration={TOOLTIP_HOVER_DELAY_MS}
156+
delayDuration={tooltipDelay}
149157
disableHoverableContent
150158
>
151-
<TooltipPrimitive.Trigger disabled={isDragging}>
159+
<TooltipPrimitive.Trigger>
152160
{#snippet child({ props })}
153161
{@const buttonProps = props as HTMLButtonAttributes}
154162
<button
@@ -160,17 +168,12 @@
160168
}}
161169
onpointermove={(e) => {
162170
buttonProps.onpointermove?.(e)
163-
onPointerMove(e)
171+
onPointerMoveOnTrigger(e)
164172
}}
165173
onpointerdown={(e) => {
166174
buttonProps.onpointerdown?.(e)
167175
onPointerDown(e)
168176
}}
169-
onpointerup={(e) => {
170-
buttonProps.onpointerup?.(e)
171-
onPointerUp(e)
172-
}}
173-
onpointercancel={onPointerUp}
174177
onclick={(e) => {
175178
buttonProps.onclick?.(e)
176179
onClick(e)
@@ -203,19 +206,24 @@
203206
side="bottom"
204207
align="center"
205208
sideOffset={TOOLTIP_CURSOR_OFFSET}
206-
class="px-3 py-2"
207209
>
208-
<div class="flex flex-col gap-1.5">
210+
<div class="flex flex-col gap-1">
209211
{#if sidebar.state === 'expanded'}
210-
<div>Drag to resize</div>
212+
<div class="flex w-full items-center justify-between gap-3">
213+
<span>Drag to resize</span>
214+
<div class="flex items-center gap-0.5 opacity-0">
215+
<ShortcutWrapper size="sm" theme="navigation">⌘</ShortcutWrapper>
216+
<ShortcutWrapper size="sm" theme="navigation">.</ShortcutWrapper>
217+
</div>
218+
</div>
211219
{/if}
212-
<div class="flex items-center justify-between gap-3">
220+
<div class="flex w-full items-center justify-between gap-3">
213221
<span>
214222
{sidebar.state === 'expanded' ? 'Click to collapse' : 'Click to expand'}
215223
</span>
216-
<div class="flex items-center gap-1">
217-
<ShortcutWrapper size="md" theme="navigation">⌘</ShortcutWrapper>
218-
<ShortcutWrapper size="md" theme="navigation">.</ShortcutWrapper>
224+
<div class="flex items-center gap-0.5">
225+
<ShortcutWrapper size="sm" theme="navigation">⌘</ShortcutWrapper>
226+
<ShortcutWrapper size="sm" theme="navigation">.</ShortcutWrapper>
219227
</div>
220228
</div>
221229
</div>

svelte/src/lib/sidebar/sidebar.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
<div
7474
data-slot="sidebar-gap"
7575
class={cn(
76-
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear group-data-[resizing=true]:transition-none',
76+
'relative w-(--sidebar-width) bg-transparent transition-[width] duration-150 ease-linear group-data-[resizing=true]:transition-none',
7777
'group-data-[collapsible=offcanvas]:w-0',
7878
'group-data-[side=right]:rotate-180',
7979
variant === 'floating' || variant === 'inset'
@@ -84,7 +84,7 @@
8484
<div
8585
data-slot="sidebar-container"
8686
class={cn(
87-
'fixed inset-y-0 z-50 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear group-data-[resizing=true]:transition-none md:flex',
87+
'fixed inset-y-0 z-50 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-150 ease-linear group-data-[resizing=true]:transition-none md:flex',
8888
side === 'left'
8989
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
9090
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',

svelte/src/lib/tooltip/tooltip-content.svelte

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
{side}
2525
{...rest}
2626
class={cn(
27-
'bg-background-default-negative border border-border-inverse z-[1002] rounded-md px-2 py-1 text-sm font-medium text-foreground-inverse leading-5 tracking-tight shadow-md',
27+
'bg-background-default-negative border border-border-inverse z-1002 rounded-md px-2 py-1 text-base font-medium text-foreground-inverse shadow-md',
2828
className
2929
)}
3030
>
@@ -34,11 +34,11 @@
3434
{#snippet child({ props })}
3535
<div
3636
class={cn(
37-
'bg-background-default-negative z-[1002] size-2.5 rotate-45 rounded-[2px]',
38-
'data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%_+_2px)]',
39-
'data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%_+_1px)]',
40-
'data-[side=right]:translate-x-[calc(50%_+_2px)] data-[side=right]:translate-y-1/2',
41-
'data-[side=left]:-translate-y-[calc(50%_-_3px)]',
37+
'bg-background-default-negative z-1002 size-2.5 rotate-45 rounded-xs',
38+
'data-[side=top]:translate-x-1/2 data-[side=top]:translate-y-[calc(-50%+2px)]',
39+
'data-[side=bottom]:-translate-x-1/2 data-[side=bottom]:-translate-y-[calc(-50%+1px)]',
40+
'data-[side=right]:translate-x-[calc(50%+2px)] data-[side=right]:translate-y-1/2',
41+
'data-[side=left]:-translate-y-[calc(50%-3px)]',
4242
arrowClasses
4343
)}
4444
{...props}

0 commit comments

Comments
 (0)