@groupher/rich-editor 0.0.7 → 0.0.9

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 (36) hide show
  1. package/dist/rich-editor.es.js +33455 -26238
  2. package/dist/rich-editor.umd.js +77 -101
  3. package/package.json +21 -6
  4. package/src/RichEditor.tsx +204 -92
  5. package/src/components/editor/editor-kit.tsx +27 -0
  6. package/src/components/editor/plugins/autoformat-kit.tsx +99 -0
  7. package/src/components/editor/plugins/callout-kit.tsx +7 -0
  8. package/src/components/editor/plugins/emoji-kit.tsx +14 -0
  9. package/src/components/editor/plugins/indent-kit.tsx +12 -0
  10. package/src/components/editor/plugins/link-kit.tsx +13 -0
  11. package/src/components/editor/plugins/list-kit.tsx +17 -0
  12. package/src/components/editor/plugins/mention-kit.tsx +17 -0
  13. package/src/components/editor/plugins/slash-kit.tsx +15 -0
  14. package/src/components/editor/plugins/toggle-kit.tsx +7 -0
  15. package/src/components/ui/action-bar.tsx +208 -0
  16. package/src/components/ui/block-list.tsx +94 -0
  17. package/src/components/ui/button.tsx +49 -50
  18. package/src/components/ui/callout-node.tsx +65 -0
  19. package/src/components/ui/editor-static.tsx +44 -44
  20. package/src/components/ui/editor.tsx +107 -107
  21. package/src/components/ui/emoji-node.tsx +71 -0
  22. package/src/components/ui/emoji-toolbar-button.tsx +618 -0
  23. package/src/components/ui/floating-toolbar.tsx +86 -0
  24. package/src/components/ui/inline-combobox.tsx +414 -0
  25. package/src/components/ui/link-node.tsx +31 -0
  26. package/src/components/ui/link-toolbar-button.tsx +33 -0
  27. package/src/components/ui/mention-node.tsx +126 -0
  28. package/src/components/ui/slash-node.tsx +191 -0
  29. package/src/components/ui/toggle-node.tsx +36 -0
  30. package/src/components/ui/toolbar.tsx +10 -10
  31. package/src/hooks/use-debounce.ts +15 -0
  32. package/src/hooks/use-mounted.ts +11 -0
  33. package/src/i18n.tsx +155 -0
  34. package/src/main.tsx +35 -14
  35. package/src/mention-context.tsx +32 -0
  36. package/src/vite-env.d.ts +7 -0
@@ -1,129 +1,129 @@
1
- 'use client';
1
+ "use client";
2
2
 
3
- import * as React from 'react';
3
+ import * as React from "react";
4
4
 
5
- import type { VariantProps } from 'class-variance-authority';
6
- import type { PlateContentProps, PlateViewProps } from 'platejs/react';
5
+ import type { VariantProps } from "class-variance-authority";
6
+ import type { PlateContentProps, PlateViewProps } from "platejs/react";
7
7
 
8
- import { cva } from 'class-variance-authority';
9
- import { PlateContainer, PlateContent, PlateView } from 'platejs/react';
8
+ import { cva } from "class-variance-authority";
9
+ import { PlateContainer, PlateContent, PlateView } from "platejs/react";
10
10
 
11
- import { cn } from '@/lib/utils';
11
+ import { cn } from "@/lib/utils";
12
12
 
13
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
- }
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
36
  );
37
37
 
38
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
- );
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
53
  }
54
54
 
55
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
- }
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-24 text-base sm:px-[max(64px,calc(50%-350px))]",
81
+ demo: "size-full px-16 pt-4 pb-24 text-base sm:px-[max(64px,calc(50%-350px))]",
82
+ fullWidth: "size-full px-16 pt-4 pb-24 text-base sm:px-24",
83
+ none: "",
84
+ select: "px-3 py-2 text-base data-readonly:w-fit",
85
+ },
86
+ },
87
+ },
88
88
  );
89
89
 
90
- export type EditorProps = PlateContentProps &
91
- VariantProps<typeof editorVariants>;
90
+ export type TEditorProps = PlateContentProps &
91
+ VariantProps<typeof editorVariants>;
92
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
- }
93
+ export const Editor = React.forwardRef<HTMLDivElement, TEditorProps>(
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
112
  );
113
113
 
114
- Editor.displayName = 'Editor';
114
+ Editor.displayName = "Editor";
115
115
 
116
116
  export function EditorView({
117
- className,
118
- variant,
119
- ...props
117
+ className,
118
+ variant,
119
+ ...props
120
120
  }: PlateViewProps & VariantProps<typeof editorVariants>) {
121
- return (
122
- <PlateView
123
- {...props}
124
- className={cn(editorVariants({ variant }), className)}
125
- />
126
- );
121
+ return (
122
+ <PlateView
123
+ {...props}
124
+ className={cn(editorVariants({ variant }), className)}
125
+ />
126
+ );
127
127
  }
128
128
 
129
- EditorView.displayName = 'EditorView';
129
+ EditorView.displayName = "EditorView";
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+
5
+ import type { PlateElementProps } from 'platejs/react';
6
+
7
+ import { EmojiInlineIndexSearch, insertEmoji } from '@platejs/emoji';
8
+ import { EmojiPlugin } from '@platejs/emoji/react';
9
+ import { PlateElement, usePluginOption } from 'platejs/react';
10
+
11
+ import { useDebounce } from '@/hooks/use-debounce';
12
+
13
+ import {
14
+ InlineCombobox,
15
+ InlineComboboxContent,
16
+ InlineComboboxEmpty,
17
+ InlineComboboxGroup,
18
+ InlineComboboxInput,
19
+ InlineComboboxItem,
20
+ } from './inline-combobox';
21
+
22
+ const trailingColonRegex = /:$/;
23
+
24
+ export function EmojiInputElement(props: PlateElementProps) {
25
+ const { children, editor, element } = props;
26
+ const data = usePluginOption(EmojiPlugin, 'data');
27
+ const [value, setValue] = React.useState('');
28
+ const debouncedValue = useDebounce(value, 100);
29
+ const isPending = value !== debouncedValue;
30
+
31
+ const filteredEmojis = React.useMemo(() => {
32
+ if (!data || debouncedValue.trim().length === 0) return [];
33
+
34
+ return EmojiInlineIndexSearch.getInstance(data)
35
+ .search(debouncedValue.replace(trailingColonRegex, ''))
36
+ .get();
37
+ }, [data, debouncedValue]);
38
+
39
+ return (
40
+ <PlateElement as="span" {...props}>
41
+ <InlineCombobox
42
+ value={value}
43
+ element={element}
44
+ filter={false}
45
+ setValue={setValue}
46
+ trigger=":"
47
+ hideWhenNoValue
48
+ >
49
+ <InlineComboboxInput />
50
+
51
+ <InlineComboboxContent>
52
+ {!isPending && <InlineComboboxEmpty>No results</InlineComboboxEmpty>}
53
+
54
+ <InlineComboboxGroup>
55
+ {filteredEmojis.map((emoji) => (
56
+ <InlineComboboxItem
57
+ key={emoji.id}
58
+ value={emoji.name}
59
+ onClick={() => insertEmoji(editor, emoji)}
60
+ >
61
+ {emoji.skins[0].native} {emoji.name}
62
+ </InlineComboboxItem>
63
+ ))}
64
+ </InlineComboboxGroup>
65
+ </InlineComboboxContent>
66
+ </InlineCombobox>
67
+
68
+ {children}
69
+ </PlateElement>
70
+ );
71
+ }