-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b20bcb1
commit c02c61c
Showing
47 changed files
with
1,108 additions
and
242 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
.../builder/components/board/graph/BlockNode/StepNode/ChoiceInputStepNode/ChoiceItemNode.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { | ||
EditablePreview, | ||
EditableInput, | ||
Editable, | ||
useEventListener, | ||
Flex, | ||
Fade, | ||
IconButton, | ||
} from '@chakra-ui/react' | ||
import { PlusIcon } from 'assets/icons' | ||
import { ContextMenu } from 'components/shared/ContextMenu' | ||
import { Coordinates } from 'contexts/GraphContext' | ||
import { useTypebot } from 'contexts/TypebotContext' | ||
import { ChoiceInputStep, ChoiceItem } from 'models' | ||
import React, { useState } from 'react' | ||
import { isDefined, isSingleChoiceInput } from 'utils' | ||
import { SourceEndpoint } from '../SourceEndpoint' | ||
import { ChoiceItemNodeContextMenu } from './ChoiceItemNodeContextMenu' | ||
|
||
type ChoiceItemNodeProps = { | ||
item: ChoiceItem | ||
onMouseMoveBottomOfElement?: () => void | ||
onMouseMoveTopOfElement?: () => void | ||
onMouseDown?: ( | ||
stepNodePosition: { absolute: Coordinates; relative: Coordinates }, | ||
item: ChoiceItem | ||
) => void | ||
} | ||
|
||
export const ChoiceItemNode = ({ | ||
item, | ||
onMouseDown, | ||
onMouseMoveBottomOfElement, | ||
onMouseMoveTopOfElement, | ||
}: ChoiceItemNodeProps) => { | ||
const { deleteChoiceItem, updateChoiceItem, typebot, createChoiceItem } = | ||
useTypebot() | ||
const [mouseDownEvent, setMouseDownEvent] = | ||
useState<{ absolute: Coordinates; relative: Coordinates }>() | ||
const [isMouseOver, setIsMouseOver] = useState(false) | ||
|
||
const handleMouseDown = (e: React.MouseEvent) => { | ||
if (!onMouseDown) return | ||
e.stopPropagation() | ||
const element = e.currentTarget as HTMLDivElement | ||
const rect = element.getBoundingClientRect() | ||
const relativeX = e.clientX - rect.left | ||
const relativeY = e.clientY - rect.top | ||
setMouseDownEvent({ | ||
absolute: { x: e.clientX + relativeX, y: e.clientY + relativeY }, | ||
relative: { x: relativeX, y: relativeY }, | ||
}) | ||
} | ||
|
||
const handleGlobalMouseUp = () => { | ||
setMouseDownEvent(undefined) | ||
} | ||
useEventListener('mouseup', handleGlobalMouseUp) | ||
|
||
const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => { | ||
if (!onMouseMoveBottomOfElement || !onMouseMoveTopOfElement) return | ||
const isMovingAndIsMouseDown = | ||
mouseDownEvent && | ||
onMouseDown && | ||
(event.movementX > 0 || event.movementY > 0) | ||
if (isMovingAndIsMouseDown) { | ||
onMouseDown(mouseDownEvent, item) | ||
deleteChoiceItem(item.id) | ||
setMouseDownEvent(undefined) | ||
} | ||
const element = event.currentTarget as HTMLDivElement | ||
const rect = element.getBoundingClientRect() | ||
const y = event.clientY - rect.top | ||
if (y > rect.height / 2) onMouseMoveBottomOfElement() | ||
else onMouseMoveTopOfElement() | ||
} | ||
|
||
const handleInputSubmit = (content: string) => | ||
updateChoiceItem(item.id, { content: content === '' ? undefined : content }) | ||
|
||
const handlePlusClick = () => { | ||
const nextIndex = | ||
( | ||
typebot?.steps.byId[item.stepId] as ChoiceInputStep | ||
).options.itemIds.indexOf(item.id) + 1 | ||
createChoiceItem({ stepId: item.stepId }, nextIndex) | ||
} | ||
|
||
const handleMouseEnter = () => setIsMouseOver(true) | ||
const handleMouseLeave = () => setIsMouseOver(false) | ||
return ( | ||
<ContextMenu<HTMLDivElement> | ||
renderMenu={() => <ChoiceItemNodeContextMenu itemId={item.id} />} | ||
> | ||
{(ref, isOpened) => ( | ||
<Flex | ||
align="center" | ||
pos="relative" | ||
onMouseEnter={handleMouseEnter} | ||
onMouseLeave={handleMouseLeave} | ||
justifyContent="center" | ||
> | ||
<Editable | ||
ref={ref} | ||
px="4" | ||
py="2" | ||
rounded="md" | ||
bgColor="green.200" | ||
borderWidth="2px" | ||
borderColor={isOpened ? 'blue.400' : 'gray.400'} | ||
defaultValue={item.content ?? 'Click to edit'} | ||
flex="1" | ||
startWithEditView={!isDefined(item.content)} | ||
onSubmit={handleInputSubmit} | ||
onMouseDown={handleMouseDown} | ||
onMouseMove={handleMouseMove} | ||
> | ||
<EditablePreview /> | ||
<EditableInput /> | ||
</Editable> | ||
{typebot && isSingleChoiceInput(typebot.steps.byId[item.stepId]) && ( | ||
<SourceEndpoint | ||
source={{ | ||
blockId: typebot.steps.byId[item.stepId].blockId, | ||
stepId: item.stepId, | ||
choiceItemId: item.id, | ||
}} | ||
pos="absolute" | ||
right="15px" | ||
/> | ||
)} | ||
<Fade | ||
in={isMouseOver} | ||
style={{ position: 'absolute', bottom: '-15px', zIndex: 3 }} | ||
unmountOnExit | ||
> | ||
<IconButton | ||
aria-label="Add item" | ||
icon={<PlusIcon />} | ||
size="xs" | ||
onClick={handlePlusClick} | ||
/> | ||
</Fade> | ||
</Flex> | ||
)} | ||
</ContextMenu> | ||
) | ||
} |
17 changes: 17 additions & 0 deletions
17
...mponents/board/graph/BlockNode/StepNode/ChoiceInputStepNode/ChoiceItemNodeContextMenu.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { MenuList, MenuItem } from '@chakra-ui/react' | ||
import { TrashIcon } from 'assets/icons' | ||
import { useTypebot } from 'contexts/TypebotContext/TypebotContext' | ||
|
||
export const ChoiceItemNodeContextMenu = ({ itemId }: { itemId: string }) => { | ||
const { deleteChoiceItem } = useTypebot() | ||
|
||
const handleDeleteClick = () => deleteChoiceItem(itemId) | ||
|
||
return ( | ||
<MenuList> | ||
<MenuItem icon={<TrashIcon />} onClick={handleDeleteClick}> | ||
Delete | ||
</MenuItem> | ||
</MenuList> | ||
) | ||
} |
28 changes: 28 additions & 0 deletions
28
...r/components/board/graph/BlockNode/StepNode/ChoiceInputStepNode/ChoiceItemNodeOverlay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Flex, FlexProps } from '@chakra-ui/react' | ||
import { ChoiceItem } from 'models' | ||
import React from 'react' | ||
|
||
type ChoiceItemNodeOverlayProps = { | ||
item: ChoiceItem | ||
} & FlexProps | ||
|
||
export const ChoiceItemNodeOverlay = ({ | ||
item, | ||
...props | ||
}: ChoiceItemNodeOverlayProps) => { | ||
return ( | ||
<Flex | ||
px="4" | ||
py="2" | ||
rounded="md" | ||
bgColor="green.200" | ||
borderWidth="2px" | ||
borderColor={'gray.400'} | ||
w="212px" | ||
pointerEvents="none" | ||
{...props} | ||
> | ||
{item.content ?? 'Click to edit'} | ||
</Flex> | ||
) | ||
} |
Oops, something went wrong.
c02c61c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
viewer-v2 – ./apps/viewer
typebot-io.vercel.app
viewer-v2-typebot-io.vercel.app
viewer-v2-git-main-typebot-io.vercel.app
c02c61c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
builder-v2 – ./apps/builder
builder-v2-git-main-typebot-io.vercel.app
next.typebot.io
builder-v2-typebot-io.vercel.app