@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.
Files changed (49) hide show
  1. package/README.md +69 -0
  2. package/components.json +21 -0
  3. package/dist/rich-editor.css +1 -0
  4. package/dist/rich-editor.es.js +60825 -0
  5. package/dist/rich-editor.umd.js +361 -0
  6. package/dist/vite.svg +1 -0
  7. package/eslint.config.js +26 -0
  8. package/index.html +13 -0
  9. package/package.json +54 -0
  10. package/public/vite.svg +1 -0
  11. package/src/RichEditor.tsx +102 -0
  12. package/src/assets/react.svg +1 -0
  13. package/src/components/editor/plate-editor.tsx +54 -0
  14. package/src/components/editor/plugins/basic-blocks-base-kit.tsx +35 -0
  15. package/src/components/editor/plugins/basic-blocks-kit.tsx +88 -0
  16. package/src/components/editor/plugins/basic-marks-base-kit.tsx +27 -0
  17. package/src/components/editor/plugins/basic-marks-kit.tsx +41 -0
  18. package/src/components/editor/plugins/basic-nodes-kit.tsx +6 -0
  19. package/src/components/ui/blockquote-node-static.tsx +11 -0
  20. package/src/components/ui/blockquote-node.tsx +13 -0
  21. package/src/components/ui/button.tsx +59 -0
  22. package/src/components/ui/code-node-static.tsx +15 -0
  23. package/src/components/ui/code-node.tsx +17 -0
  24. package/src/components/ui/dropdown-menu.tsx +255 -0
  25. package/src/components/ui/editor-static.tsx +53 -0
  26. package/src/components/ui/editor.tsx +129 -0
  27. package/src/components/ui/fixed-toolbar.tsx +17 -0
  28. package/src/components/ui/heading-node-static.tsx +66 -0
  29. package/src/components/ui/heading-node.tsx +58 -0
  30. package/src/components/ui/highlight-node-static.tsx +11 -0
  31. package/src/components/ui/highlight-node.tsx +13 -0
  32. package/src/components/ui/hr-node-static.tsx +20 -0
  33. package/src/components/ui/hr-node.tsx +33 -0
  34. package/src/components/ui/kbd-node-static.tsx +15 -0
  35. package/src/components/ui/kbd-node.tsx +17 -0
  36. package/src/components/ui/mark-toolbar-button.tsx +19 -0
  37. package/src/components/ui/paragraph-node-static.tsx +13 -0
  38. package/src/components/ui/paragraph-node.tsx +15 -0
  39. package/src/components/ui/separator.tsx +28 -0
  40. package/src/components/ui/toolbar.tsx +389 -0
  41. package/src/components/ui/tooltip.tsx +59 -0
  42. package/src/global.css +235 -0
  43. package/src/lib/utils.ts +6 -0
  44. package/src/main.tsx +11 -0
  45. package/src/vite-env.d.ts +1 -0
  46. package/tsconfig.app.json +31 -0
  47. package/tsconfig.json +10 -0
  48. package/tsconfig.node.json +25 -0
  49. package/vite.config.ts +40 -0
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+
3
+ import type { PlateLeafProps } from 'platejs/react';
4
+
5
+ import { PlateLeaf } from 'platejs/react';
6
+
7
+ export function KbdLeaf(props: PlateLeafProps) {
8
+ return (
9
+ <PlateLeaf
10
+ {...props}
11
+ as="kbd"
12
+ 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]"
13
+ >
14
+ {props.children}
15
+ </PlateLeaf>
16
+ );
17
+ }
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+
3
+ import { useMarkToolbarButton, useMarkToolbarButtonState } from 'platejs/react';
4
+
5
+ import { ToolbarButton } from './toolbar';
6
+
7
+ export function MarkToolbarButton({
8
+ clear,
9
+ nodeType,
10
+ ...props
11
+ }: React.ComponentProps<typeof ToolbarButton> & {
12
+ nodeType: string;
13
+ clear?: string[] | string;
14
+ }) {
15
+ const state = useMarkToolbarButtonState({ clear, nodeType });
16
+ const { props: buttonProps } = useMarkToolbarButton(state);
17
+
18
+ return <ToolbarButton {...props} {...buttonProps} />;
19
+ }
@@ -0,0 +1,13 @@
1
+ import type { SlateElementProps } from 'platejs';
2
+
3
+ import { SlateElement } from 'platejs';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ export function ParagraphElementStatic(props: SlateElementProps) {
8
+ return (
9
+ <SlateElement {...props} className={cn('m-0 px-0 py-1')}>
10
+ {props.children}
11
+ </SlateElement>
12
+ );
13
+ }
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import type { PlateElementProps } from 'platejs/react';
4
+
5
+ import { PlateElement } from 'platejs/react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ export function ParagraphElement(props: PlateElementProps) {
10
+ return (
11
+ <PlateElement {...props} className={cn('m-0 px-0 py-1')}>
12
+ {props.children}
13
+ </PlateElement>
14
+ );
15
+ }
@@ -0,0 +1,28 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Separator({
9
+ className,
10
+ orientation = "horizontal",
11
+ decorative = true,
12
+ ...props
13
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
14
+ return (
15
+ <SeparatorPrimitive.Root
16
+ data-slot="separator"
17
+ decorative={decorative}
18
+ orientation={orientation}
19
+ className={cn(
20
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
21
+ className
22
+ )}
23
+ {...props}
24
+ />
25
+ )
26
+ }
27
+
28
+ export { Separator }
@@ -0,0 +1,389 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+
5
+ import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
6
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
7
+ import { type VariantProps, cva } from 'class-variance-authority';
8
+ import { ChevronDown } from 'lucide-react';
9
+
10
+ import {
11
+ DropdownMenuLabel,
12
+ DropdownMenuRadioGroup,
13
+ DropdownMenuSeparator,
14
+ } from '@/components/ui/dropdown-menu';
15
+ import { Separator } from '@/components/ui/separator';
16
+ import { Tooltip, TooltipTrigger } from '@/components/ui/tooltip';
17
+ import { cn } from '@/lib/utils';
18
+
19
+ export function Toolbar({
20
+ className,
21
+ ...props
22
+ }: React.ComponentProps<typeof ToolbarPrimitive.Root>) {
23
+ return (
24
+ <ToolbarPrimitive.Root
25
+ className={cn('relative flex items-center select-none', className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ export function ToolbarToggleGroup({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof ToolbarPrimitive.ToolbarToggleGroup>) {
35
+ return (
36
+ <ToolbarPrimitive.ToolbarToggleGroup
37
+ className={cn('flex items-center', className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ export function ToolbarLink({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<typeof ToolbarPrimitive.Link>) {
47
+ return (
48
+ <ToolbarPrimitive.Link
49
+ className={cn('font-medium underline underline-offset-4', className)}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export function ToolbarSeparator({
56
+ className,
57
+ ...props
58
+ }: React.ComponentProps<typeof ToolbarPrimitive.Separator>) {
59
+ return (
60
+ <ToolbarPrimitive.Separator
61
+ className={cn('mx-2 my-1 w-px shrink-0 bg-border', className)}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ // From toggleVariants
68
+ const toolbarButtonVariants = cva(
69
+ "inline-flex cursor-pointer items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-checked:bg-accent aria-checked:text-accent-foreground aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
70
+ {
71
+ defaultVariants: {
72
+ size: 'default',
73
+ variant: 'default',
74
+ },
75
+ variants: {
76
+ size: {
77
+ default: 'h-9 min-w-9 px-2',
78
+ lg: 'h-10 min-w-10 px-2.5',
79
+ sm: 'h-8 min-w-8 px-1.5',
80
+ },
81
+ variant: {
82
+ default: 'bg-transparent',
83
+ outline:
84
+ 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground',
85
+ },
86
+ },
87
+ }
88
+ );
89
+
90
+ const dropdownArrowVariants = cva(
91
+ cn(
92
+ 'inline-flex items-center justify-center rounded-r-md text-sm font-medium text-foreground transition-colors disabled:pointer-events-none disabled:opacity-50'
93
+ ),
94
+ {
95
+ defaultVariants: {
96
+ size: 'sm',
97
+ variant: 'default',
98
+ },
99
+ variants: {
100
+ size: {
101
+ default: 'h-9 w-6',
102
+ lg: 'h-10 w-8',
103
+ sm: 'h-8 w-4',
104
+ },
105
+ variant: {
106
+ default:
107
+ 'bg-transparent hover:bg-muted hover:text-muted-foreground aria-checked:bg-accent aria-checked:text-accent-foreground',
108
+ outline:
109
+ 'border border-l-0 border-input bg-transparent hover:bg-accent hover:text-accent-foreground',
110
+ },
111
+ },
112
+ }
113
+ );
114
+
115
+ type ToolbarButtonProps = {
116
+ isDropdown?: boolean;
117
+ pressed?: boolean;
118
+ } & Omit<
119
+ React.ComponentPropsWithoutRef<typeof ToolbarToggleItem>,
120
+ 'asChild' | 'value'
121
+ > &
122
+ VariantProps<typeof toolbarButtonVariants>;
123
+
124
+ export const ToolbarButton = withTooltip(function ToolbarButton({
125
+ children,
126
+ className,
127
+ isDropdown,
128
+ pressed,
129
+ size = 'sm',
130
+ variant,
131
+ ...props
132
+ }: ToolbarButtonProps) {
133
+ return typeof pressed === 'boolean' ? (
134
+ <ToolbarToggleGroup disabled={props.disabled} value="single" type="single">
135
+ <ToolbarToggleItem
136
+ className={cn(
137
+ toolbarButtonVariants({
138
+ size,
139
+ variant,
140
+ }),
141
+ isDropdown && 'justify-between gap-1 pr-1',
142
+ className
143
+ )}
144
+ value={pressed ? 'single' : ''}
145
+ {...props}
146
+ >
147
+ {isDropdown ? (
148
+ <>
149
+ <div className="flex flex-1 items-center gap-2 whitespace-nowrap">
150
+ {children}
151
+ </div>
152
+ <div>
153
+ <ChevronDown
154
+ className="size-3.5 text-muted-foreground"
155
+ data-icon
156
+ />
157
+ </div>
158
+ </>
159
+ ) : (
160
+ children
161
+ )}
162
+ </ToolbarToggleItem>
163
+ </ToolbarToggleGroup>
164
+ ) : (
165
+ <ToolbarPrimitive.Button
166
+ className={cn(
167
+ toolbarButtonVariants({
168
+ size,
169
+ variant,
170
+ }),
171
+ isDropdown && 'pr-1',
172
+ className
173
+ )}
174
+ {...props}
175
+ >
176
+ {children}
177
+ </ToolbarPrimitive.Button>
178
+ );
179
+ });
180
+
181
+ export function ToolbarSplitButton({
182
+ className,
183
+ ...props
184
+ }: React.ComponentPropsWithoutRef<typeof ToolbarButton>) {
185
+ return (
186
+ <ToolbarButton
187
+ className={cn('group flex gap-0 px-0 hover:bg-transparent', className)}
188
+ {...props}
189
+ />
190
+ );
191
+ }
192
+
193
+ type ToolbarSplitButtonPrimaryProps = Omit<
194
+ React.ComponentPropsWithoutRef<typeof ToolbarToggleItem>,
195
+ 'value'
196
+ > &
197
+ VariantProps<typeof toolbarButtonVariants>;
198
+
199
+ export function ToolbarSplitButtonPrimary({
200
+ children,
201
+ className,
202
+ size = 'sm',
203
+ variant,
204
+ ...props
205
+ }: ToolbarSplitButtonPrimaryProps) {
206
+ return (
207
+ <span
208
+ className={cn(
209
+ toolbarButtonVariants({
210
+ size,
211
+ variant,
212
+ }),
213
+ 'rounded-r-none',
214
+ 'group-data-[pressed=true]:bg-accent group-data-[pressed=true]:text-accent-foreground',
215
+ className
216
+ )}
217
+ {...props}
218
+ >
219
+ {children}
220
+ </span>
221
+ );
222
+ }
223
+
224
+ export function ToolbarSplitButtonSecondary({
225
+ className,
226
+ size,
227
+ variant,
228
+ ...props
229
+ }: React.ComponentPropsWithoutRef<'span'> &
230
+ VariantProps<typeof dropdownArrowVariants>) {
231
+ return (
232
+ <span
233
+ className={cn(
234
+ dropdownArrowVariants({
235
+ size,
236
+ variant,
237
+ }),
238
+ 'group-data-[pressed=true]:bg-accent group-data-[pressed=true]:text-accent-foreground',
239
+ className
240
+ )}
241
+ onClick={(e) => e.stopPropagation()}
242
+ role="button"
243
+ {...props}
244
+ >
245
+ <ChevronDown className="size-3.5 text-muted-foreground" data-icon />
246
+ </span>
247
+ );
248
+ }
249
+
250
+ export function ToolbarToggleItem({
251
+ className,
252
+ size = 'sm',
253
+ variant,
254
+ ...props
255
+ }: React.ComponentProps<typeof ToolbarPrimitive.ToggleItem> &
256
+ VariantProps<typeof toolbarButtonVariants>) {
257
+ return (
258
+ <ToolbarPrimitive.ToggleItem
259
+ className={cn(toolbarButtonVariants({ size, variant }), className)}
260
+ {...props}
261
+ />
262
+ );
263
+ }
264
+
265
+ export function ToolbarGroup({
266
+ children,
267
+ className,
268
+ }: React.ComponentProps<'div'>) {
269
+ return (
270
+ <div
271
+ className={cn(
272
+ 'group/toolbar-group',
273
+ 'relative hidden has-[button]:flex',
274
+ className
275
+ )}
276
+ >
277
+ <div className="flex items-center">{children}</div>
278
+
279
+ <div className="mx-1.5 py-0.5 group-last/toolbar-group:hidden!">
280
+ <Separator orientation="vertical" />
281
+ </div>
282
+ </div>
283
+ );
284
+ }
285
+
286
+ type TooltipProps<T extends React.ElementType> = {
287
+ tooltip?: React.ReactNode;
288
+ tooltipContentProps?: Omit<
289
+ React.ComponentPropsWithoutRef<typeof TooltipContent>,
290
+ 'children'
291
+ >;
292
+ tooltipProps?: Omit<
293
+ React.ComponentPropsWithoutRef<typeof Tooltip>,
294
+ 'children'
295
+ >;
296
+ tooltipTriggerProps?: React.ComponentPropsWithoutRef<typeof TooltipTrigger>;
297
+ } & React.ComponentProps<T>;
298
+
299
+ function withTooltip<T extends React.ElementType>(Component: T) {
300
+ return function ExtendComponent({
301
+ tooltip,
302
+ tooltipContentProps,
303
+ tooltipProps,
304
+ tooltipTriggerProps,
305
+ ...props
306
+ }: TooltipProps<T>) {
307
+ const [mounted, setMounted] = React.useState(false);
308
+
309
+ React.useEffect(() => {
310
+ setMounted(true);
311
+ }, []);
312
+
313
+ const component = <Component {...(props as React.ComponentProps<T>)} />;
314
+
315
+ if (tooltip && mounted) {
316
+ return (
317
+ <Tooltip {...tooltipProps}>
318
+ <TooltipTrigger asChild {...tooltipTriggerProps}>
319
+ {component}
320
+ </TooltipTrigger>
321
+
322
+ <TooltipContent {...tooltipContentProps}>{tooltip}</TooltipContent>
323
+ </Tooltip>
324
+ );
325
+ }
326
+
327
+ return component;
328
+ };
329
+ }
330
+
331
+ function TooltipContent({
332
+ children,
333
+ className,
334
+ // CHANGE
335
+ sideOffset = 4,
336
+ ...props
337
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
338
+ return (
339
+ <TooltipPrimitive.Portal>
340
+ <TooltipPrimitive.Content
341
+ className={cn(
342
+ 'z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md bg-primary px-3 py-1.5 text-xs text-balance text-primary-foreground',
343
+ className
344
+ )}
345
+ data-slot="tooltip-content"
346
+ sideOffset={sideOffset}
347
+ {...props}
348
+ >
349
+ {children}
350
+ {/* CHANGE */}
351
+ {/* <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" /> */}
352
+ </TooltipPrimitive.Content>
353
+ </TooltipPrimitive.Portal>
354
+ );
355
+ }
356
+
357
+ export function ToolbarMenuGroup({
358
+ children,
359
+ className,
360
+ label,
361
+ ...props
362
+ }: React.ComponentProps<typeof DropdownMenuRadioGroup> & { label?: string }) {
363
+ return (
364
+ <>
365
+ <DropdownMenuSeparator
366
+ className={cn(
367
+ 'hidden',
368
+ 'mb-0 shrink-0 peer-has-[[role=menuitem]]/menu-group:block peer-has-[[role=menuitemradio]]/menu-group:block peer-has-[[role=option]]/menu-group:block'
369
+ )}
370
+ />
371
+
372
+ <DropdownMenuRadioGroup
373
+ {...props}
374
+ className={cn(
375
+ 'hidden',
376
+ 'peer/menu-group group/menu-group my-1.5 has-[[role=menuitem]]:block has-[[role=menuitemradio]]:block has-[[role=option]]:block',
377
+ className
378
+ )}
379
+ >
380
+ {label && (
381
+ <DropdownMenuLabel className="text-xs font-semibold text-muted-foreground select-none">
382
+ {label}
383
+ </DropdownMenuLabel>
384
+ )}
385
+ {children}
386
+ </DropdownMenuRadioGroup>
387
+ </>
388
+ );
389
+ }
@@ -0,0 +1,59 @@
1
+ import * as React from "react"
2
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ function TooltipProvider({
7
+ delayDuration = 0,
8
+ ...props
9
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
10
+ return (
11
+ <TooltipPrimitive.Provider
12
+ data-slot="tooltip-provider"
13
+ delayDuration={delayDuration}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ function Tooltip({
20
+ ...props
21
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
22
+ return (
23
+ <TooltipProvider>
24
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
25
+ </TooltipProvider>
26
+ )
27
+ }
28
+
29
+ function TooltipTrigger({
30
+ ...props
31
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
32
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
33
+ }
34
+
35
+ function TooltipContent({
36
+ className,
37
+ sideOffset = 0,
38
+ children,
39
+ ...props
40
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
41
+ return (
42
+ <TooltipPrimitive.Portal>
43
+ <TooltipPrimitive.Content
44
+ data-slot="tooltip-content"
45
+ sideOffset={sideOffset}
46
+ className={cn(
47
+ "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
48
+ className
49
+ )}
50
+ {...props}
51
+ >
52
+ {children}
53
+ <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
54
+ </TooltipPrimitive.Content>
55
+ </TooltipPrimitive.Portal>
56
+ )
57
+ }
58
+
59
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }