Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
4.7 kB ยท 193 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194"use client";
import { InputGroup, InputGroupAddon, InputGroupTextarea,} from "@openstatus/ui/components/ui/input-group";import { Tabs, TabsContent, TabsList, TabsTrigger,} from "@openstatus/ui/components/ui/tabs";import { cn } from "@openstatus/ui/lib/utils";
import { ProcessMessage } from "@/components/content/process-message";
/** * Markdown composer: write/preview tabs around a textarea, with a footer for * actions. `size="lg"` is for long-form documents (message, postmortem). */export function Composer({ children, className, size = "default", ...props}: React.ComponentProps<typeof Tabs> & { size?: "default" | "lg" }) { return ( <Tabs defaultValue="write" data-size={size} className={cn("min-w-0 flex-1", className)} {...props} > <InputGroup className="bg-background items-stretch overflow-hidden"> {children} </InputGroup> </Tabs> );}
/** Write/Preview tabs on the left; `children` replaces the "Markdown" hint on the right. */export function ComposerHeader({ children, className, ...props}: React.ComponentProps<typeof InputGroupAddon>) { return ( <InputGroupAddon align="block-start" className={cn( "justify-between border-b px-2 py-1.5 [.border-b]:pb-1.5", className, )} {...props} > <ComposerTabs /> {children ?? <ComposerHint>Markdown</ComposerHint>} </InputGroupAddon> );}
const tabsTriggerClassName = "text-muted-foreground hover:text-foreground data-[state=active]:text-foreground dark:data-[state=active]:bg-transparent h-7 flex-none data-[state=active]:bg-transparent data-[state=active]:shadow-none dark:data-[state=active]:border-transparent";
function ComposerTabs({ className, ...props}: Omit<React.ComponentProps<typeof TabsList>, "children">) { return ( <TabsList className={cn("h-auto rounded-none bg-transparent p-0", className)} {...props} > <TabsTrigger value="write" className={tabsTriggerClassName}> Write </TabsTrigger> <TabsTrigger value="preview" className={tabsTriggerClassName}> Preview </TabsTrigger> </TabsList> );}
export function ComposerHint({ children, className, ...props}: React.ComponentProps<"span">) { return ( <span data-slot="composer-hint" className={cn( "text-muted-foreground px-1 text-xs font-normal", className, )} {...props} > {children} </span> );}
/** `onSubmit` fires on Cmd/Ctrl+Enter. */export function ComposerTextarea({ className, onSubmit, onKeyDown, ...props}: React.ComponentProps<typeof InputGroupTextarea> & { onSubmit?: () => void;}) { return ( <TabsContent value="write"> <InputGroupTextarea className={cn("min-h-24 in-data-[size=lg]:min-h-96", className)} onKeyDown={(e) => { onKeyDown?.(e); if (e.defaultPrevented || e.nativeEvent.isComposing) return; if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && onSubmit) { e.preventDefault(); onSubmit(); } }} {...props} /> </TabsContent> );}
export function ComposerPreview({ value, className, ...props}: Omit<React.ComponentProps<"div">, "children"> & { value: string }) { return ( <TabsContent value="preview"> <div data-slot="composer-preview" className={cn( "prose prose-sm dark:prose-invert min-h-24 max-w-none px-3 py-3 in-data-[size=lg]:min-h-96", className, )} {...props} > {value.trim() ? ( <ProcessMessage value={value} /> ) : ( <p className="text-muted-foreground">Nothing to preview.</p> )} </div> </TabsContent> );}
/** Structured fields between the textarea and the footer. */export function ComposerSection({ children, className, ...props}: React.ComponentProps<typeof InputGroupAddon>) { return ( <InputGroupAddon align="block-end" className={cn( "flex-col items-stretch gap-2 border-t px-3 py-3 font-normal [.border-t]:pt-3", className, )} {...props} > {children} </InputGroupAddon> );}
export function ComposerFooter({ children, className, ...props}: React.ComponentProps<typeof InputGroupAddon>) { return ( <InputGroupAddon align="block-end" className={cn( "bg-muted/50 flex-wrap justify-between border-t px-2 py-2 font-normal [.border-t]:pt-2", className, )} {...props} > {children} </InputGroupAddon> );}