2
0

💄 Improve editor header responsiveness

Closes #1204
This commit is contained in:
Baptiste Arnaud
2024-03-07 10:09:00 +01:00
parent c2003dab91
commit 5dafb64963
2 changed files with 293 additions and 236 deletions

View File

@ -8,6 +8,8 @@ import {
Text,
useColorModeValue,
useDisclosure,
StackProps,
chakra,
} from '@chakra-ui/react'
import {
BuoyIcon,
@ -37,70 +39,12 @@ import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
import { Plan } from '@typebot.io/prisma'
export const TypebotHeader = () => {
const { t } = useTranslate()
const router = useRouter()
const {
typebot,
publishedTypebot,
updateTypebot,
save,
undo,
redo,
canUndo,
canRedo,
isSavingLoading,
currentUserMode,
} = useTypebot()
const { typebot, publishedTypebot, currentUserMode } = useTypebot()
const { workspace } = useWorkspace()
const {
setRightPanel,
rightPanel,
setStartPreviewAtGroup,
setStartPreviewAtEvent,
} = useEditor()
const [isUndoShortcutTooltipOpen, setUndoShortcutTooltipOpen] =
useState(false)
const hideUndoShortcutTooltipLater = useDebouncedCallback(() => {
setUndoShortcutTooltipOpen(false)
}, 1000)
const [isRedoShortcutTooltipOpen, setRedoShortcutTooltipOpen] =
useState(false)
const hideRedoShortcutTooltipLater = useDebouncedCallback(() => {
setRedoShortcutTooltipOpen(false)
}, 1000)
const { isOpen, onOpen } = useDisclosure()
const headerBgColor = useColorModeValue('white', 'gray.900')
const handleNameSubmit = (name: string) =>
updateTypebot({ updates: { name } })
const handleChangeIcon = (icon: string) =>
updateTypebot({ updates: { icon } })
const handlePreviewClick = async () => {
setStartPreviewAtGroup(undefined)
setStartPreviewAtEvent(undefined)
save().then()
setRightPanel(RightPanel.PREVIEW)
}
useKeyboardShortcuts({
undo: () => {
if (!canUndo) return
hideUndoShortcutTooltipLater.flush()
setUndoShortcutTooltipOpen(true)
hideUndoShortcutTooltipLater()
undo()
},
redo: () => {
if (!canRedo) return
hideUndoShortcutTooltipLater.flush()
setRedoShortcutTooltipOpen(true)
hideRedoShortcutTooltipLater()
redo()
},
})
const handleHelpClick = () => {
isCloudProdInstance() && workspace?.plan && workspace.plan !== Plan.FREE
? onOpen()
@ -121,66 +65,76 @@ export const TypebotHeader = () => {
flexShrink={0}
>
{isOpen && <SupportBubble autoShowDelay={0} />}
<HStack
display={['none', 'flex']}
pos={{ base: 'absolute', xl: 'static' }}
right={{ base: isDefined(publishedTypebot) ? 340 : 295, xl: 0 }}
>
<Button
as={Link}
href={`/typebots/${typebot?.id}/edit`}
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.flowButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebot?.id}/theme`}
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.themeButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebot?.id}/settings`}
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.settingsButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebot?.id}/share`}
colorScheme={router.pathname.endsWith('share') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
size="sm"
>
{t('share.button.label')}
</Button>
{isDefined(publishedTypebot) && (
<Button
as={Link}
href={`/typebots/${typebot?.id}/results`}
colorScheme={router.pathname.includes('results') ? 'blue' : 'gray'}
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.resultsButton.label')}
</Button>
)}
</HStack>
<HStack
<LeftElements pos="absolute" left="1rem" onHelpClick={handleHelpClick} />
<TypebotNav
display={{ base: 'none', xl: 'flex' }}
pos={{ base: 'absolute' }}
typebotId={typebot?.id}
isResultsDisplayed={isDefined(publishedTypebot)}
/>
<RightElements
right="40px"
pos="absolute"
left="1rem"
justify="center"
align="center"
spacing="6"
>
display={['none', 'flex']}
isResultsDisplayed={isDefined(publishedTypebot)}
/>
</Flex>
)
}
const LeftElements = (props: StackProps & { onHelpClick: () => void }) => {
const { t } = useTranslate()
const router = useRouter()
const {
typebot,
updateTypebot,
canUndo,
canRedo,
undo,
redo,
currentUserMode,
isSavingLoading,
} = useTypebot()
const [isRedoShortcutTooltipOpen, setRedoShortcutTooltipOpen] =
useState(false)
const [isUndoShortcutTooltipOpen, setUndoShortcutTooltipOpen] =
useState(false)
const hideUndoShortcutTooltipLater = useDebouncedCallback(() => {
setUndoShortcutTooltipOpen(false)
}, 1000)
const hideRedoShortcutTooltipLater = useDebouncedCallback(() => {
setRedoShortcutTooltipOpen(false)
}, 1000)
const handleNameSubmit = (name: string) =>
updateTypebot({ updates: { name } })
const handleChangeIcon = (icon: string) =>
updateTypebot({ updates: { icon } })
useKeyboardShortcuts({
undo: () => {
if (!canUndo) return
hideUndoShortcutTooltipLater.flush()
setUndoShortcutTooltipOpen(true)
hideUndoShortcutTooltipLater()
undo()
},
redo: () => {
if (!canRedo) return
hideUndoShortcutTooltipLater.flush()
setRedoShortcutTooltipOpen(true)
hideRedoShortcutTooltipLater()
redo()
},
})
return (
<HStack justify="center" align="center" spacing="6" {...props}>
<HStack alignItems="center" spacing={3}>
<IconButton
as={Link}
@ -266,8 +220,15 @@ export const TypebotHeader = () => {
</Tooltip>
</HStack>
)}
<Button leftIcon={<BuoyIcon />} onClick={handleHelpClick} size="sm">
<Button
leftIcon={<BuoyIcon />}
onClick={props.onHelpClick}
size="sm"
iconSpacing={{ base: 0, xl: 2 }}
>
<chakra.span display={{ base: 'none', xl: 'inline' }}>
{t('editor.header.helpButton.label')}
</chakra.span>
</Button>
</HStack>
{isSavingLoading && (
@ -279,8 +240,34 @@ export const TypebotHeader = () => {
</HStack>
)}
</HStack>
)
}
<HStack right="40px" pos="absolute" display={['none', 'flex']}>
const RightElements = (props: StackProps & { isResultsDisplayed: boolean }) => {
const router = useRouter()
const { t } = useTranslate()
const { typebot, currentUserMode, save } = useTypebot()
const {
setRightPanel,
rightPanel,
setStartPreviewAtGroup,
setStartPreviewAtEvent,
} = useEditor()
const handlePreviewClick = async () => {
setStartPreviewAtGroup(undefined)
setStartPreviewAtEvent(undefined)
save().then()
setRightPanel(RightPanel.PREVIEW)
}
return (
<HStack {...props}>
<TypebotNav
display={{ base: 'none', md: 'flex', xl: 'none' }}
typebotId={typebot?.id}
isResultsDisplayed={props.isResultsDisplayed}
/>
<Flex pos="relative">
<ShareTypebotButton isLoading={isNotDefined(typebot)} />
</Flex>
@ -291,12 +278,78 @@ export const TypebotHeader = () => {
isLoading={isNotDefined(typebot)}
leftIcon={<PlayIcon />}
size="sm"
iconSpacing={{ base: 0, xl: 2 }}
>
<chakra.span display={{ base: 'none', xl: 'inline' }}>
{t('editor.header.previewButton.label')}
</chakra.span>
</Button>
)}
{currentUserMode === 'write' && <PublishButton size="sm" />}
</HStack>
</Flex>
)
}
const TypebotNav = ({
typebotId,
isResultsDisplayed,
...stackProps
}: {
typebotId?: string
isResultsDisplayed: boolean
} & StackProps) => {
const { t } = useTranslate()
const router = useRouter()
return (
<HStack {...stackProps}>
<Button
as={Link}
href={`/typebots/${typebotId}/edit`}
colorScheme={router.pathname.includes('/edit') ? 'blue' : 'gray'}
variant={router.pathname.includes('/edit') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.flowButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebotId}/theme`}
colorScheme={router.pathname.endsWith('theme') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('theme') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.themeButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebotId}/settings`}
colorScheme={router.pathname.endsWith('settings') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('settings') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.settingsButton.label')}
</Button>
<Button
as={Link}
href={`/typebots/${typebotId}/share`}
colorScheme={router.pathname.endsWith('share') ? 'blue' : 'gray'}
variant={router.pathname.endsWith('share') ? 'outline' : 'ghost'}
size="sm"
>
{t('share.button.label')}
</Button>
{isResultsDisplayed && (
<Button
as={Link}
href={`/typebots/${typebotId}/results`}
colorScheme={router.pathname.includes('results') ? 'blue' : 'gray'}
variant={router.pathname.includes('results') ? 'outline' : 'ghost'}
size="sm"
>
{t('editor.header.resultsButton.label')}
</Button>
)}
</HStack>
)
}

View File

@ -3,6 +3,7 @@ import {
PopoverTrigger,
PopoverContent,
Button,
chakra,
} from '@chakra-ui/react'
import { UsersIcon } from '@/components/icons'
import React from 'react'
@ -20,8 +21,11 @@ export const ShareTypebotButton = ({ isLoading }: { isLoading: boolean }) => {
leftIcon={<UsersIcon />}
aria-label={t('share.button.popover.ariaLabel')}
size="sm"
iconSpacing={{ base: 0, xl: 2 }}
>
<chakra.span display={{ base: 'none', xl: 'inline' }}>
{t('share.button.label')}
</chakra.span>
</Button>
</PopoverTrigger>
<PopoverContent