diff --git a/package-lock.json b/package-lock.json
index 11cbe77..a8fb247 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,9 +12,11 @@
"@heroicons/react": "^2.0.18",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-toast": "^1.1.5",
"@storybook/react-vite": "^8.0.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
@@ -3519,6 +3521,14 @@
}
}
},
+ "node_modules/@radix-ui/react-icons": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
+ "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==",
+ "peerDependencies": {
+ "react": "^16.x || ^17.x || ^18.x"
+ }
+ },
"node_modules/@radix-ui/react-id": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
@@ -3723,6 +3733,40 @@
}
}
},
+ "node_modules/@radix-ui/react-toast": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz",
+ "integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-collection": "1.0.3",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-dismissable-layer": "1.0.5",
+ "@radix-ui/react-portal": "1.0.4",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-use-callback-ref": "1.0.1",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-layout-effect": "1.0.1",
+ "@radix-ui/react-visually-hidden": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
diff --git a/package.json b/package.json
index 93bfc83..2c8609e 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,11 @@
"@heroicons/react": "^2.0.18",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-toast": "^1.1.5",
"@storybook/react-vite": "^8.0.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
diff --git a/src/components/Avatar/Avatar.jsx b/src/components/Avatar/Avatar.jsx
new file mode 100644
index 0000000..f2f65f6
--- /dev/null
+++ b/src/components/Avatar/Avatar.jsx
@@ -0,0 +1,24 @@
+
+
+/**
+ * @typedef {Object} AvatarProps
+ * @property {string} AvatarProps.imgSrc - some description here
+ * @property {string} AvatarProps.descricao - some description here
+ * @property { 'square' | 'circle' } AvatarProps.format - Formato do Avatar (Circular ou Quadrado)
+ * @param {AvatarProps} props
+ * @param props
+ */
+
+
+export function Avatar({ imgSrc, descricao, format }) {
+
+ return (
+
+

+
+ );
+ }
diff --git a/src/components/Avatar/Avatar.stories.tsx b/src/components/Avatar/Avatar.stories.tsx
new file mode 100644
index 0000000..b4efe89
--- /dev/null
+++ b/src/components/Avatar/Avatar.stories.tsx
@@ -0,0 +1,48 @@
+import { StoryObj, Meta } from '@storybook/react';
+import { AvatarProps } from './Avatar';
+import { Avatar } from './Avatar'
+
+
+const meta: Meta = {
+ component: Avatar,
+ argTypes: {
+ imgSrc: { description: 'Endereço (URL) da Imagem do Avatar' },
+ descricao: { description: 'Descrição da Imagem do Avatar' },
+ format: {
+ description: 'Formato do Avatar',
+ control: { type: 'radio' },
+ options: ['circle','square'],
+ },
+ },
+ render: (args: AvatarProps) => ,
+};
+
+export default meta;
+type Story = StoryObj;
+
+
+// More on writing stories with args: https://storybook.js.org/docs/html/writing-stories/args
+export const Circle: Story = {
+ args: {
+ imgSrc: 'data:image/jpeg;base64,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',
+ descricao: 'Octocat from Github',
+ format: 'circle'
+ }
+};
+export const Square: Story = {
+ args: {
+ imgSrc: 'data:image/jpeg;base64,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',
+ descricao: 'Octocat from Github',
+ format: 'square'
+ }
+};
+
+/*
+ *👇 Render functions are a framework specific feature to allow you control on how the component renders.
+ * See https://storybook.js.org/docs/api/csf
+ * to learn how to use render functions.
+ */
+export const Primary: Story = {
+ render: (args) => ,
+};
+
diff --git a/src/components/Popup/PopUp.jsx b/src/components/Popup/PopUp.jsx
new file mode 100644
index 0000000..cf59d53
--- /dev/null
+++ b/src/components/Popup/PopUp.jsx
@@ -0,0 +1,55 @@
+import { Button } from "@/components/ui/button";
+import { useToast } from "@/components/ui/use-toast";
+import { Toaster } from "@/components/ui/toaster";
+import { CheckCircledIcon } from '@radix-ui/react-icons';
+
+/**
+ * @typedef {Object} ToastContentProps
+ * @property {string} mensagem - A mensagem a ser exibida no toast.
+ */
+
+/**
+ * Componente para o conteúdo do Toast
+ * @param {ToastContentProps} props
+ */
+export const ToastContent = ({ mensagem }) => (
+
+
+ {mensagem}
+
+);
+
+/**
+ * @typedef {Object} PopUpProps
+ * @property {string} [buttonText="Save"] - O texto do botão.
+ * @property {string} [mensagem="Cat was updated"] - A mensagem do toast.
+ */
+
+/**
+ * Componente PopUp
+ * @param {PopUpProps} props
+ */
+
+export function PopUp({ buttonText = "Save", mensagem = "Cat was updated" }) {
+ const { toast } = useToast();
+
+ const handleSaveClick = () => {
+ toast({
+ description: ,
+ });
+ };
+
+ return (
+
+ );
+}
+
+// Componente PopUpWithToaster (wrapper para PopUp e Toaster)
+export const PopUpWithToaster = ({ buttonText = "Save", mensagem = "Cat was updated" }) => (
+ <>
+
+
+ >
+);
diff --git a/src/components/Popup/PopUp.stories.tsx b/src/components/Popup/PopUp.stories.tsx
new file mode 100644
index 0000000..1938315
--- /dev/null
+++ b/src/components/Popup/PopUp.stories.tsx
@@ -0,0 +1,31 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { PopUp, PopUpWithToaster } from './PopUp';
+
+
+const meta: Meta = {
+ title: 'Components/PopUp', // Define um título para o grupo de stories
+ component: PopUp,
+ argTypes: {
+ buttonText: { control: 'text' },
+ mensagem: { control: 'text' },
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+// Stories
+export const Primary: Story = {
+ render: () => ,
+}; // Usa os valores padrão das props
+
+export const Secondary: Story = {
+ args: {
+ buttonText: "Atualizar",
+ mensagem: "As informações do gato foram atualizadas com sucesso!",
+ },
+ render: ( {buttonText, mensagem}) => ,
+};
diff --git a/src/components/ui/toast.tsx b/src/components/ui/toast.tsx
new file mode 100644
index 0000000..a822477
--- /dev/null
+++ b/src/components/ui/toast.tsx
@@ -0,0 +1,127 @@
+import * as React from "react"
+import * as ToastPrimitives from "@radix-ui/react-toast"
+import { cva, type VariantProps } from "class-variance-authority"
+import { X } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const ToastProvider = ToastPrimitives.Provider
+
+const ToastViewport = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastViewport.displayName = ToastPrimitives.Viewport.displayName
+
+const toastVariants = cva(
+ "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
+ {
+ variants: {
+ variant: {
+ default: "border bg-background text-foreground",
+ destructive:
+ "destructive group border-destructive bg-destructive text-destructive-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+const Toast = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, variant, ...props }, ref) => {
+ return (
+
+ )
+})
+Toast.displayName = ToastPrimitives.Root.displayName
+
+const ToastAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastAction.displayName = ToastPrimitives.Action.displayName
+
+const ToastClose = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+ToastClose.displayName = ToastPrimitives.Close.displayName
+
+const ToastTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastTitle.displayName = ToastPrimitives.Title.displayName
+
+const ToastDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+ToastDescription.displayName = ToastPrimitives.Description.displayName
+
+type ToastProps = React.ComponentPropsWithoutRef
+
+type ToastActionElement = React.ReactElement
+
+export {
+ type ToastProps,
+ type ToastActionElement,
+ ToastProvider,
+ ToastViewport,
+ Toast,
+ ToastTitle,
+ ToastDescription,
+ ToastClose,
+ ToastAction,
+}
diff --git a/src/components/ui/toaster.tsx b/src/components/ui/toaster.tsx
new file mode 100644
index 0000000..a2209ba
--- /dev/null
+++ b/src/components/ui/toaster.tsx
@@ -0,0 +1,33 @@
+import {
+ Toast,
+ ToastClose,
+ ToastDescription,
+ ToastProvider,
+ ToastTitle,
+ ToastViewport,
+} from "@/components/ui/toast"
+import { useToast } from "@/components/ui/use-toast"
+
+export function Toaster() {
+ const { toasts } = useToast()
+
+ return (
+
+ {toasts.map(function ({ id, title, description, action, ...props }) {
+ return (
+
+
+ {title && {title}}
+ {description && (
+ {description}
+ )}
+
+ {action}
+
+
+ )
+ })}
+
+
+ )
+}
diff --git a/src/components/ui/use-toast.ts b/src/components/ui/use-toast.ts
new file mode 100644
index 0000000..1671307
--- /dev/null
+++ b/src/components/ui/use-toast.ts
@@ -0,0 +1,192 @@
+// Inspired by react-hot-toast library
+import * as React from "react"
+
+import type {
+ ToastActionElement,
+ ToastProps,
+} from "@/components/ui/toast"
+
+const TOAST_LIMIT = 1
+const TOAST_REMOVE_DELAY = 1000000
+
+type ToasterToast = ToastProps & {
+ id: string
+ title?: React.ReactNode
+ description?: React.ReactNode
+ action?: ToastActionElement
+}
+
+const actionTypes = {
+ ADD_TOAST: "ADD_TOAST",
+ UPDATE_TOAST: "UPDATE_TOAST",
+ DISMISS_TOAST: "DISMISS_TOAST",
+ REMOVE_TOAST: "REMOVE_TOAST",
+} as const
+
+let count = 0
+
+function genId() {
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
+ return count.toString()
+}
+
+type ActionType = typeof actionTypes
+
+type Action =
+ | {
+ type: ActionType["ADD_TOAST"]
+ toast: ToasterToast
+ }
+ | {
+ type: ActionType["UPDATE_TOAST"]
+ toast: Partial
+ }
+ | {
+ type: ActionType["DISMISS_TOAST"]
+ toastId?: ToasterToast["id"]
+ }
+ | {
+ type: ActionType["REMOVE_TOAST"]
+ toastId?: ToasterToast["id"]
+ }
+
+interface State {
+ toasts: ToasterToast[]
+}
+
+const toastTimeouts = new Map>()
+
+const addToRemoveQueue = (toastId: string) => {
+ if (toastTimeouts.has(toastId)) {
+ return
+ }
+
+ const timeout = setTimeout(() => {
+ toastTimeouts.delete(toastId)
+ dispatch({
+ type: "REMOVE_TOAST",
+ toastId: toastId,
+ })
+ }, TOAST_REMOVE_DELAY)
+
+ toastTimeouts.set(toastId, timeout)
+}
+
+export const reducer = (state: State, action: Action): State => {
+ switch (action.type) {
+ case "ADD_TOAST":
+ return {
+ ...state,
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
+ }
+
+ case "UPDATE_TOAST":
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
+ ),
+ }
+
+ case "DISMISS_TOAST": {
+ const { toastId } = action
+
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
+ // but I'll keep it here for simplicity
+ if (toastId) {
+ addToRemoveQueue(toastId)
+ } else {
+ state.toasts.forEach((toast) => {
+ addToRemoveQueue(toast.id)
+ })
+ }
+
+ return {
+ ...state,
+ toasts: state.toasts.map((t) =>
+ t.id === toastId || toastId === undefined
+ ? {
+ ...t,
+ open: false,
+ }
+ : t
+ ),
+ }
+ }
+ case "REMOVE_TOAST":
+ if (action.toastId === undefined) {
+ return {
+ ...state,
+ toasts: [],
+ }
+ }
+ return {
+ ...state,
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
+ }
+ }
+}
+
+const listeners: Array<(state: State) => void> = []
+
+let memoryState: State = { toasts: [] }
+
+function dispatch(action: Action) {
+ memoryState = reducer(memoryState, action)
+ listeners.forEach((listener) => {
+ listener(memoryState)
+ })
+}
+
+type Toast = Omit
+
+function toast({ ...props }: Toast) {
+ const id = genId()
+
+ const update = (props: ToasterToast) =>
+ dispatch({
+ type: "UPDATE_TOAST",
+ toast: { ...props, id },
+ })
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
+
+ dispatch({
+ type: "ADD_TOAST",
+ toast: {
+ ...props,
+ id,
+ open: true,
+ onOpenChange: (open) => {
+ if (!open) dismiss()
+ },
+ },
+ })
+
+ return {
+ id: id,
+ dismiss,
+ update,
+ }
+}
+
+function useToast() {
+ const [state, setState] = React.useState(memoryState)
+
+ React.useEffect(() => {
+ listeners.push(setState)
+ return () => {
+ const index = listeners.indexOf(setState)
+ if (index > -1) {
+ listeners.splice(index, 1)
+ }
+ }
+ }, [state])
+
+ return {
+ ...state,
+ toast,
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
+ }
+}
+
+export { useToast, toast }