| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import type { CSSProperties } from 'react'
- export const DRAG_THRESHOLD_PX = 8
- export const GAP_FROM_BOTTOM = 50
- export const SHEET_OPEN_GAP_FRACTION = 0.15
- export const DEFAULT_NAV_HEIGHT = 56
- export function isMenuContent(content: unknown): boolean {
- return content !== null && typeof content !== 'string'
- }
- export function shouldShowMenuButton(pathname: string): boolean {
- return (
- pathname.startsWith('/project/') ||
- pathname.startsWith('/org/') ||
- pathname.startsWith('/account')
- )
- }
- export type Viewport = { width: number; height: number }
- export type NavSize = { width: number; height: number }
- export type Position = { x: number; y: number }
- export function clampPosition(
- current: Position,
- delta: { dx: number; dy: number },
- viewport: Viewport,
- navSize: NavSize
- ): Position {
- const maxX = Math.max(0, viewport.width - navSize.width)
- const maxY = Math.max(0, viewport.height - navSize.height)
- return {
- x: Math.max(0, Math.min(maxX, current.x + delta.dx)),
- y: Math.max(0, Math.min(maxY, current.y + delta.dy)),
- }
- }
- export type DragStartState = {
- x: number
- y: number
- startX: number
- startY: number
- }
- export function getNextPosition(
- dragStart: DragStartState,
- clientX: number,
- clientY: number,
- viewport: Viewport,
- navSize: NavSize,
- threshold: number = DRAG_THRESHOLD_PX
- ): Position | null {
- const dist = Math.hypot(clientX - dragStart.startX, clientY - dragStart.startY)
- if (dist < threshold) return null
- const dx = clientX - dragStart.startX
- const dy = clientY - dragStart.startY
- return clampPosition({ x: dragStart.x, y: dragStart.y }, { dx, dy }, viewport, navSize)
- }
- export function getToolbarStyle(params: {
- position: Position | null
- navSize: NavSize
- isSheetOpen: boolean
- viewport: Viewport
- isDragging: boolean
- }): CSSProperties {
- const { position, navSize, isSheetOpen, viewport, isDragging } = params
- const { width: navW, height: navH } = navSize
- const vw = viewport.width
- const vh = viewport.height
- const transition = isDragging
- ? 'transform 0ms, z-index 0s'
- : 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), z-index 0s'
- const base: CSSProperties = {
- zIndex: isSheetOpen ? 101 : 41,
- transition,
- touchAction: 'none',
- }
- const centerX = vw > 0 && navW > 0 ? vw / 2 - navW / 2 : 0
- const sheetOpenGapPx = vh * SHEET_OPEN_GAP_FRACTION
- const topWhenSheetOpen = vh > 0 ? sheetOpenGapPx / 2 - navH / 2 : 0
- const defaultYClosed = vh > 0 ? vh - GAP_FROM_BOTTOM - (navH > 0 ? navH : DEFAULT_NAV_HEIGHT) : 0
- if (position === null) {
- return {
- ...base,
- left: '50%',
- top: 0,
- transform: isSheetOpen
- ? `translate(-50%, ${topWhenSheetOpen}px)`
- : `translate(-50%, ${defaultYClosed}px)`,
- }
- }
- if (isSheetOpen) {
- return {
- ...base,
- left: 0,
- top: 0,
- transform: `translate(${centerX}px, ${topWhenSheetOpen}px)`,
- }
- }
- return {
- ...base,
- left: 0,
- top: 0,
- transform: `translate(${position.x}px, ${position.y}px)`,
- }
- }
|