@groupher/rich-editor 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/components.json +21 -0
- package/dist/rich-editor.css +1 -0
- package/dist/rich-editor.es.js +60825 -0
- package/dist/rich-editor.umd.js +361 -0
- package/dist/vite.svg +1 -0
- package/eslint.config.js +26 -0
- package/index.html +13 -0
- package/package.json +54 -0
- package/public/vite.svg +1 -0
- package/src/RichEditor.tsx +102 -0
- package/src/assets/react.svg +1 -0
- package/src/components/editor/plate-editor.tsx +54 -0
- package/src/components/editor/plugins/basic-blocks-base-kit.tsx +35 -0
- package/src/components/editor/plugins/basic-blocks-kit.tsx +88 -0
- package/src/components/editor/plugins/basic-marks-base-kit.tsx +27 -0
- package/src/components/editor/plugins/basic-marks-kit.tsx +41 -0
- package/src/components/editor/plugins/basic-nodes-kit.tsx +6 -0
- package/src/components/ui/blockquote-node-static.tsx +11 -0
- package/src/components/ui/blockquote-node.tsx +13 -0
- package/src/components/ui/button.tsx +59 -0
- package/src/components/ui/code-node-static.tsx +15 -0
- package/src/components/ui/code-node.tsx +17 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/editor-static.tsx +53 -0
- package/src/components/ui/editor.tsx +129 -0
- package/src/components/ui/fixed-toolbar.tsx +17 -0
- package/src/components/ui/heading-node-static.tsx +66 -0
- package/src/components/ui/heading-node.tsx +58 -0
- package/src/components/ui/highlight-node-static.tsx +11 -0
- package/src/components/ui/highlight-node.tsx +13 -0
- package/src/components/ui/hr-node-static.tsx +20 -0
- package/src/components/ui/hr-node.tsx +33 -0
- package/src/components/ui/kbd-node-static.tsx +15 -0
- package/src/components/ui/kbd-node.tsx +17 -0
- package/src/components/ui/mark-toolbar-button.tsx +19 -0
- package/src/components/ui/paragraph-node-static.tsx +13 -0
- package/src/components/ui/paragraph-node.tsx +15 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/toolbar.tsx +389 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/global.css +235 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +11 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +31 -0
- package/tsconfig.json +10 -0
- package/tsconfig.node.json +25 -0
- package/vite.config.ts +40 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
3
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function DropdownMenu({
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
10
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuPortal({
|
|
14
|
+
...props
|
|
15
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
|
16
|
+
return (
|
|
17
|
+
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function DropdownMenuTrigger({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
24
|
+
return (
|
|
25
|
+
<DropdownMenuPrimitive.Trigger
|
|
26
|
+
data-slot="dropdown-menu-trigger"
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function DropdownMenuContent({
|
|
33
|
+
className,
|
|
34
|
+
sideOffset = 4,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
37
|
+
return (
|
|
38
|
+
<DropdownMenuPrimitive.Portal>
|
|
39
|
+
<DropdownMenuPrimitive.Content
|
|
40
|
+
data-slot="dropdown-menu-content"
|
|
41
|
+
sideOffset={sideOffset}
|
|
42
|
+
className={cn(
|
|
43
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
</DropdownMenuPrimitive.Portal>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function DropdownMenuGroup({
|
|
53
|
+
...props
|
|
54
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
55
|
+
return (
|
|
56
|
+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function DropdownMenuItem({
|
|
61
|
+
className,
|
|
62
|
+
inset,
|
|
63
|
+
variant = "default",
|
|
64
|
+
...props
|
|
65
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
|
66
|
+
inset?: boolean
|
|
67
|
+
variant?: "default" | "destructive"
|
|
68
|
+
}) {
|
|
69
|
+
return (
|
|
70
|
+
<DropdownMenuPrimitive.Item
|
|
71
|
+
data-slot="dropdown-menu-item"
|
|
72
|
+
data-inset={inset}
|
|
73
|
+
data-variant={variant}
|
|
74
|
+
className={cn(
|
|
75
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function DropdownMenuCheckboxItem({
|
|
84
|
+
className,
|
|
85
|
+
children,
|
|
86
|
+
checked,
|
|
87
|
+
...props
|
|
88
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
|
89
|
+
return (
|
|
90
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
91
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
92
|
+
className={cn(
|
|
93
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
checked={checked}
|
|
97
|
+
{...props}
|
|
98
|
+
>
|
|
99
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
100
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
101
|
+
<CheckIcon className="size-4" />
|
|
102
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
103
|
+
</span>
|
|
104
|
+
{children}
|
|
105
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function DropdownMenuRadioGroup({
|
|
110
|
+
...props
|
|
111
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
|
112
|
+
return (
|
|
113
|
+
<DropdownMenuPrimitive.RadioGroup
|
|
114
|
+
data-slot="dropdown-menu-radio-group"
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function DropdownMenuRadioItem({
|
|
121
|
+
className,
|
|
122
|
+
children,
|
|
123
|
+
...props
|
|
124
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
|
125
|
+
return (
|
|
126
|
+
<DropdownMenuPrimitive.RadioItem
|
|
127
|
+
data-slot="dropdown-menu-radio-item"
|
|
128
|
+
className={cn(
|
|
129
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
130
|
+
className
|
|
131
|
+
)}
|
|
132
|
+
{...props}
|
|
133
|
+
>
|
|
134
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
135
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
136
|
+
<CircleIcon className="size-2 fill-current" />
|
|
137
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
138
|
+
</span>
|
|
139
|
+
{children}
|
|
140
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function DropdownMenuLabel({
|
|
145
|
+
className,
|
|
146
|
+
inset,
|
|
147
|
+
...props
|
|
148
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
149
|
+
inset?: boolean
|
|
150
|
+
}) {
|
|
151
|
+
return (
|
|
152
|
+
<DropdownMenuPrimitive.Label
|
|
153
|
+
data-slot="dropdown-menu-label"
|
|
154
|
+
data-inset={inset}
|
|
155
|
+
className={cn(
|
|
156
|
+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
157
|
+
className
|
|
158
|
+
)}
|
|
159
|
+
{...props}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function DropdownMenuSeparator({
|
|
165
|
+
className,
|
|
166
|
+
...props
|
|
167
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
168
|
+
return (
|
|
169
|
+
<DropdownMenuPrimitive.Separator
|
|
170
|
+
data-slot="dropdown-menu-separator"
|
|
171
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
172
|
+
{...props}
|
|
173
|
+
/>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function DropdownMenuShortcut({
|
|
178
|
+
className,
|
|
179
|
+
...props
|
|
180
|
+
}: React.ComponentProps<"span">) {
|
|
181
|
+
return (
|
|
182
|
+
<span
|
|
183
|
+
data-slot="dropdown-menu-shortcut"
|
|
184
|
+
className={cn(
|
|
185
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
186
|
+
className
|
|
187
|
+
)}
|
|
188
|
+
{...props}
|
|
189
|
+
/>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function DropdownMenuSub({
|
|
194
|
+
...props
|
|
195
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
196
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function DropdownMenuSubTrigger({
|
|
200
|
+
className,
|
|
201
|
+
inset,
|
|
202
|
+
children,
|
|
203
|
+
...props
|
|
204
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
205
|
+
inset?: boolean
|
|
206
|
+
}) {
|
|
207
|
+
return (
|
|
208
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
209
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
210
|
+
data-inset={inset}
|
|
211
|
+
className={cn(
|
|
212
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
|
|
213
|
+
className
|
|
214
|
+
)}
|
|
215
|
+
{...props}
|
|
216
|
+
>
|
|
217
|
+
{children}
|
|
218
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
219
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function DropdownMenuSubContent({
|
|
224
|
+
className,
|
|
225
|
+
...props
|
|
226
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
227
|
+
return (
|
|
228
|
+
<DropdownMenuPrimitive.SubContent
|
|
229
|
+
data-slot="dropdown-menu-sub-content"
|
|
230
|
+
className={cn(
|
|
231
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
232
|
+
className
|
|
233
|
+
)}
|
|
234
|
+
{...props}
|
|
235
|
+
/>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export {
|
|
240
|
+
DropdownMenu,
|
|
241
|
+
DropdownMenuPortal,
|
|
242
|
+
DropdownMenuTrigger,
|
|
243
|
+
DropdownMenuContent,
|
|
244
|
+
DropdownMenuGroup,
|
|
245
|
+
DropdownMenuLabel,
|
|
246
|
+
DropdownMenuItem,
|
|
247
|
+
DropdownMenuCheckboxItem,
|
|
248
|
+
DropdownMenuRadioGroup,
|
|
249
|
+
DropdownMenuRadioItem,
|
|
250
|
+
DropdownMenuSeparator,
|
|
251
|
+
DropdownMenuShortcut,
|
|
252
|
+
DropdownMenuSub,
|
|
253
|
+
DropdownMenuSubTrigger,
|
|
254
|
+
DropdownMenuSubContent,
|
|
255
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { type PlateStaticProps, PlateStatic } from 'platejs';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
export const editorVariants = cva(
|
|
9
|
+
cn(
|
|
10
|
+
'group/editor',
|
|
11
|
+
'relative w-full cursor-text overflow-x-hidden break-words whitespace-pre-wrap select-text',
|
|
12
|
+
'rounded-md ring-offset-background focus-visible:outline-none',
|
|
13
|
+
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:top-[auto_!important] **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
|
|
14
|
+
'[&_strong]:font-bold'
|
|
15
|
+
),
|
|
16
|
+
{
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: 'none',
|
|
19
|
+
},
|
|
20
|
+
variants: {
|
|
21
|
+
disabled: {
|
|
22
|
+
true: 'cursor-not-allowed opacity-50',
|
|
23
|
+
},
|
|
24
|
+
focused: {
|
|
25
|
+
true: 'ring-2 ring-ring ring-offset-2',
|
|
26
|
+
},
|
|
27
|
+
variant: {
|
|
28
|
+
ai: 'w-full px-0 text-base md:text-sm',
|
|
29
|
+
aiChat:
|
|
30
|
+
'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-5 py-3 text-base md:text-sm',
|
|
31
|
+
default:
|
|
32
|
+
'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
|
|
33
|
+
demo: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
|
|
34
|
+
fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24',
|
|
35
|
+
none: '',
|
|
36
|
+
select: 'px-3 py-2 text-base data-readonly:w-fit',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export function EditorStatic({
|
|
43
|
+
className,
|
|
44
|
+
variant,
|
|
45
|
+
...props
|
|
46
|
+
}: PlateStaticProps & VariantProps<typeof editorVariants>) {
|
|
47
|
+
return (
|
|
48
|
+
<PlateStatic
|
|
49
|
+
className={cn(editorVariants({ variant }), className)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
6
|
+
import type { PlateContentProps, PlateViewProps } from 'platejs/react';
|
|
7
|
+
|
|
8
|
+
import { cva } from 'class-variance-authority';
|
|
9
|
+
import { PlateContainer, PlateContent, PlateView } from 'platejs/react';
|
|
10
|
+
|
|
11
|
+
import { cn } from '@/lib/utils';
|
|
12
|
+
|
|
13
|
+
const editorContainerVariants = cva(
|
|
14
|
+
'relative w-full cursor-text overflow-y-auto caret-primary select-text selection:bg-brand/25 focus-visible:outline-none [&_.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 py-0.5 text-sm',
|
|
23
|
+
'rounded-md border-[1.5px] border-transparent bg-transparent',
|
|
24
|
+
'has-[[data-slate-editor]:focus]:border-brand/50 has-[[data-slate-editor]:focus]:ring-2 has-[[data-slate-editor]:focus]:ring-brand/30',
|
|
25
|
+
'has-aria-disabled:border-input has-aria-disabled:bg-muted'
|
|
26
|
+
),
|
|
27
|
+
default: 'h-full',
|
|
28
|
+
demo: 'h-[650px]',
|
|
29
|
+
select: cn(
|
|
30
|
+
'group rounded-md border border-input ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2',
|
|
31
|
+
'has-data-readonly:w-fit has-data-readonly:cursor-default has-data-readonly:border-transparent has-data-readonly:focus-within:[box-shadow:none]'
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export function EditorContainer({
|
|
39
|
+
className,
|
|
40
|
+
variant,
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof editorContainerVariants>) {
|
|
43
|
+
return (
|
|
44
|
+
<PlateContainer
|
|
45
|
+
className={cn(
|
|
46
|
+
'ignore-click-outside/toolbar',
|
|
47
|
+
editorContainerVariants({ variant }),
|
|
48
|
+
className
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const editorVariants = cva(
|
|
56
|
+
cn(
|
|
57
|
+
'group/editor',
|
|
58
|
+
'relative w-full cursor-text overflow-x-hidden break-words whitespace-pre-wrap select-text',
|
|
59
|
+
'rounded-md ring-offset-background focus-visible:outline-none',
|
|
60
|
+
'placeholder:text-muted-foreground/80 **:data-slate-placeholder:!top-1/2 **:data-slate-placeholder:-translate-y-1/2 **:data-slate-placeholder:text-muted-foreground/80 **:data-slate-placeholder:opacity-100!',
|
|
61
|
+
'[&_strong]:font-bold'
|
|
62
|
+
),
|
|
63
|
+
{
|
|
64
|
+
defaultVariants: {
|
|
65
|
+
variant: 'default',
|
|
66
|
+
},
|
|
67
|
+
variants: {
|
|
68
|
+
disabled: {
|
|
69
|
+
true: 'cursor-not-allowed opacity-50',
|
|
70
|
+
},
|
|
71
|
+
focused: {
|
|
72
|
+
true: 'ring-2 ring-ring ring-offset-2',
|
|
73
|
+
},
|
|
74
|
+
variant: {
|
|
75
|
+
ai: 'w-full px-0 text-base md:text-sm',
|
|
76
|
+
aiChat:
|
|
77
|
+
'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-base md:text-sm',
|
|
78
|
+
comment: cn('rounded-none border-none bg-transparent text-sm'),
|
|
79
|
+
default:
|
|
80
|
+
'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
|
|
81
|
+
demo: 'size-full px-16 pt-4 pb-72 text-base sm:px-[max(64px,calc(50%-350px))]',
|
|
82
|
+
fullWidth: 'size-full px-16 pt-4 pb-72 text-base sm:px-24',
|
|
83
|
+
none: '',
|
|
84
|
+
select: 'px-3 py-2 text-base data-readonly:w-fit',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export type EditorProps = PlateContentProps &
|
|
91
|
+
VariantProps<typeof editorVariants>;
|
|
92
|
+
|
|
93
|
+
export const Editor = React.forwardRef<HTMLDivElement, EditorProps>(
|
|
94
|
+
({ className, disabled, focused, variant, ...props }, ref) => {
|
|
95
|
+
return (
|
|
96
|
+
<PlateContent
|
|
97
|
+
ref={ref}
|
|
98
|
+
className={cn(
|
|
99
|
+
editorVariants({
|
|
100
|
+
disabled,
|
|
101
|
+
focused,
|
|
102
|
+
variant,
|
|
103
|
+
}),
|
|
104
|
+
className
|
|
105
|
+
)}
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
disableDefaultStyles
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
Editor.displayName = 'Editor';
|
|
115
|
+
|
|
116
|
+
export function EditorView({
|
|
117
|
+
className,
|
|
118
|
+
variant,
|
|
119
|
+
...props
|
|
120
|
+
}: PlateViewProps & VariantProps<typeof editorVariants>) {
|
|
121
|
+
return (
|
|
122
|
+
<PlateView
|
|
123
|
+
{...props}
|
|
124
|
+
className={cn(editorVariants({ variant }), className)}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
EditorView.displayName = 'EditorView';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
import { Toolbar } from './toolbar';
|
|
6
|
+
|
|
7
|
+
export function FixedToolbar(props: React.ComponentProps<typeof Toolbar>) {
|
|
8
|
+
return (
|
|
9
|
+
<Toolbar
|
|
10
|
+
{...props}
|
|
11
|
+
className={cn(
|
|
12
|
+
'sticky top-0 left-0 z-50 scrollbar-hide w-full justify-between overflow-x-auto rounded-t-lg border-b border-b-border bg-background/95 p-1 backdrop-blur-sm supports-backdrop-blur:bg-background/60',
|
|
13
|
+
props.className
|
|
14
|
+
)}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SlateElementProps } from 'platejs';
|
|
2
|
+
|
|
3
|
+
import { type VariantProps, cva } from 'class-variance-authority';
|
|
4
|
+
import { SlateElement } from 'platejs';
|
|
5
|
+
|
|
6
|
+
const headingVariants = cva('relative mb-1', {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
h1: 'mt-[1.6em] pb-1 font-heading text-4xl font-bold',
|
|
10
|
+
h2: 'mt-[1.4em] pb-px font-heading text-2xl font-semibold tracking-tight',
|
|
11
|
+
h3: 'mt-[1em] pb-px font-heading text-xl font-semibold tracking-tight',
|
|
12
|
+
h4: 'mt-[0.75em] font-heading text-lg font-semibold tracking-tight',
|
|
13
|
+
h5: 'mt-[0.75em] text-lg font-semibold tracking-tight',
|
|
14
|
+
h6: 'mt-[0.75em] text-base font-semibold tracking-tight',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export function HeadingElementStatic({
|
|
20
|
+
variant = 'h1',
|
|
21
|
+
...props
|
|
22
|
+
}: SlateElementProps & VariantProps<typeof headingVariants>) {
|
|
23
|
+
return (
|
|
24
|
+
<SlateElement
|
|
25
|
+
as={variant!}
|
|
26
|
+
className={headingVariants({ variant })}
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
{props.children}
|
|
30
|
+
</SlateElement>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function H1ElementStatic(props: SlateElementProps) {
|
|
35
|
+
return <HeadingElementStatic variant="h1" {...props} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function H2ElementStatic(
|
|
39
|
+
props: React.ComponentProps<typeof HeadingElementStatic>
|
|
40
|
+
) {
|
|
41
|
+
return <HeadingElementStatic variant="h2" {...props} />;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function H3ElementStatic(
|
|
45
|
+
props: React.ComponentProps<typeof HeadingElementStatic>
|
|
46
|
+
) {
|
|
47
|
+
return <HeadingElementStatic variant="h3" {...props} />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function H4ElementStatic(
|
|
51
|
+
props: React.ComponentProps<typeof HeadingElementStatic>
|
|
52
|
+
) {
|
|
53
|
+
return <HeadingElementStatic variant="h4" {...props} />;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function H5ElementStatic(
|
|
57
|
+
props: React.ComponentProps<typeof HeadingElementStatic>
|
|
58
|
+
) {
|
|
59
|
+
return <HeadingElementStatic variant="h5" {...props} />;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function H6ElementStatic(
|
|
63
|
+
props: React.ComponentProps<typeof HeadingElementStatic>
|
|
64
|
+
) {
|
|
65
|
+
return <HeadingElementStatic variant="h6" {...props} />;
|
|
66
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { PlateElementProps } from 'platejs/react';
|
|
4
|
+
|
|
5
|
+
import { type VariantProps, cva } from 'class-variance-authority';
|
|
6
|
+
import { PlateElement } from 'platejs/react';
|
|
7
|
+
|
|
8
|
+
const headingVariants = cva('relative mb-1', {
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
h1: 'mt-[1.6em] pb-1 font-heading text-4xl font-bold',
|
|
12
|
+
h2: 'mt-[1.4em] pb-px font-heading text-2xl font-semibold tracking-tight',
|
|
13
|
+
h3: 'mt-[1em] pb-px font-heading text-xl font-semibold tracking-tight',
|
|
14
|
+
h4: 'mt-[0.75em] font-heading text-lg font-semibold tracking-tight',
|
|
15
|
+
h5: 'mt-[0.75em] text-lg font-semibold tracking-tight',
|
|
16
|
+
h6: 'mt-[0.75em] text-base font-semibold tracking-tight',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export function HeadingElement({
|
|
22
|
+
variant = 'h1',
|
|
23
|
+
...props
|
|
24
|
+
}: PlateElementProps & VariantProps<typeof headingVariants>) {
|
|
25
|
+
return (
|
|
26
|
+
<PlateElement
|
|
27
|
+
as={variant!}
|
|
28
|
+
className={headingVariants({ variant })}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
{props.children}
|
|
32
|
+
</PlateElement>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function H1Element(props: PlateElementProps) {
|
|
37
|
+
return <HeadingElement variant="h1" {...props} />;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function H2Element(props: PlateElementProps) {
|
|
41
|
+
return <HeadingElement variant="h2" {...props} />;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function H3Element(props: PlateElementProps) {
|
|
45
|
+
return <HeadingElement variant="h3" {...props} />;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function H4Element(props: PlateElementProps) {
|
|
49
|
+
return <HeadingElement variant="h4" {...props} />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function H5Element(props: PlateElementProps) {
|
|
53
|
+
return <HeadingElement variant="h5" {...props} />;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function H6Element(props: PlateElementProps) {
|
|
57
|
+
return <HeadingElement variant="h6" {...props} />;
|
|
58
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SlateLeafProps } from 'platejs';
|
|
2
|
+
|
|
3
|
+
import { SlateLeaf } from 'platejs';
|
|
4
|
+
|
|
5
|
+
export function HighlightLeafStatic(props: SlateLeafProps) {
|
|
6
|
+
return (
|
|
7
|
+
<SlateLeaf {...props} as="mark" className="bg-highlight/30 text-inherit">
|
|
8
|
+
{props.children}
|
|
9
|
+
</SlateLeaf>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { PlateLeafProps } from 'platejs/react';
|
|
4
|
+
|
|
5
|
+
import { PlateLeaf } from 'platejs/react';
|
|
6
|
+
|
|
7
|
+
export function HighlightLeaf(props: PlateLeafProps) {
|
|
8
|
+
return (
|
|
9
|
+
<PlateLeaf {...props} as="mark" className="bg-highlight/30 text-inherit">
|
|
10
|
+
{props.children}
|
|
11
|
+
</PlateLeaf>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SlateElementProps } from 'platejs';
|
|
2
|
+
|
|
3
|
+
import { SlateElement } from 'platejs';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/lib/utils';
|
|
6
|
+
|
|
7
|
+
export function HrElementStatic(props: SlateElementProps) {
|
|
8
|
+
return (
|
|
9
|
+
<SlateElement {...props}>
|
|
10
|
+
<div className="cursor-text py-6" contentEditable={false}>
|
|
11
|
+
<hr
|
|
12
|
+
className={cn(
|
|
13
|
+
'h-0.5 rounded-sm border-none bg-muted bg-clip-content'
|
|
14
|
+
)}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
{props.children}
|
|
18
|
+
</SlateElement>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { PlateElementProps } from 'platejs/react';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
PlateElement,
|
|
7
|
+
useFocused,
|
|
8
|
+
useReadOnly,
|
|
9
|
+
useSelected,
|
|
10
|
+
} from 'platejs/react';
|
|
11
|
+
|
|
12
|
+
import { cn } from '@/lib/utils';
|
|
13
|
+
|
|
14
|
+
export function HrElement(props: PlateElementProps) {
|
|
15
|
+
const readOnly = useReadOnly();
|
|
16
|
+
const selected = useSelected();
|
|
17
|
+
const focused = useFocused();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<PlateElement {...props}>
|
|
21
|
+
<div className="py-6" contentEditable={false}>
|
|
22
|
+
<hr
|
|
23
|
+
className={cn(
|
|
24
|
+
'h-0.5 rounded-sm border-none bg-muted bg-clip-content',
|
|
25
|
+
selected && focused && 'ring-2 ring-ring ring-offset-2',
|
|
26
|
+
!readOnly && 'cursor-pointer'
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
{props.children}
|
|
31
|
+
</PlateElement>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SlateLeafProps } from 'platejs';
|
|
2
|
+
|
|
3
|
+
import { SlateLeaf } from 'platejs';
|
|
4
|
+
|
|
5
|
+
export function KbdLeafStatic(props: SlateLeafProps) {
|
|
6
|
+
return (
|
|
7
|
+
<SlateLeaf
|
|
8
|
+
{...props}
|
|
9
|
+
as="kbd"
|
|
10
|
+
className="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-sm shadow-[rgba(255,_255,_255,_0.1)_0px_0.5px_0px_0px_inset,_rgb(248,_249,_250)_0px_1px_5px_0px_inset,_rgb(193,_200,_205)_0px_0px_0px_0.5px,_rgb(193,_200,_205)_0px_2px_1px_-1px,_rgb(193,_200,_205)_0px_1px_0px_0px] dark:shadow-[rgba(255,_255,_255,_0.1)_0px_0.5px_0px_0px_inset,_rgb(26,_29,_30)_0px_1px_5px_0px_inset,_rgb(76,_81,_85)_0px_0px_0px_0.5px,_rgb(76,_81,_85)_0px_2px_1px_-1px,_rgb(76,_81,_85)_0px_1px_0px_0px]"
|
|
11
|
+
>
|
|
12
|
+
{props.children}
|
|
13
|
+
</SlateLeaf>
|
|
14
|
+
);
|
|
15
|
+
}
|