Skip to content

Commit 2df8e35

Browse files
committed
fix(Sidebar): simplify resizable rail and fix collapsedSize handling
1 parent adcd5e8 commit 2df8e35

4 files changed

Lines changed: 66 additions & 75 deletions

File tree

src/runtime/components/Sidebar.vue

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export interface SidebarSlots {
9292
close?(props: { ui: Sidebar['ui'], state: SidebarState }): VNode[]
9393
default?(props: { state: SidebarState, open: boolean, close: () => void }): VNode[]
9494
footer?(props: { state: SidebarState, open: boolean, close: () => void }): VNode[]
95-
rail?(props: { ui: Sidebar['ui'], state: SidebarState }): VNode[]
95+
rail?(props: { ui: Sidebar['ui'], state: SidebarState, onMouseDown: (e: MouseEvent) => void, onTouchStart: (e: TouchEvent) => void, onDoubleClick: (e: MouseEvent) => void }): VNode[]
9696
content?(props: { close: () => void }): VNode[]
9797
}
9898
</script>
@@ -194,14 +194,16 @@ const canCollapse = computed(() => isResizable.value && props.collapsible !== 'n
194194
const sidebarId = `sidebar-${props.id || useId()}`
195195
const desktopCollapsed = ref(!modelOpen.value)
196196
197+
const effectiveCollapsedSize = computed(() => props.collapsedSize ?? 4)
198+
197199
const { el: containerEl, size: sidebarSize, isDragging, isCollapsed, onMouseDown: handleMouseDown, onTouchStart: handleTouchStart, onDoubleClick: handleDoubleClick, collapse } = useResizable(sidebarId, computed(() => ({
198200
side: props.side,
199201
minSize: props.minSize,
200202
maxSize: props.maxSize,
201203
defaultSize: props.defaultSize,
202204
resizable: isResizable.value,
203205
collapsible: canCollapse.value,
204-
collapsedSize: props.collapsedSize ?? Math.max(0, props.minSize - 8),
206+
collapsedSize: effectiveCollapsedSize.value,
205207
unit: 'rem' as const,
206208
persistent: true,
207209
storage: 'cookie' as const
@@ -212,27 +214,8 @@ if (!isMobile.value && canCollapse.value && isCollapsed.value) {
212214
modelOpen.value = false
213215
}
214216
215-
// Track whether mousedown resulted in a drag (to distinguish click vs drag on the rail)
216-
let didDrag = false
217-
218-
function onRailMouseDown(e: MouseEvent) {
219-
didDrag = false
220-
const startX = e.clientX
221-
const onMove = (ev: MouseEvent) => {
222-
if (Math.abs(ev.clientX - startX) > 3) didDrag = true
223-
}
224-
const onUp = () => {
225-
document.removeEventListener('mousemove', onMove)
226-
document.removeEventListener('mouseup', onUp)
227-
}
228-
document.addEventListener('mousemove', onMove)
229-
document.addEventListener('mouseup', onUp)
230-
handleMouseDown(e)
231-
}
232-
233217
function onRailClick() {
234-
if (!isResizable.value) return (open.value = !open.value)
235-
if (!didDrag && canCollapse.value) collapse(!isCollapsed.value)
218+
if (!isResizable.value) open.value = !open.value
236219
}
237220
238221
// Dynamic cursor: ew-resize (bidirectional) by default, directional at bounds
@@ -370,7 +353,7 @@ const menuProps = toRef(() => defu(props.menu, {
370353
:class="ui.root({ class: [uiProp?.root, props.class] })"
371354
:style="isResizable ? {
372355
'--sidebar-width': `${expandedWidth}rem`,
373-
...(props.collapsedSize && props.collapsible === 'icon' ? { '--sidebar-width-icon': `${props.collapsedSize}rem` } : {})
356+
...(props.collapsible === 'icon' ? { '--sidebar-width-icon': `${effectiveCollapsedSize}rem` } : {})
374357
} : undefined"
375358
>
376359
<!-- Gap spacer: reserves layout space for the fixed sidebar -->
@@ -389,16 +372,24 @@ const menuProps = toRef(() => defu(props.menu, {
389372
>
390373
<ReuseInnerTemplate />
391374

392-
<slot v-if="rail" name="rail" :state="state" :ui="ui">
375+
<slot
376+
v-if="rail"
377+
name="rail"
378+
:state="state"
379+
:ui="ui"
380+
:on-mouse-down="handleMouseDown"
381+
:on-touch-start="handleTouchStart"
382+
:on-double-click="handleDoubleClick"
383+
>
393384
<button
394385
data-slot="rail"
395386
:data-state="state"
396387
:aria-label="t('sidebar.toggle')"
397388
:tabindex="-1"
398389
:class="ui.rail({ class: uiProp?.rail })"
399390
:style="railCursor ? { cursor: railCursor } : undefined"
400-
@mousedown="isResizable ? onRailMouseDown($event) : undefined"
401-
@touchstart="isResizable && !isCollapsed ? handleTouchStart($event) : undefined"
391+
@mousedown="isResizable ? handleMouseDown($event) : undefined"
392+
@touchstart="isResizable ? handleTouchStart($event) : undefined"
402393
@dblclick="isResizable ? handleDoubleClick($event) : undefined"
403394
@click="onRailClick"
404395
/>

src/theme/sidebar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { ModuleOptions } from '../module'
22

33
export default (options: Required<ModuleOptions>) => ({
44
slots: {
5-
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
5+
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem] data-[dragging=true]:select-none',
66
gap: 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-out group-data-[dragging=true]/sidebar:!duration-0',
77
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-out lg:flex group-data-[dragging=true]/sidebar:!duration-0',
88
inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',

0 commit comments

Comments
 (0)