Allow user to duplicate prompt (#57)
* Add dropdown header for model switching * Allow variant duplication * Fix prettier
This commit is contained in:
@@ -2,11 +2,25 @@ import { useState, type DragEvent } from "react";
|
||||
import { type PromptVariant } from "./types";
|
||||
import { api } from "~/utils/api";
|
||||
import { useHandledAsyncCallback } from "~/utils/hooks";
|
||||
import { Button, HStack, Icon, Tooltip } from "@chakra-ui/react"; // Changed here
|
||||
import { BsX } from "react-icons/bs";
|
||||
import { RiDraggable } from "react-icons/ri";
|
||||
import {
|
||||
Button,
|
||||
HStack,
|
||||
Icon,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
MenuDivider,
|
||||
Text,
|
||||
GridItem,
|
||||
Spinner,
|
||||
} from "@chakra-ui/react"; // Changed here
|
||||
import { BsFillTrashFill, BsGear } from "react-icons/bs";
|
||||
import { FaRegClone } from "react-icons/fa";
|
||||
import { RiDraggable, RiExchangeFundsFill } from "react-icons/ri";
|
||||
import { cellPadding, headerMinHeight } from "../constants";
|
||||
import AutoResizeTextArea from "../AutoResizeTextArea";
|
||||
import { stickyHeaderStyle } from "./styles";
|
||||
|
||||
export default function VariantHeader(props: { variant: PromptVariant; canHide: boolean }) {
|
||||
const utils = api.useContext();
|
||||
@@ -49,59 +63,109 @@ export default function VariantHeader(props: { variant: PromptVariant; canHide:
|
||||
[reorderMutation, props.variant.id],
|
||||
);
|
||||
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const duplicateMutation = api.promptVariants.create.useMutation();
|
||||
|
||||
const [duplicateVariant, duplicationInProgress] = useHandledAsyncCallback(async () => {
|
||||
await duplicateMutation.mutateAsync({
|
||||
experimentId: props.variant.experimentId,
|
||||
variantId: props.variant.id,
|
||||
});
|
||||
await utils.promptVariants.list.invalidate();
|
||||
}, [duplicateMutation, props.variant.experimentId, props.variant.id]);
|
||||
|
||||
return (
|
||||
<HStack
|
||||
spacing={4}
|
||||
alignItems="center"
|
||||
minH={headerMinHeight}
|
||||
draggable={!isInputHovered}
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData("text/plain", props.variant.id);
|
||||
e.currentTarget.style.opacity = "0.4";
|
||||
<GridItem
|
||||
padding={0}
|
||||
sx={{
|
||||
...stickyHeaderStyle,
|
||||
zIndex: menuOpen ? "dropdown" : stickyHeaderStyle.zIndex,
|
||||
}}
|
||||
onDragEnd={(e) => {
|
||||
e.currentTarget.style.opacity = "1";
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
setIsDragTarget(true);
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setIsDragTarget(false);
|
||||
}}
|
||||
onDrop={onReorder}
|
||||
backgroundColor={isDragTarget ? "gray.100" : "transparent"}
|
||||
borderTopWidth={1}
|
||||
>
|
||||
<Icon
|
||||
as={RiDraggable}
|
||||
boxSize={6}
|
||||
color="gray.400"
|
||||
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||
/>
|
||||
<AutoResizeTextArea // Changed to Input
|
||||
size="sm"
|
||||
value={label}
|
||||
onChange={(e) => setLabel(e.target.value)}
|
||||
onBlur={onSaveLabel}
|
||||
placeholder="Variant Name"
|
||||
borderWidth={1}
|
||||
borderColor="transparent"
|
||||
fontWeight="bold"
|
||||
fontSize={16}
|
||||
_hover={{ borderColor: "gray.300" }}
|
||||
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||
flex={1}
|
||||
px={cellPadding.x}
|
||||
onMouseEnter={() => setIsInputHovered(true)}
|
||||
onMouseLeave={() => setIsInputHovered(false)}
|
||||
/>
|
||||
{props.canHide && (
|
||||
<Tooltip label="Remove Variant" hasArrow>
|
||||
<Button variant="ghost" colorScheme="gray" size="sm" onClick={onHide}>
|
||||
<Icon as={BsX} boxSize={6} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HStack>
|
||||
<HStack
|
||||
spacing={4}
|
||||
alignItems="flex-start"
|
||||
minH={headerMinHeight}
|
||||
draggable={!isInputHovered}
|
||||
onDragStart={(e) => {
|
||||
e.dataTransfer.setData("text/plain", props.variant.id);
|
||||
e.currentTarget.style.opacity = "0.4";
|
||||
}}
|
||||
onDragEnd={(e) => {
|
||||
e.currentTarget.style.opacity = "1";
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
setIsDragTarget(true);
|
||||
}}
|
||||
onDragLeave={() => {
|
||||
setIsDragTarget(false);
|
||||
}}
|
||||
onDrop={onReorder}
|
||||
backgroundColor={isDragTarget ? "gray.100" : "transparent"}
|
||||
>
|
||||
<Icon
|
||||
as={RiDraggable}
|
||||
boxSize={6}
|
||||
mt={2}
|
||||
color="gray.400"
|
||||
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||
/>
|
||||
<AutoResizeTextArea
|
||||
size="sm"
|
||||
value={label}
|
||||
onChange={(e) => setLabel(e.target.value)}
|
||||
onBlur={onSaveLabel}
|
||||
placeholder="Variant Name"
|
||||
borderWidth={1}
|
||||
borderColor="transparent"
|
||||
fontWeight="bold"
|
||||
fontSize={16}
|
||||
_hover={{ borderColor: "gray.300" }}
|
||||
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||
flex={1}
|
||||
px={cellPadding.x}
|
||||
onMouseEnter={() => setIsInputHovered(true)}
|
||||
onMouseLeave={() => setIsInputHovered(false)}
|
||||
/>
|
||||
|
||||
<Menu
|
||||
z-index="dropdown"
|
||||
onOpen={() => setMenuOpen(true)}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
>
|
||||
{duplicationInProgress ? (
|
||||
<Spinner boxSize={4} mx={3} my={3} />
|
||||
) : (
|
||||
<MenuButton>
|
||||
<Button variant="ghost">
|
||||
<Icon as={BsGear} />
|
||||
</Button>
|
||||
</MenuButton>
|
||||
)}
|
||||
|
||||
<MenuList mt={-3} fontSize="md">
|
||||
<MenuItem icon={<Icon as={FaRegClone} boxSize={4} w={5} />} onClick={duplicateVariant}>
|
||||
Duplicate
|
||||
</MenuItem>
|
||||
<MenuItem icon={<Icon as={RiExchangeFundsFill} boxSize={5} />}>Change Model</MenuItem>
|
||||
{props.canHide && (
|
||||
<>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
onClick={onHide}
|
||||
icon={<Icon as={BsFillTrashFill} boxSize={5} />}
|
||||
color="red.600"
|
||||
_hover={{ backgroundColor: "red.50" }}
|
||||
>
|
||||
<Text>Hide</Text>
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</HStack>
|
||||
</GridItem>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,7 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
||||
<ScenariosHeader headerRows={headerRows} numScenarios={scenarios.data.length} />
|
||||
|
||||
{variants.data.map((variant) => (
|
||||
<GridItem key={variant.uiId} padding={0} sx={stickyHeaderStyle} borderTopWidth={1}>
|
||||
<VariantHeader variant={variant} canHide={variants.data.length > 1} />
|
||||
</GridItem>
|
||||
<VariantHeader key={variant.uiId} variant={variant} canHide={variants.data.length > 1} />
|
||||
))}
|
||||
<GridItem
|
||||
rowSpan={scenarios.data.length + headerRows}
|
||||
|
||||
Reference in New Issue
Block a user