mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-04 23:06:57 -05:00
Add open / close mobile sidebar on left / right swipe
This commit is contained in:
@@ -19,7 +19,7 @@ export const xLink = "https://x.com/compassmeet"
|
||||
export const formLink = "https://forms.gle/tKnXUMAbEreMK6FC6"
|
||||
export const ANDROID_APP_URL = 'https://play.google.com/store/apps/details?id=com.compassconnections.app'
|
||||
|
||||
export const IS_MAINTENANCE = false // set to true to enable the maintenance mode banner
|
||||
export const IS_MAINTENANCE = true // set to true to enable the maintenance mode banner
|
||||
|
||||
export const MIN_BIO_LENGTH = 250
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
import {MenuAlt3Icon} from '@heroicons/react/solid'
|
||||
import {Dialog, Transition} from '@headlessui/react'
|
||||
import {Fragment, useState} from 'react'
|
||||
import {Fragment, useState, useRef, useEffect} from 'react'
|
||||
import {useRouter} from 'next/router'
|
||||
import Sidebar from './sidebar'
|
||||
import {Item} from './sidebar-item'
|
||||
@@ -13,6 +13,7 @@ import {trackCallback} from 'web/lib/service/analytics'
|
||||
import {User} from 'common/user'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {useProfile} from 'web/hooks/use-profile'
|
||||
import {useIsMobile} from "web/hooks/use-is-mobile"
|
||||
|
||||
const itemClass =
|
||||
'sm:hover:bg-ink-200 block w-full py-1 px-3 text-center sm:hover:text-primary-700 transition-colors'
|
||||
@@ -38,37 +39,37 @@ export function BottomNavBar(props: {
|
||||
}
|
||||
|
||||
return (<Col>
|
||||
<nav
|
||||
className={clsx(
|
||||
"border-ink-200 dark:border-ink-300 text-ink-700 bg-canvas-50 fixed inset-x-0 bottom-0 z-50 flex select-none items-center justify-between border-t-2 text-xs lg:hidden sidebar-nav",
|
||||
'safe-bottom',
|
||||
)}
|
||||
>
|
||||
{navigationOptions.map((item) => (
|
||||
<NavBarItem
|
||||
key={item.name}
|
||||
item={item}
|
||||
currentPage={currentPage}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
<nav
|
||||
className={clsx(
|
||||
itemClass,
|
||||
'relative',
|
||||
sidebarOpen ? selectedItemClass : ''
|
||||
"border-ink-200 dark:border-ink-300 text-ink-700 bg-canvas-50 fixed inset-x-0 bottom-0 z-50 flex select-none items-center justify-between border-t-2 text-xs lg:hidden sidebar-nav",
|
||||
'safe-bottom',
|
||||
)}
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
>
|
||||
<MenuAlt3Icon className="mx-auto my-1 h-6 w-6" aria-hidden="true"/>
|
||||
More
|
||||
</div>
|
||||
<MobileSidebar
|
||||
sidebarOpen={sidebarOpen}
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
sidebarNavigationOptions={sidebarNavigationOptions}
|
||||
/>
|
||||
</nav>
|
||||
{navigationOptions.map((item) => (
|
||||
<NavBarItem
|
||||
key={item.name}
|
||||
item={item}
|
||||
currentPage={currentPage}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className={clsx(
|
||||
itemClass,
|
||||
'relative',
|
||||
sidebarOpen ? selectedItemClass : ''
|
||||
)}
|
||||
onClick={() => setSidebarOpen(true)}
|
||||
>
|
||||
<MenuAlt3Icon className="mx-auto my-1 h-6 w-6" aria-hidden="true"/>
|
||||
More
|
||||
</div>
|
||||
<MobileSidebar
|
||||
sidebarOpen={sidebarOpen}
|
||||
setSidebarOpen={setSidebarOpen}
|
||||
sidebarNavigationOptions={sidebarNavigationOptions}
|
||||
/>
|
||||
</nav>
|
||||
|
||||
<div
|
||||
className="fixed inset-x-0 bg-canvas-50"
|
||||
@@ -187,6 +188,73 @@ export function MobileSidebar(props: {
|
||||
sidebarNavigationOptions: Item[]
|
||||
}) {
|
||||
const {sidebarOpen, sidebarNavigationOptions, setSidebarOpen} = props
|
||||
|
||||
// Touch gesture handlers to open/close the mobile sidebar on any page
|
||||
const touchStartX = useRef<number | null>(null)
|
||||
const touchStartY = useRef<number | null>(null)
|
||||
const gestureHandled = useRef(false)
|
||||
const isMobile = useIsMobile(1024) // for lg threshold, like in BottomNavBar vs Sidebar
|
||||
|
||||
const HORIZONTAL_THRESHOLD = 50 // px required to count as swipe
|
||||
// const EDGE_START_MAX = 30 // px from left edge to allow open gesture
|
||||
|
||||
// Prefer global pointer events so gestures work even if a child intercepts touches
|
||||
useEffect(() => {
|
||||
if (!isMobile) return
|
||||
|
||||
const onPointerDown = (e: PointerEvent) => {
|
||||
// console.log("onPointerDown")
|
||||
if (!['touch', 'mouse'].includes(e.pointerType)) return
|
||||
touchStartX.current = e.clientX
|
||||
touchStartY.current = e.clientY
|
||||
gestureHandled.current = false
|
||||
}
|
||||
|
||||
const onPointerMove = (e: PointerEvent) => {
|
||||
// console.log("onPointerMove")
|
||||
if (!['touch', 'mouse'].includes(e.pointerType)) return
|
||||
if (gestureHandled.current) return
|
||||
if (touchStartX.current == null) return
|
||||
const deltaX = e.clientX - touchStartX.current
|
||||
const deltaY = e.clientY - (touchStartY.current ?? 0)
|
||||
|
||||
// Ignore primarily vertical gestures
|
||||
if (Math.abs(deltaY) > Math.abs(deltaX)) return
|
||||
|
||||
if (!sidebarOpen) {
|
||||
// console.log("checking opening")
|
||||
// Open gesture: swipe right starting from the very left edge
|
||||
if (deltaX > HORIZONTAL_THRESHOLD) {
|
||||
gestureHandled.current = true
|
||||
setSidebarOpen(true)
|
||||
}
|
||||
} else {
|
||||
// Close gesture: swipe left anywhere
|
||||
if (deltaX < -HORIZONTAL_THRESHOLD) {
|
||||
gestureHandled.current = true
|
||||
setSidebarOpen(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onPointerUp = (_e: PointerEvent) => {
|
||||
// console.log("onPointerUp")
|
||||
touchStartX.current = null
|
||||
touchStartY.current = null
|
||||
gestureHandled.current = false
|
||||
}
|
||||
|
||||
window.addEventListener('pointerdown', onPointerDown, {passive: true})
|
||||
window.addEventListener('pointermove', onPointerMove, {passive: true})
|
||||
window.addEventListener('pointerup', onPointerUp, {passive: true})
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('pointerdown', onPointerDown)
|
||||
window.removeEventListener('pointermove', onPointerMove)
|
||||
window.removeEventListener('pointerup', onPointerUp)
|
||||
}
|
||||
}, [isMobile, sidebarOpen])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition.Root show={sidebarOpen} as={Fragment}>
|
||||
|
||||
Reference in New Issue
Block a user