| 1 | "use client"; |
| 2 | |
| 3 | import { cva, type VariantProps } from "class-variance-authority"; |
| 4 | import { |
| 5 | PlateContainer, |
| 6 | PlateContent, |
| 7 | type PlateContentProps, |
| 8 | } from "platejs/react"; |
| 9 | import type * as React from "react"; |
| 10 | |
| 11 | import { cn } from "@/lib/utils"; |
| 12 | |
| 13 | const editorContainerVariants = cva( |
| 14 | "relative w-full cursor-text caret-primary select-text selection:bg-brand/25 focus-visible:outline-hidden [&_.slate-selection-area]:z-50 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15", |
| 15 | { |
| 16 | defaultVariants: { |
| 17 | variant: "default", |
| 18 | }, |
| 19 | variants: { |
| 20 | variant: { |
| 21 | comment: cn( |
| 22 | "flex flex-wrap justify-between gap-1 px-1.5 py-1 text-sm", |
| 23 | "rounded-lg border border-transparent bg-transparent", |
| 24 | "has-[[data-slate-editor]:focus]:border-border/50 has-[[data-slate-editor]:focus]:bg-muted/20", |
| 25 | "has-aria-disabled:border-input has-aria-disabled:bg-muted", |
| 26 | ), |
| 27 | default: "h-full overflow-y-auto", |
| 28 | demo: "h-162.5 overflow-y-auto", |
| 29 | // Notes variant: no overflow, let parent handle scrolling |
| 30 | notes: "", |
| 31 | select: cn( |
| 32 | "group rounded-md border border-input ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2", |
| 33 | "has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]", |
| 34 | ), |
| 35 | }, |
| 36 | }, |
| 37 | }, |
| 38 | ); |
| 39 | |
| 40 | export function EditorContainer({ |
| 41 | className, |
| 42 | variant, |
| 43 | ...props |
| 44 | }: React.ComponentProps<"div"> & VariantProps<typeof editorContainerVariants>) { |
| 45 | return ( |
| 46 | <PlateContainer |
| 47 | data-print-editor-surface="true" |
| 48 | className={cn( |
| 49 | // "ignore-click-outside/toolbar", |
| 50 | editorContainerVariants({ variant }), |
| 51 | className, |
| 52 | )} |
| 53 | {...props} |
| 54 | /> |
| 55 | ); |
| 56 | } |
| 57 | |
| 58 | const editorVariants = cva( |
| 59 | cn( |
| 60 | "group/editor", |
| 61 | "relative w-full cursor-text overflow-x-hidden wrap-break-word whitespace-pre-wrap select-text", |
| 62 | "rounded-md ring-offset-background focus-visible:outline-hidden", |
| 63 | "placeholder:text-muted-foreground/80 data-slate-placeholder:**:top-[auto_!important] data-slate-placeholder:**:text-muted-foreground/80 data-slate-placeholder:**:opacity-100!", |
| 64 | "[&_strong]:font-bold", |
| 65 | ), |
| 66 | { |
| 67 | defaultVariants: { |
| 68 | variant: "default", |
| 69 | }, |
| 70 | variants: { |
| 71 | disabled: { |
| 72 | true: "cursor-not-allowed opacity-50", |
| 73 | }, |
| 74 | focused: { |
| 75 | true: "ring-2 ring-ring ring-offset-2", |
| 76 | }, |
| 77 | variant: { |
| 78 | ghost: "", |
| 79 | allweone: "size-full pt-4 pb-72 text-base", |
| 80 | ai: "w-full px-0 text-base md:text-sm", |
| 81 | aiChat: |
| 82 | "max-h-[min(70vh,320px)] w-full max-w-175 overflow-y-auto px-3 py-2 text-base md:text-sm", |
| 83 | comment: cn("rounded-none border-none bg-transparent text-sm"), |
| 84 | default: |
| 85 | "size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]", |
| 86 | demo: "size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]", |
| 87 | fullWidth: "size-full px-16 pt-4 pb-72 text-base sm:px-24", |
| 88 | none: "", |
| 89 | select: "px-3 py-2 text-base data-readonly:w-fit", |
| 90 | }, |
| 91 | }, |
| 92 | }, |
| 93 | ); |
| 94 | |
| 95 | export type EditorProps = PlateContentProps & |
| 96 | VariantProps<typeof editorVariants>; |
| 97 | |
| 98 | export const Editor = ({ |
| 99 | className, |
| 100 | disabled, |
| 101 | focused, |
| 102 | variant, |
| 103 | ref, |
| 104 | ...props |
| 105 | }: EditorProps & React.RefAttributes<HTMLDivElement>) => { |
| 106 | return ( |
| 107 | <PlateContent |
| 108 | ref={ref} |
| 109 | className={cn( |
| 110 | editorVariants({ |
| 111 | disabled, |
| 112 | focused, |
| 113 | variant, |
| 114 | }), |
| 115 | className, |
| 116 | )} |
| 117 | disabled={disabled} |
| 118 | disableDefaultStyles |
| 119 | {...props} |
| 120 | /> |
| 121 | ); |
| 122 | }; |
| 123 | |
| 124 | Editor.displayName = "Editor"; |
| 125 |