diff --git a/src/components/ui/RichTextEditor.stories.tsx b/src/components/ui/RichTextEditor.stories.tsx index c89b646..3d10271 100644 --- a/src/components/ui/RichTextEditor.stories.tsx +++ b/src/components/ui/RichTextEditor.stories.tsx @@ -1,4 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; +import { WandSparkles } from "lucide-react"; +import { fn } from "storybook/test"; import { RichTextEditor } from "./rich-text-editor"; import { Label } from "./label"; @@ -14,6 +16,7 @@ const meta = { }, placeholder: { control: "text" }, defaultValue: { control: "text" }, + contentAction: { control: false }, }, decorators: [ (Story) => ( @@ -28,7 +31,7 @@ export default meta; type Story = StoryObj; -/** Full toolbar variant with all formatting options including font size, image, undo/redo, and more. */ +/** Full toolbar variant with all formatting options. Content action is hidden by default. */ export const Default: Story = { args: { variant: "full", @@ -75,3 +78,32 @@ export const WithLabel: Story = { placeholder: "Enter your description here...", }, }; + +/** Show the bottom-right content action button with default sparkles icon (no text). */ +export const ContentActionIconOnly: Story = { + args: { + variant: "full", + placeholder: "Icon-only content action...", + contentAction: { + show: true, + onClick: fn(), + }, + }, +}; + +/** Replace the default sparkles button with custom icon, label, and click handler. */ +export const ContentActionCustom: Story = { + args: { + variant: "full", + placeholder: "Try the custom AI action...", + contentAction: { + show: true, + title: "Rewrite with AI", + showContent: true, + icon: , + content: Rewrite, + className: "border border-border bg-background px-2", + onClick: fn(), + }, + }, +}; diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index 510ab30..70cd463 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -74,7 +74,11 @@ export { } from "./sortable"; export { Switch, LabeledSwitch, SwitchCard, type SwitchProps, type LabeledSwitchProps, type SwitchCardProps } from "./switch"; export { Textarea } from "./textarea"; -export { RichTextEditor, type RichTextEditorProps } from "./rich-text-editor"; +export { + RichTextEditor, + type RichTextEditorProps, + type RichTextEditorContentAction, +} from "./rich-text-editor"; export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"; export { Toggle } from './toggle' export { ToggleGroup, ToggleGroupItem } from './toggle-group' diff --git a/src/components/ui/rich-text-editor.tsx b/src/components/ui/rich-text-editor.tsx index 831379d..709a68d 100644 --- a/src/components/ui/rich-text-editor.tsx +++ b/src/components/ui/rich-text-editor.tsx @@ -18,6 +18,16 @@ import { import { cn } from "@/lib/utils"; +interface RichTextEditorContentAction { + show?: boolean; + showContent?: boolean; + title?: string; + icon?: React.ReactNode; + content?: React.ReactNode; + onClick?: () => void; + className?: string; +} + interface RichTextEditorProps { value?: string; defaultValue?: string; @@ -25,6 +35,7 @@ interface RichTextEditorProps { placeholder?: string; className?: string; variant?: "full" | "simple"; + contentAction?: RichTextEditorContentAction; } function RichTextEditor({ @@ -34,6 +45,7 @@ function RichTextEditor({ placeholder = "Start typing...", className = "", variant = "full", + contentAction, }: RichTextEditorProps) { const editorRef = React.useRef(null); const [fontSize, setFontSize] = React.useState("14 px"); @@ -90,6 +102,12 @@ function RichTextEditor({ executeCommand("formatBlock", formatMap[e.target.value] || "p"); }; + const showContentAction = contentAction?.show ?? false; + const showContentActionContent = contentAction?.showContent ?? false; + const contentActionIcon = contentAction?.icon === undefined + ? + : contentAction.icon; + return (
- + {showContentAction && ( + + )}