Remove sidebar menus in favor of lil' popovers (#3046)
* Convert user menu to a popover from a sidebar * Move the user menu over to the left menu cluster * Replace project sidebar with popover-style menu * Styling tweaks, give export button a proper tooltip when disabled * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Filter orphan breaks, tweak space to remove mouse gaps * Unify with and without avatar image code * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Rerun CI * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Rerun CI * Prepare to move UserSidebarMenu over to right * Revert AppHeader tweaks * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Rerun CI * Fix typo in README * Fix export E2E tests that relied on button text * Missed the data-testid we used to have on the data-testid we had on the settings button * Dang I missed another testId * Update snapshots * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) * Rerun CI --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { ActionButton, ActionButtonProps } from './ActionButton'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { isTauri } from '../lib/isTauri'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Fragment } from 'react'
|
||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
import { sep } from '@tauri-apps/api/path'
|
||||
import { Logo } from './Logo'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
@ -12,6 +12,9 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { CustomIcon } from './CustomIcon'
|
||||
import { useLspContext } from './LspProvider'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import usePlatform from 'hooks/usePlatform'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import Tooltip from './Tooltip'
|
||||
|
||||
const ProjectSidebarMenu = ({
|
||||
project,
|
||||
@ -80,6 +83,10 @@ function ProjectMenuPopover({
|
||||
project?: IndexLoaderData['project']
|
||||
file?: IndexLoaderData['file']
|
||||
}) {
|
||||
const platform = usePlatform()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const { onProjectClose } = useLspContext()
|
||||
const exportCommandInfo = { name: 'Export', groupId: 'modeling' }
|
||||
@ -90,13 +97,82 @@ function ProjectMenuPopover({
|
||||
)
|
||||
)
|
||||
|
||||
// We filter this memoized list so that no orphan "break" elements are rendered.
|
||||
const projectMenuItems = useMemo<(ActionButtonProps | 'break')[]>(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
id: 'settings',
|
||||
Element: 'button',
|
||||
children: (
|
||||
<>
|
||||
<span className="flex-1">Project settings</span>
|
||||
<kbd className="hotkey">{`${platform === 'macos' ? '⌘' : 'Ctrl'}${
|
||||
isTauri() ? '' : '⬆'
|
||||
},`}</kbd>
|
||||
</>
|
||||
),
|
||||
onClick: () => {
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath + '?tab=project')
|
||||
},
|
||||
},
|
||||
'break',
|
||||
{
|
||||
id: 'export',
|
||||
Element: 'button',
|
||||
children: (
|
||||
<>
|
||||
<span>Export current part</span>
|
||||
{!findCommand(exportCommandInfo) && (
|
||||
<Tooltip position="right" className="!max-w-none min-w-fit">
|
||||
Awaiting engine connection
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
),
|
||||
disabled: !findCommand(exportCommandInfo),
|
||||
onClick: () =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: exportCommandInfo,
|
||||
}),
|
||||
},
|
||||
'break',
|
||||
{
|
||||
id: 'go-home',
|
||||
Element: 'button',
|
||||
children: 'Go to Home',
|
||||
className: !isTauri() ? 'hidden' : '',
|
||||
onClick: () => {
|
||||
onProjectClose(file || null, project?.path || null, true)
|
||||
// Clear the scene and end the session.
|
||||
engineCommandManager.endSession()
|
||||
},
|
||||
},
|
||||
].filter(
|
||||
(props) =>
|
||||
props === 'break' ||
|
||||
(typeof props !== 'string' && !props.className?.includes('hidden'))
|
||||
) as (ActionButtonProps | 'break')[],
|
||||
[
|
||||
platform,
|
||||
findCommand,
|
||||
commandBarSend,
|
||||
engineCommandManager,
|
||||
onProjectClose,
|
||||
isTauri,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
<Popover.Button
|
||||
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 pl-0 pr-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary dark:hover:bg-chalkboard-90"
|
||||
className="gap-1 rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary dark:hover:bg-chalkboard-90"
|
||||
data-testid="project-sidebar-toggle"
|
||||
>
|
||||
<CustomIcon name="three-dots" className="w-5 h-5 rotate-90" />
|
||||
<div className="flex flex-col items-start py-0.5">
|
||||
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
|
||||
{isTauri() && file?.name
|
||||
@ -109,68 +185,53 @@ function ProjectMenuPopover({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<CustomIcon
|
||||
name="caretDown"
|
||||
className="w-4 h-4 text-chalkboard-70 dark:text-chalkboard-40 ui-open:rotate-180"
|
||||
/>
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
enter="duration-200 ease-out"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="duration-100 ease-in"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
as={Fragment}
|
||||
>
|
||||
<Popover.Overlay className="fixed inset-0 z-20 bg-chalkboard-110/50" />
|
||||
</Transition>
|
||||
|
||||
<Transition
|
||||
enter="duration-100 ease-out"
|
||||
enterFrom="opacity-0 -translate-x-1/4"
|
||||
enterTo="opacity-100 translate-x-0"
|
||||
leave="duration-75 ease-in"
|
||||
leaveFrom="opacity-100 translate-x-0"
|
||||
leaveTo="opacity-0 -translate-x-4"
|
||||
enterFrom="opacity-0 -translate-y-2"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
as={Fragment}
|
||||
>
|
||||
<Popover.Panel
|
||||
className="fixed inset-0 right-auto z-30 grid w-64 h-screen max-h-screen grid-cols-1 border rounded-r-md shadow-md bg-chalkboard-10 dark:bg-chalkboard-100 border-chalkboard-40 dark:border-chalkboard-80"
|
||||
style={{ gridTemplateRows: 'auto 1fr auto' }}
|
||||
className={`z-10 absolute top-full left-0 mt-1 pb-1 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
|
||||
border border-solid border-chalkboard-20 dark:border-chalkboard-90 rounded
|
||||
shadow-lg`}
|
||||
>
|
||||
{({ close }) => (
|
||||
<>
|
||||
<div className="flex flex-col gap-2 p-4">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
iconStart={{ icon: 'exportFile', className: 'p-1' }}
|
||||
className="border-transparent dark:border-transparent"
|
||||
disabled={!findCommand(exportCommandInfo)}
|
||||
onClick={() =>
|
||||
commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: exportCommandInfo,
|
||||
})
|
||||
}
|
||||
>
|
||||
Export Part
|
||||
</ActionButton>
|
||||
{isTauri() && (
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => {
|
||||
onProjectClose(file || null, project?.path || null, true)
|
||||
// Clear the scene and end the session.
|
||||
engineCommandManager.endSession()
|
||||
}}
|
||||
iconStart={{
|
||||
icon: 'arrowLeft',
|
||||
className: 'p-1',
|
||||
}}
|
||||
className="border-transparent dark:border-transparent"
|
||||
>
|
||||
Go to Home
|
||||
</ActionButton>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
<ul className="relative flex flex-col items-stretch content-stretch p-0.5">
|
||||
{projectMenuItems.map((props, index) => {
|
||||
if (props === 'break') {
|
||||
return index !== projectMenuItems.length - 1 ? (
|
||||
<li key={`break-${index}`} className="contents">
|
||||
<hr className="border-chalkboard-20 dark:border-chalkboard-80" />
|
||||
</li>
|
||||
) : null
|
||||
}
|
||||
|
||||
const { id, className, children, ...rest } = props
|
||||
return (
|
||||
<li key={id} className="contents">
|
||||
<ActionButton
|
||||
{...rest}
|
||||
className={
|
||||
'relative !font-sans flex items-center gap-2 rounded-sm py-1.5 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left ' +
|
||||
className
|
||||
}
|
||||
onMouseUp={() => {
|
||||
close()
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ActionButton>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
|
Reference in New Issue
Block a user