Files
AutoGPT/autogpt_platform/frontend/src/components/InputModalComponent.tsx
Ubbe b3b5eefe2c feat(frontend): change to use Sonner toast (#10334)
## Changes 🏗️

Makes changes to use [Sonner for Toasts](https://sonner.emilkowal.ski/)
rather than the [Radix UI
primitive](https://www.radix-ui.com/primitives/docs/components/toast).

<img width="431" alt="Screenshot 2025-07-09 at 15 49 47"
src="https://github.com/user-attachments/assets/c09c3c1e-fd80-44d2-9336-c955c2d4f288"
/>
<img width="444" alt="Screenshot 2025-07-09 at 15 51 05"
src="https://github.com/user-attachments/assets/cc2a3491-7b76-44e2-8bec-3ad0ac917148"
/>
<img width="450" alt="Screenshot 2025-07-09 at 15 51 50"
src="https://github.com/user-attachments/assets/e8ede05d-3488-43f4-aa43-7d3cba92a050"
/>


https://github.com/user-attachments/assets/deb4ce1c-13bb-4f69-890e-9b8680c848e7

<img width="500" alt="Screenshot 2025-07-09 at 15 59 09"
src="https://github.com/user-attachments/assets/5636969d-4c9a-41e6-acd1-afa49b8e70c6"
/>

Sonner is [the one used in
shadcn](https://ui.shadcn.com/docs/components/toast) nowadays, because
it brings great UX on touch devices:
- allows to swipe to dismiss
- they can stack nicely if multiple toasts appear ( see video 📹 )
- when stack, hovering over them reveals them all nicely ( see video 📹 )

I kept the existing `useToast()` API used on the pages, so I had to only
refactor the hook not the calls 🏁

## Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Login
  - [x] Click around the app and trigger toasts
  - [x] Toasts look good 

### For configuration changes

Nope
2025-07-09 17:09:16 +00:00

108 lines
2.8 KiB
TypeScript

import React, { FC, useEffect, useState } from "react";
import { Button } from "./ui/button";
import { Textarea } from "./ui/textarea";
import { Maximize2, Minimize2, Clipboard } from "lucide-react";
import { createPortal } from "react-dom";
import { toast } from "./molecules/Toast/use-toast";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
onSave: (value: string) => void;
title?: string;
defaultValue: string;
}
const InputModalComponent: FC<ModalProps> = ({
isOpen,
onClose,
onSave,
title,
defaultValue,
}) => {
const [tempValue, setTempValue] = useState(defaultValue);
const [isMaximized, setIsMaximized] = useState(false);
useEffect(() => {
if (isOpen) {
setTempValue(defaultValue);
setIsMaximized(false);
}
}, [isOpen, defaultValue]);
const handleSave = () => {
onSave(tempValue);
onClose();
};
const toggleSize = () => {
setIsMaximized(!isMaximized);
};
const copyValue = () => {
navigator.clipboard.writeText(tempValue).then(() => {
toast({
title: "Input value copied to clipboard!",
duration: 2000,
});
});
};
if (!isOpen) {
return null;
}
const modalContent = (
<div
id="modal-content"
className={`fixed rounded-lg border-[1.5px] bg-white p-5 ${
isMaximized ? "inset-[128px] flex flex-col" : `w-[90%] max-w-[800px]`
}`}
>
<h2 className="mb-4 text-center text-lg font-semibold">
{title || "Enter input text"}
</h2>
<div className="nowheel relative flex-grow">
<Textarea
className="h-full min-h-[200px] w-full resize-none"
value={tempValue}
onChange={(e) => setTempValue(e.target.value)}
/>
<div className="absolute bottom-2 right-2 flex space-x-2">
<Button onClick={copyValue} size="icon" variant="outline">
<Clipboard size={18} />
</Button>
<Button onClick={toggleSize} size="icon" variant="outline">
{isMaximized ? <Minimize2 size={18} /> : <Maximize2 size={18} />}
</Button>
</div>
</div>
<div className="mt-4 flex justify-end space-x-2">
<Button onClick={onClose} variant="outline">
Cancel
</Button>
<Button onClick={handleSave}>Save</Button>
</div>
</div>
);
return (
<>
{isMaximized ? (
createPortal(
<div className="fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
{modalContent}
</div>,
document.body,
)
) : (
<div className="nodrag fixed inset-0 flex items-center justify-center bg-white bg-opacity-60">
{modalContent}
</div>
)}
</>
);
};
export default InputModalComponent;