FloatingMobileToolbar.tsx133 lines · main
1import { useParams, useViewport } from 'common'
2import { AnimatePresence } from 'framer-motion'
3import { Menu, Search, X } from 'lucide-react'
4import { useRef } from 'react'
5import { Button, cn, KeyboardShortcut } from 'ui'
6
7import { getToolbarStyle } from './FloatingMobileToolbar.utils'
8import { useFloatingToolbarDrag } from './useFloatingToolbarDrag'
9import { useFloatingToolbarNavSize } from './useFloatingToolbarNavSize'
10import { useFloatingToolbarSheet } from './useFloatingToolbarSheet'
11import { useFloatingToolbarSidebarClick } from './useFloatingToolbarSidebarClick'
12import { AdvisorButton } from '@/components/layouts/AppLayout/AdvisorButton'
13import { AssistantButton } from '@/components/layouts/AppLayout/AssistantButton'
14import { InlineEditorButton } from '@/components/layouts/AppLayout/InlineEditorButton'
15import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
16import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
17import { HelpButton } from '@/components/ui/HelpPanel/HelpButton'
18
19export const FloatingMobileToolbar = ({ hideMobileMenu }: { hideMobileMenu?: boolean }) => {
20 const navRef = useRef<HTMLElement | null>(null)
21 const sheet = useFloatingToolbarSheet(hideMobileMenu)
22 const drag = useFloatingToolbarDrag(navRef)
23 const handleNavClickCapture = useFloatingToolbarSidebarClick()
24 const navSize = useFloatingToolbarNavSize(navRef, sheet.isSheetOpen)
25 const viewport = useViewport()
26 const { handleSearchClick, isSearchOpen } = sheet
27
28 const style = getToolbarStyle({
29 position: drag.position,
30 navSize,
31 isSheetOpen: sheet.isSheetOpen,
32 viewport,
33 isDragging: drag.dragStartRef.current !== null,
34 })
35
36 const { ref: projectRef } = useParams()
37
38 return (
39 <nav
40 ref={navRef}
41 aria-label="Floating toolbar"
42 className={cn(
43 'flex pointer-events-auto cursor-grab active:cursor-grabbing flex-row items-centerw-auto',
44 'gap-2',
45 'fixed md:hidden'
46 )}
47 style={style}
48 onClickCapture={handleNavClickCapture}
49 onPointerDown={drag.handlePointerDown}
50 onPointerMove={drag.handlePointerMove}
51 >
52 <div
53 className={cn(
54 'flex pointer-events-auto cursor-grab active:cursor-grabbing flex-row items-center justify-between w-auto rounded-full',
55 'bg-overlay/80 backdrop-blur-md px-2.5 py-2 gap-2',
56 'border border-strong shadow-[0px_3px_6px_-2px_rgba(0,0,0,0.07),0px_10px_30px_0px_rgba(0,0,0,0.10)]'
57 )}
58 >
59 <AnimatePresence initial={false}>
60 <ButtonTooltip
61 key="search"
62 type={isSearchOpen ? 'secondary' : 'outline'}
63 size="tiny"
64 id="search-trigger"
65 className={cn(
66 'rounded-full w-[32px] h-[32px] flex items-center justify-center p-0',
67 isSearchOpen && 'text-background'
68 )}
69 tooltip={{
70 content: {
71 className: 'p-1 pl-2.5',
72 text: (
73 <div className="flex items-center gap-2.5">
74 <span>Search</span>
75 <KeyboardShortcut keys={['Meta', 'K']} />
76 </div>
77 ),
78 },
79 }}
80 onClick={handleSearchClick}
81 >
82 <Search size={16} strokeWidth={1} />
83 </ButtonTooltip>
84 <span key="help" data-sidebar-id={SIDEBAR_KEYS.HELP_PANEL}>
85 <HelpButton />
86 </span>
87 <span key="advisor" data-sidebar-id={SIDEBAR_KEYS.ADVISOR_PANEL}>
88 <AdvisorButton projectRef={projectRef} />
89 </span>
90 {!!projectRef && (
91 <>
92 <span key="editor" data-sidebar-id={SIDEBAR_KEYS.EDITOR_PANEL}>
93 <InlineEditorButton />
94 </span>
95 <span key="assistant" data-sidebar-id={SIDEBAR_KEYS.AI_ASSISTANT}>
96 <AssistantButton />
97 </span>
98 </>
99 )}
100 {sheet.showMenuButton && sheet.isSheetOpen && (
101 <Button
102 key="menu"
103 title="Menu dropdown button"
104 type={sheet.isMenuOpen ? 'secondary' : 'default'}
105 className={cn(
106 'flex lg:hidden mr-1 rounded-md min-w-[30px] w-[30px] h-[30px] data-open:bg-overlay-hover/30',
107 !sheet.isMenuOpen && 'bg-surface-300!'
108 )}
109 icon={<Menu />}
110 onClick={sheet.handleMenuClick}
111 />
112 )}
113 </AnimatePresence>
114 </div>
115 <AnimatePresence initial={false}>
116 <Button
117 title="close"
118 type="text"
119 className={cn(
120 'flex flex-row items-center justify-center rounded-full',
121 'bg-overlay/50 backdrop-blur-md my-auto p-1! gap-2',
122 'border border-strong shadow-[0px_3px_6px_-2px_rgba(0,0,0,0.07),0px_10px_30px_0px_rgba(0,0,0,0.10)]',
123 'w-10! h-10! min-w-10! min-h-10!',
124 'rounded-full',
125 !sheet.isSheetOpen && 'hidden'
126 )}
127 icon={<X />}
128 onClick={sheet.handleClose}
129 />
130 </AnimatePresence>
131 </nav>
132 )
133}