@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.
- package/dist/rich-editor.es.js +33455 -26238
- package/dist/rich-editor.umd.js +77 -101
- package/package.json +21 -6
- package/src/RichEditor.tsx +204 -92
- package/src/components/editor/editor-kit.tsx +27 -0
- package/src/components/editor/plugins/autoformat-kit.tsx +99 -0
- package/src/components/editor/plugins/callout-kit.tsx +7 -0
- package/src/components/editor/plugins/emoji-kit.tsx +14 -0
- package/src/components/editor/plugins/indent-kit.tsx +12 -0
- package/src/components/editor/plugins/link-kit.tsx +13 -0
- package/src/components/editor/plugins/list-kit.tsx +17 -0
- package/src/components/editor/plugins/mention-kit.tsx +17 -0
- package/src/components/editor/plugins/slash-kit.tsx +15 -0
- package/src/components/editor/plugins/toggle-kit.tsx +7 -0
- package/src/components/ui/action-bar.tsx +208 -0
- package/src/components/ui/block-list.tsx +94 -0
- package/src/components/ui/button.tsx +49 -50
- package/src/components/ui/callout-node.tsx +65 -0
- package/src/components/ui/editor-static.tsx +44 -44
- package/src/components/ui/editor.tsx +107 -107
- package/src/components/ui/emoji-node.tsx +71 -0
- package/src/components/ui/emoji-toolbar-button.tsx +618 -0
- package/src/components/ui/floating-toolbar.tsx +86 -0
- package/src/components/ui/inline-combobox.tsx +414 -0
- package/src/components/ui/link-node.tsx +31 -0
- package/src/components/ui/link-toolbar-button.tsx +33 -0
- package/src/components/ui/mention-node.tsx +126 -0
- package/src/components/ui/slash-node.tsx +191 -0
- package/src/components/ui/toggle-node.tsx +36 -0
- package/src/components/ui/toolbar.tsx +10 -10
- package/src/hooks/use-debounce.ts +15 -0
- package/src/hooks/use-mounted.ts +11 -0
- package/src/i18n.tsx +155 -0
- package/src/main.tsx +35 -14
- package/src/mention-context.tsx +32 -0
- package/src/vite-env.d.ts +7 -0
|
@@ -1,129 +1,129 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import * as React from
|
|
3
|
+
import * as React from "react";
|
|
4
4
|
|
|
5
|
-
import type { VariantProps } from
|
|
6
|
-
import type { PlateContentProps, PlateViewProps } from
|
|
5
|
+
import type { VariantProps } from "class-variance-authority";
|
|
6
|
+
import type { PlateContentProps, PlateViewProps } from "platejs/react";
|
|
7
7
|
|
|
8
|
-
import { cva } from
|
|
9
|
-
import { PlateContainer, PlateContent, PlateView } from
|
|
8
|
+
import { cva } from "class-variance-authority";
|
|
9
|
+
import { PlateContainer, PlateContent, PlateView } from "platejs/react";
|
|
10
10
|
|
|
11
|
-
import { cn } from
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
12
|
|
|
13
13
|
const editorContainerVariants = cva(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}: React.ComponentProps<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
91
|
-
|
|
90
|
+
export type TEditorProps = PlateContentProps &
|
|
91
|
+
VariantProps<typeof editorVariants>;
|
|
92
92
|
|
|
93
|
-
export const Editor = React.forwardRef<HTMLDivElement,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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 =
|
|
114
|
+
Editor.displayName = "Editor";
|
|
115
115
|
|
|
116
116
|
export function EditorView({
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
className,
|
|
118
|
+
variant,
|
|
119
|
+
...props
|
|
120
120
|
}: PlateViewProps & VariantProps<typeof editorVariants>) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
return (
|
|
122
|
+
<PlateView
|
|
123
|
+
{...props}
|
|
124
|
+
className={cn(editorVariants({ variant }), className)}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
EditorView.displayName =
|
|
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
|
+
}
|