diff --git a/package-lock.json b/package-lock.json index ba039b2..2710014 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", + "sonner": "^2.0.7", "tailwind-merge": "^2.6.1", "tw-animate-css": "^1.4.0" }, @@ -23219,6 +23220,16 @@ "node": ">= 14" } }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", diff --git a/package.json b/package.json index c37fd86..663a89f 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "input-otp": "^1.4.2", "lucide-react": "^0.563.0", "recharts": "^2.15.4", + "sonner": "^2.0.7", "tailwind-merge": "^2.6.1", "tw-animate-css": "^1.4.0" }, diff --git a/src/components/ui/Sonner.stories.tsx b/src/components/ui/Sonner.stories.tsx new file mode 100644 index 0000000..a2f1a92 --- /dev/null +++ b/src/components/ui/Sonner.stories.tsx @@ -0,0 +1,244 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { toast } from "sonner"; +import { Toaster } from "./sonner"; +import { Button } from "./button"; + +const meta = { + title: "UI/Sonner", + component: Toaster, + parameters: { layout: "centered" }, + tags: ["autodocs"], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + <> +
+ + + + + + + + + + + +
+ + + ), +}; + +export const Positions: Story = { + render: () => { + const positions = [ + "top-left", + "top-center", + "top-right", + "bottom-left", + "bottom-center", + "bottom-right", + ] as const; + + return ( + <> +
+ {positions.map((position) => ( + + ))} +
+ + + ); + }, +}; + +export const RichColors: Story = { + render: () => ( + <> +
+ + + + +
+ + + ), +}; + +export const CloseButton: Story = { + render: () => ( + <> + + + + ), +}; + +export const Expanded: Story = { + render: () => ( + <> + + + + ), +}; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index bb0a40d..76f881c 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -286,6 +286,9 @@ export { type LayoutContextValue, } from "../wordpress/layout"; +// Sonner (toast notifications) +export { Toaster } from "./sonner"; + // Layout menu (searchable, multi-label nested menu for LayoutSidebar) export { LayoutMenu, diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx new file mode 100644 index 0000000..6bf4a8d --- /dev/null +++ b/src/components/ui/sonner.tsx @@ -0,0 +1,39 @@ +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon, +} from "lucide-react"; +import { useThemeOptional } from "@/providers"; +import { Toaster as Sonner, type ToasterProps } from "sonner"; + +const Toaster = ({ ...props }: ToasterProps) => { + const theme = useThemeOptional(); + const mode = theme?.mode ?? "system"; + + return ( + , + info: , + warning: , + error: , + loading: , + }} + style={ + { + "--normal-bg": "var(--popover)", + "--normal-text": "var(--popover-foreground)", + "--normal-border": "var(--border)", + "--border-radius": "var(--radius)", + } as React.CSSProperties + } + {...props} + /> + ); +}; + +export { Toaster }; diff --git a/src/index.ts b/src/index.ts index 3d5c9e2..0868343 100644 --- a/src/index.ts +++ b/src/index.ts @@ -236,6 +236,8 @@ export { useComboboxAnchor, // Rich Text Editor RichTextEditor, + // Sonner (toast notifications) + Toaster, // Calendar & DatePicker Calendar, DatePicker, @@ -334,4 +336,5 @@ export { // ============================================ export { cn } from "@/lib/utils"; export { twMerge } from "tailwind-merge"; -export * as recharts from 'recharts'; \ No newline at end of file +export * as recharts from 'recharts'; +export { toast } from 'sonner'; \ No newline at end of file