Skip to content

Commit

Permalink
🐛 (editor) Fix variable dropdown overflow
Browse files Browse the repository at this point in the history
Closes #209
  • Loading branch information
baptisteArno committed Jan 4, 2023
1 parent e1af6af commit c1a32ce
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 94 deletions.
2 changes: 1 addition & 1 deletion apps/builder/src/components/SearchableDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
useDisclosure,
useOutsideClick,
Flex,
Popover,
Input,
Expand All @@ -16,6 +15,7 @@ import { useState, useRef, useEffect, ChangeEvent, ReactNode } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import { env, isDefined } from 'utils'
import { VariablesButton } from '@/features/variables'
import { useOutsideClick } from '@/hooks/useOutsideClick'

type Props = {
selectedItem?: string
Expand Down
144 changes: 74 additions & 70 deletions apps/builder/src/components/VariableSearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
HStack,
useColorModeValue,
PopoverAnchor,
useOutsideClick,
Portal,
} from '@chakra-ui/react'
import { EditIcon, PlusIcon, TrashIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor'
Expand All @@ -19,6 +19,7 @@ import { Variable } from 'models'
import React, { useState, useRef, ChangeEvent, useEffect } from 'react'
import { useDebouncedCallback } from 'use-debounce'
import { byId, env, isDefined, isNotDefined } from 'utils'
import { useOutsideClick } from '@/hooks/useOutsideClick'

type Props = {
initialVariableId?: string
Expand Down Expand Up @@ -188,75 +189,78 @@ export const VariableSearchInput = ({
{...inputProps}
/>
</PopoverAnchor>
<PopoverContent
maxH="35vh"
overflowY="scroll"
role="menu"
w="inherit"
shadow="lg"
>
{isCreateVariableButtonDisplayed && (
<Button
ref={createVariableItemRef}
role="menuitem"
minH="40px"
onClick={handleCreateNewVariableClick}
fontSize="16px"
fontWeight="normal"
rounded="none"
colorScheme="gray"
variant="ghost"
justifyContent="flex-start"
leftIcon={<PlusIcon />}
bgColor={keyboardFocusIndex === 0 ? bg : 'transparent'}
>
Create &quot;{inputValue}&quot;
</Button>
)}
{filteredItems.length > 0 && (
<>
{filteredItems.map((item, idx) => {
const indexInList = isCreateVariableButtonDisplayed
? idx + 1
: idx
return (
<Button
ref={(el) => (itemsRef.current[idx] = el)}
role="menuitem"
minH="40px"
key={idx}
onClick={handleVariableNameClick(item)}
fontSize="16px"
fontWeight="normal"
rounded="none"
colorScheme="gray"
variant="ghost"
justifyContent="space-between"
bgColor={
keyboardFocusIndex === indexInList ? bg : 'transparent'
}
>
{item.name}
<HStack>
<IconButton
icon={<EditIcon />}
aria-label="Rename variable"
size="xs"
onClick={handleRenameVariableClick(item)}
/>
<IconButton
icon={<TrashIcon />}
aria-label="Remove variable"
size="xs"
onClick={handleDeleteVariableClick(item)}
/>
</HStack>
</Button>
)
})}
</>
)}
</PopoverContent>
<Portal>
<PopoverContent
maxH="35vh"
overflowY="scroll"
role="menu"
w="inherit"
shadow="lg"
>
{isCreateVariableButtonDisplayed && (
<Button
ref={createVariableItemRef}
role="menuitem"
minH="40px"
onClick={handleCreateNewVariableClick}
fontSize="16px"
fontWeight="normal"
rounded="none"
colorScheme="gray"
variant="ghost"
justifyContent="flex-start"
leftIcon={<PlusIcon />}
bgColor={keyboardFocusIndex === 0 ? bg : 'transparent'}
>
Create &quot;{inputValue}&quot;
</Button>
)}
{filteredItems.length > 0 && (
<>
{filteredItems.map((item, idx) => {
const indexInList = isCreateVariableButtonDisplayed
? idx + 1
: idx
return (
<Button
ref={(el) => (itemsRef.current[idx] = el)}
role="menuitem"
minH="40px"
key={idx}
onMouseDown={(e) => e.stopPropagation()}
onClick={handleVariableNameClick(item)}
fontSize="16px"
fontWeight="normal"
rounded="none"
colorScheme="gray"
variant="ghost"
justifyContent="space-between"
bgColor={
keyboardFocusIndex === indexInList ? bg : 'transparent'
}
>
{item.name}
<HStack>
<IconButton
icon={<EditIcon />}
aria-label="Rename variable"
size="xs"
onClick={handleRenameVariableClick(item)}
/>
<IconButton
icon={<TrashIcon />}
aria-label="Remove variable"
size="xs"
onClick={handleDeleteVariableClick(item)}
/>
</HStack>
</Button>
)
})}
</>
)}
</PopoverContent>
</Portal>
</Popover>
</Flex>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
Stack,
useColorModeValue,
useEventListener,
useOutsideClick,
} from '@chakra-ui/react'
import React, { useEffect, useRef, useState } from 'react'
import {
Expand All @@ -22,6 +21,7 @@ import { serializeHtml } from '@udecode/plate-serializer-html'
import { parseHtmlStringToPlainText } from '../../utils'
import { VariableSearchInput } from '@/components/VariableSearchInput'
import { colors } from '@/lib/theme'
import { useOutsideClick } from '@/hooks/useOutsideClick'

type TextBubbleEditorContentProps = {
id: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const WebhookSettings = ({
Query params
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<AccordionPanel py={4} as={Stack} spacing="6">
<TableList<KeyValue>
initialItems={localWebhook.queryParams}
onItemsChange={handleQueryParamsChange}
Expand All @@ -208,7 +208,7 @@ export const WebhookSettings = ({
Headers
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<AccordionPanel py={4} as={Stack} spacing="6">
<TableList<KeyValue>
initialItems={localWebhook.headers}
onItemsChange={handleHeadersChange}
Expand All @@ -223,7 +223,7 @@ export const WebhookSettings = ({
Body
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<AccordionPanel py={4} as={Stack} spacing="6">
<SwitchWithLabel
label="Custom body"
initialValue={options.isCustomBody ?? true}
Expand All @@ -244,7 +244,7 @@ export const WebhookSettings = ({
Variable values for test
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<AccordionPanel py={4} as={Stack} spacing="6">
<TableList<VariableForTest>
initialItems={
options?.variablesForTest ?? { byId: {}, allIds: [] }
Expand Down Expand Up @@ -279,7 +279,7 @@ export const WebhookSettings = ({
Save in variables
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} as={Stack} spacing="6">
<AccordionPanel py={4} as={Stack} spacing="6">
<TableList<ResponseVariableMapping>
initialItems={options.responseVariableMapping}
onItemsChange={handleResponseMappingChange}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,6 @@ const NonMemoizedDraggableGroupNode = ({
const handleTitleSubmit = (title: string) =>
title.length > 0 ? updateGroup(groupIndex, { title }) : undefined

const handleMouseDown = (e: React.MouseEvent) => {
e.stopPropagation()
}

const handleMouseEnter = () => {
if (isReadOnly) return
if (mouseOverGroup?.id !== group.id && !isStartGroup)
Expand Down Expand Up @@ -200,7 +196,6 @@ const NonMemoizedDraggableGroupNode = ({
currentCoordinates?.y ?? 0
}px)`,
}}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
cursor={isMouseDown ? 'grabbing' : 'pointer'}
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/src/features/graph/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ export const getEndpointTopOffset = ({
if (!endpointId) return
const endpointRef = endpoints[endpointId]?.ref
if (!endpointRef?.current) return
const endpointHeight = 28 * graphScale
const endpointHeight = 34 * graphScale
return (
(endpointRef.current.getBoundingClientRect().y +
endpointHeight / 2 -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
InputRightElement,
ModalFooter,
Link,
useColorModeValue,
} from '@chakra-ui/react'
import { ExternalLinkIcon } from '@/components/icons'
import { env, getViewerUrl } from 'utils'
Expand Down Expand Up @@ -44,7 +45,7 @@ export const WordpressModal = ({
<Link
href="https://wordpress.org/plugins/typebot/"
isExternal
color="blue.500"
color={useColorModeValue('blue.500', 'blue.300')}
>
the official Typebot WordPress plugin
<ExternalLinkIcon mx="2px" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
>
<ModalOverlay />
<ModalContent h="85vh">
<ModalBody as={HStack} p="0">
<Stack w="full" h="full">
<ModalBody as={HStack} p="0" spacing="0">
<Stack w="full" h="full" spacing="4">
<Heading pl="10" pt="4" fontSize="2xl">
{selectedTemplate.emoji}{' '}
<chakra.span ml="2">{selectedTemplate.name}</chakra.span>
Expand All @@ -79,6 +79,7 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => {
apiHost={getViewerUrl({ isBuilder: true })}
typebot={parseTypebotToPublicTypebot(typebot)}
key={typebot.id}
style={{ borderRadius: '0.25rem' }}
/>
)}
</Stack>
Expand Down
26 changes: 26 additions & 0 deletions apps/builder/src/hooks/useOutsideClick.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useEventListener } from '@chakra-ui/react'
import { RefObject } from 'react'

type Handler = (event: MouseEvent) => void

type Props<T> = {
ref: RefObject<T>
handler: Handler
mouseEvent?: 'mousedown' | 'mouseup'
}

export const useOutsideClick = <T extends HTMLElement = HTMLElement>({
ref,
handler,
mouseEvent = 'mousedown',
}: Props<T>): void => {
const triggerHandlerIfOutside = (event: MouseEvent) => {
const el = ref?.current
if (!el || el.contains(event.target as Node)) {
return
}
handler(event)
}

useEventListener(mouseEvent, triggerHandlerIfOutside)
}
20 changes: 12 additions & 8 deletions packages/scripts/fixTypebots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import {
Group,
InputBlockType,
PublicTypebot,
publicTypebotSchema,
Theme,
Typebot,
typebotSchema,
} from 'models'
import { isDefined, isNotDefined } from 'utils'
import { promptAndSetEnvironment } from './utils'
Expand Down Expand Up @@ -125,13 +125,10 @@ const fixTypebots = async () => {
log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'],
})

const twoDaysAgo = new Date()
twoDaysAgo.setDate(twoDaysAgo.getDate() - 2)

const typebots = await prisma.typebot.findMany({
const typebots = await prisma.publicTypebot.findMany({
where: {
updatedAt: {
gte: twoDaysAgo,
gte: new Date('2023-01-01T00:00:00.000Z'),
},
},
})
Expand All @@ -150,7 +147,7 @@ const fixTypebots = async () => {
(progress / total) * 100
)}%) (${totalFixed} fixed typebots)`
)
const parser = typebotSchema.safeParse({
const parser = publicTypebotSchema.safeParse({
...typebot,
updatedAt: new Date(typebot.updatedAt),
createdAt: new Date(typebot.createdAt),
Expand All @@ -161,7 +158,7 @@ const fixTypebots = async () => {
updatedAt: new Date(),
createdAt: new Date(typebot.createdAt),
}
typebotSchema.parse(fixedTypebot)
publicTypebotSchema.parse(fixedTypebot)
fixedTypebots.push(fixedTypebot)
totalFixed += 1
diffs.push({
Expand All @@ -182,6 +179,13 @@ const fixTypebots = async () => {
where: { id: fixedTypebot.id },
data: {
...fixedTypebot,
// theme: fixedTypebot.theme ?? undefined,
// settings: fixedTypebot.settings ?? undefined,
// resultsTablePreferences:
// 'resultsTablePreferences' in fixedTypebot &&
// fixedTypebot.resultsTablePreferences
// ? fixedTypebot.resultsTablePreferences
// : undefined,
} as any,
})
)
Expand Down

0 comments on commit c1a32ce

Please sign in to comment.