MobileSheetContext.tsx 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import type { PropsWithChildren, ReactNode } from 'react'
  2. import { createContext, useCallback, useContext, useRef, useState } from 'react'
  3. import type { TYPEOF_SIDEBAR_KEYS } from '../../ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
  4. /**
  5. * Sheet content: null = closed; sidebar id = one of SIDEBAR_KEYS; ReactNode = custom content (menu, search, etc.).
  6. */
  7. export type MobileSheetContentType = null | TYPEOF_SIDEBAR_KEYS | ReactNode
  8. type MobileSheetContextValue = {
  9. content: MobileSheetContentType
  10. setContent: (content: MobileSheetContentType) => void
  11. isOpen: boolean
  12. /** Open the sheet with the current menu content. Registered by ProjectLayout (project menu) or OrganizationLayout (org menu). */
  13. openMenu: () => void
  14. /** Register the callback run when openMenu() is called (e.g. from MobileNavigationBar). Returns an unregister function. */
  15. registerOpenMenu: (fn: () => void) => () => void
  16. }
  17. const MobileSheetContext = createContext<MobileSheetContextValue | null>(null)
  18. export function MobileSheetProvider({ children }: PropsWithChildren) {
  19. const [content, setContentState] = useState<MobileSheetContentType>(null)
  20. const openMenuRef = useRef<() => void>(() => {})
  21. const isOpen = content !== null
  22. const setContent = useCallback((next: MobileSheetContentType) => {
  23. setContentState(next)
  24. }, [])
  25. const openMenu = useCallback(() => {
  26. openMenuRef.current()
  27. }, [])
  28. const registerOpenMenu = useCallback((fn: () => void) => {
  29. openMenuRef.current = fn
  30. return () => {
  31. openMenuRef.current = () => {}
  32. }
  33. }, [])
  34. return (
  35. <MobileSheetContext.Provider
  36. value={{ content, setContent, isOpen, openMenu, registerOpenMenu }}
  37. >
  38. {children}
  39. </MobileSheetContext.Provider>
  40. )
  41. }
  42. export function useMobileSheet(): MobileSheetContextValue {
  43. const ctx = useContext(MobileSheetContext)
  44. if (!ctx) {
  45. throw new Error('useMobileSheet must be used within MobileSheetProvider')
  46. }
  47. return ctx
  48. }