@timmbr/ui 1.13.2 → 1.15.0
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/components/AttributeValueField/AttributeValueField.d.ts +20 -0
- package/dist/components/AttributeValueField/AttributeValueField.d.ts.map +1 -0
- package/dist/components/AttributeValueField/AttributeValueField.js +130 -0
- package/dist/components/AttributeValueField/index.d.ts +2 -0
- package/dist/components/AttributeValueField/index.d.ts.map +1 -0
- package/dist/components/DataTable/DataTable.d.ts +10 -0
- package/dist/components/DataTable/DataTable.d.ts.map +1 -0
- package/dist/components/DataTable/DataTable.js +177 -0
- package/dist/components/DataTable/DataTable.types.d.ts +42 -0
- package/dist/components/DataTable/DataTable.types.d.ts.map +1 -0
- package/dist/components/DataTable/index.d.ts +3 -0
- package/dist/components/DataTable/index.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextContent.d.ts +8 -0
- package/dist/components/RichTextEditor/RichTextContent.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextContent.js +18 -0
- package/dist/components/RichTextEditor/RichTextEditor.context.d.ts +6 -0
- package/dist/components/RichTextEditor/RichTextEditor.context.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextEditor.context.js +14 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts +4 -0
- package/dist/components/RichTextEditor/RichTextEditor.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextEditor.helpers.d.ts +28 -0
- package/dist/components/RichTextEditor/RichTextEditor.helpers.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextEditor.helpers.js +52 -0
- package/dist/components/RichTextEditor/RichTextEditor.js +156 -0
- package/dist/components/RichTextEditor/RichTextEditor.styles.d.ts +10 -0
- package/dist/components/RichTextEditor/RichTextEditor.styles.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextEditor.styles.js +95 -0
- package/dist/components/RichTextEditor/RichTextEditor.types.d.ts +73 -0
- package/dist/components/RichTextEditor/RichTextEditor.types.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextJsonContent.d.ts +9 -0
- package/dist/components/RichTextEditor/RichTextJsonContent.d.ts.map +1 -0
- package/dist/components/RichTextEditor/RichTextJsonContent.js +27 -0
- package/dist/components/RichTextEditor/index.d.ts +14 -0
- package/dist/components/RichTextEditor/index.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteCharCount.d.ts +4 -0
- package/dist/components/RichTextEditor/subcomponents/RteCharCount.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteCharCount.js +37 -0
- package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.d.ts +3 -0
- package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.js +274 -0
- package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.d.ts +3 -0
- package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.js +93 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbar.d.ts +7 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbar.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbar.js +234 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.d.ts +4 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.js +37 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.d.ts +4 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.d.ts.map +1 -0
- package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.js +13 -0
- package/dist/components/Table/Table.d.ts.map +1 -1
- package/dist/components/Table/Table.js +14 -7
- package/dist/components/Table/Table.styles.js +2 -2
- package/dist/components/Table/Table.types.d.ts +1 -0
- package/dist/components/Table/Table.types.d.ts.map +1 -1
- package/dist/components/Table/TablePagination.js +3 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/node_modules/.pnpm/@tanstack_react-table@8.21._59ea1c5c486c125464e3b5e1938c78d1/node_modules/@tanstack/react-table/build/lib/index.js +74 -0
- package/dist/node_modules/.pnpm/@tanstack_table-core@8.21.3/node_modules/@tanstack/table-core/build/lib/index.js +3027 -0
- package/package.json +15 -5
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useEditor, EditorContent } from '@tiptap/react';
|
|
5
|
+
import { cn } from '@timmbr/utils';
|
|
6
|
+
import { resolveMotion } from '../../types/motion.js';
|
|
7
|
+
import { RteContext } from './RichTextEditor.context.js';
|
|
8
|
+
import { proseEditorStyles, editorWrapperVariants } from './RichTextEditor.styles.js';
|
|
9
|
+
import { getEditorExtensions, normalizeHtml } from './RichTextEditor.helpers.js';
|
|
10
|
+
import { RteToolbar } from './subcomponents/RteToolbar.js';
|
|
11
|
+
import { RteCharCount } from './subcomponents/RteCharCount.js';
|
|
12
|
+
import { useGlobalAnimation } from '../../providers/AnimationProvider.js';
|
|
13
|
+
|
|
14
|
+
function RichTextEditor({
|
|
15
|
+
value,
|
|
16
|
+
defaultValue,
|
|
17
|
+
onChange,
|
|
18
|
+
onChangeJson,
|
|
19
|
+
onFocus,
|
|
20
|
+
onBlur,
|
|
21
|
+
placeholder,
|
|
22
|
+
maxCharacters,
|
|
23
|
+
showCharacterCount = false,
|
|
24
|
+
showWordCount = false,
|
|
25
|
+
toolbar = true,
|
|
26
|
+
toolbarClassName,
|
|
27
|
+
disabled = false,
|
|
28
|
+
readOnly = false,
|
|
29
|
+
className,
|
|
30
|
+
editorClassName,
|
|
31
|
+
style,
|
|
32
|
+
id,
|
|
33
|
+
motion: motionProp,
|
|
34
|
+
"aria-label": ariaLabel,
|
|
35
|
+
"aria-labelledby": ariaLabelledby,
|
|
36
|
+
"aria-describedby": ariaDescribedby
|
|
37
|
+
}) {
|
|
38
|
+
const globalMotion = useGlobalAnimation();
|
|
39
|
+
const { shouldAnimate, motionClass, dataAttributes } = resolveMotion(motionProp, globalMotion);
|
|
40
|
+
const isControlled = value !== void 0;
|
|
41
|
+
const lastEmittedHtmlRef = React.useRef(
|
|
42
|
+
isControlled ? value ?? "" : defaultValue ?? ""
|
|
43
|
+
);
|
|
44
|
+
const onChangeRef = React.useRef(onChange);
|
|
45
|
+
const onChangeJsonRef = React.useRef(onChangeJson);
|
|
46
|
+
const onFocusRef = React.useRef(onFocus);
|
|
47
|
+
const onBlurRef = React.useRef(onBlur);
|
|
48
|
+
React.useEffect(() => {
|
|
49
|
+
onChangeRef.current = onChange;
|
|
50
|
+
onChangeJsonRef.current = onChangeJson;
|
|
51
|
+
onFocusRef.current = onFocus;
|
|
52
|
+
onBlurRef.current = onBlur;
|
|
53
|
+
});
|
|
54
|
+
const [mounted, setMounted] = React.useState(false);
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
setMounted(true);
|
|
57
|
+
}, []);
|
|
58
|
+
const extensions = React.useMemo(
|
|
59
|
+
() => getEditorExtensions({
|
|
60
|
+
placeholder,
|
|
61
|
+
maxCharacters,
|
|
62
|
+
showCount: showCharacterCount || showWordCount
|
|
63
|
+
}),
|
|
64
|
+
// intentionally omit showCharacterCount/showWordCount from deps: extensions are
|
|
65
|
+
// frozen at mount and cannot be hot-swapped; changing count visibility after mount
|
|
66
|
+
// requires remounting the editor, which is not a supported use-case.
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
[placeholder, maxCharacters]
|
|
69
|
+
);
|
|
70
|
+
const editor = useEditor({
|
|
71
|
+
immediatelyRender: false,
|
|
72
|
+
// SSR safety — defers DOM init to useEffect
|
|
73
|
+
editable: !disabled && !readOnly,
|
|
74
|
+
content: isControlled ? value ?? "" : defaultValue ?? "",
|
|
75
|
+
extensions,
|
|
76
|
+
onUpdate: ({ editor: e }) => {
|
|
77
|
+
const html = normalizeHtml(e.getHTML());
|
|
78
|
+
const json = e.getJSON();
|
|
79
|
+
lastEmittedHtmlRef.current = html;
|
|
80
|
+
onChangeRef.current?.(html);
|
|
81
|
+
onChangeJsonRef.current?.(json);
|
|
82
|
+
},
|
|
83
|
+
onFocus: () => {
|
|
84
|
+
onFocusRef.current?.();
|
|
85
|
+
},
|
|
86
|
+
onBlur: () => {
|
|
87
|
+
onBlurRef.current?.();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
React.useEffect(() => {
|
|
91
|
+
if (!editor || !isControlled || value === void 0) return;
|
|
92
|
+
if (value === lastEmittedHtmlRef.current) return;
|
|
93
|
+
editor.commands.setContent(value, false);
|
|
94
|
+
lastEmittedHtmlRef.current = value;
|
|
95
|
+
}, [value, editor]);
|
|
96
|
+
React.useEffect(() => {
|
|
97
|
+
if (!editor) return;
|
|
98
|
+
editor.setEditable(!disabled && !readOnly);
|
|
99
|
+
}, [editor, disabled, readOnly]);
|
|
100
|
+
const showCount = showCharacterCount || showWordCount;
|
|
101
|
+
const contextValue = React.useMemo(
|
|
102
|
+
() => ({ editor, shouldAnimate, disabled, readOnly }),
|
|
103
|
+
[editor, shouldAnimate, disabled, readOnly]
|
|
104
|
+
);
|
|
105
|
+
return /* @__PURE__ */ jsx(RteContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
"data-slot": "rte",
|
|
109
|
+
style,
|
|
110
|
+
className: cn(
|
|
111
|
+
editorWrapperVariants({ disabled }),
|
|
112
|
+
motionClass,
|
|
113
|
+
className
|
|
114
|
+
),
|
|
115
|
+
...dataAttributes,
|
|
116
|
+
children: [
|
|
117
|
+
toolbar && /* @__PURE__ */ jsx(
|
|
118
|
+
RteToolbar,
|
|
119
|
+
{
|
|
120
|
+
className: cn(
|
|
121
|
+
shouldAnimate && mounted ? "animate-in fade-in duration-150" : void 0,
|
|
122
|
+
toolbarClassName
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
/* @__PURE__ */ jsx(
|
|
127
|
+
EditorContent,
|
|
128
|
+
{
|
|
129
|
+
editor,
|
|
130
|
+
id,
|
|
131
|
+
"aria-label": ariaLabel,
|
|
132
|
+
"aria-labelledby": ariaLabelledby,
|
|
133
|
+
"aria-describedby": ariaDescribedby,
|
|
134
|
+
"aria-multiline": "true",
|
|
135
|
+
"aria-readonly": readOnly || void 0,
|
|
136
|
+
className: cn(
|
|
137
|
+
proseEditorStyles,
|
|
138
|
+
"min-h-[120px] px-4 py-3 flex-1",
|
|
139
|
+
editorClassName
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
showCount && /* @__PURE__ */ jsx(
|
|
144
|
+
RteCharCount,
|
|
145
|
+
{
|
|
146
|
+
maxCharacters,
|
|
147
|
+
showCharacterCount,
|
|
148
|
+
showWordCount
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
) });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { RichTextEditor };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const editorWrapperVariants: (props?: ({
|
|
2
|
+
disabled?: boolean | null | undefined;
|
|
3
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
4
|
+
export declare const toolbarVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
5
|
+
export declare const charCountVariants: (props?: ({
|
|
6
|
+
overLimit?: boolean | null | undefined;
|
|
7
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
|
+
export declare const proseEditorStyles: string;
|
|
9
|
+
export declare const proseViewerStyles: string;
|
|
10
|
+
//# sourceMappingURL=RichTextEditor.styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextEditor.styles.d.ts","sourceRoot":"","sources":["../../../src/components/RichTextEditor/RichTextEditor.styles.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,qBAAqB;;8EAejC,CAAC;AAIF,eAAO,MAAM,eAAe,oFAE3B,CAAC;AAIF,eAAO,MAAM,iBAAiB;;8EAW7B,CAAC;AAKF,eAAO,MAAM,iBAAiB,QAgC7B,CAAC;AAKF,eAAO,MAAM,iBAAiB,QAyB7B,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { cva } from 'class-variance-authority';
|
|
2
|
+
import { cn } from '@timmbr/utils';
|
|
3
|
+
|
|
4
|
+
const editorWrapperVariants = cva(
|
|
5
|
+
[
|
|
6
|
+
"flex flex-col w-full rounded-md border border-border bg-card text-foreground font-sans",
|
|
7
|
+
"transition-shadow duration-150",
|
|
8
|
+
"focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-1"
|
|
9
|
+
],
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
disabled: {
|
|
13
|
+
true: "opacity-50 pointer-events-none select-none",
|
|
14
|
+
false: ""
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: { disabled: false }
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
const toolbarVariants = cva(
|
|
21
|
+
"flex items-center flex-wrap gap-0.5 px-2 py-1.5 border-b border-border bg-card rounded-t-[2px]"
|
|
22
|
+
);
|
|
23
|
+
const charCountVariants = cva(
|
|
24
|
+
"flex items-center justify-end gap-3 px-3 py-1.5 border-t border-border text-caption text-muted-foreground select-none rounded-b-[2px]",
|
|
25
|
+
{
|
|
26
|
+
variants: {
|
|
27
|
+
overLimit: {
|
|
28
|
+
true: "text-destructive",
|
|
29
|
+
false: ""
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: { overLimit: false }
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
const proseEditorStyles = cn(
|
|
36
|
+
// Editor focus reset
|
|
37
|
+
"[&_.ProseMirror]:outline-none",
|
|
38
|
+
"[&_.ProseMirror]:cursor-text",
|
|
39
|
+
// Paragraph
|
|
40
|
+
"[&_.ProseMirror_p]:text-body-1 [&_.ProseMirror_p]:text-foreground [&_.ProseMirror_p]:my-2 [&_.ProseMirror_p]:leading-relaxed",
|
|
41
|
+
// Headings — DS display font + scale
|
|
42
|
+
"[&_.ProseMirror_h2]:font-display [&_.ProseMirror_h2]:text-h2 [&_.ProseMirror_h2]:font-normal [&_.ProseMirror_h2]:text-foreground [&_.ProseMirror_h2]:my-4 [&_.ProseMirror_h2]:leading-tight",
|
|
43
|
+
"[&_.ProseMirror_h3]:font-display [&_.ProseMirror_h3]:text-h3 [&_.ProseMirror_h3]:font-normal [&_.ProseMirror_h3]:text-foreground [&_.ProseMirror_h3]:my-3 [&_.ProseMirror_h3]:leading-tight",
|
|
44
|
+
// Lists
|
|
45
|
+
"[&_.ProseMirror_ul]:list-disc [&_.ProseMirror_ul]:pl-6 [&_.ProseMirror_ul]:my-2",
|
|
46
|
+
"[&_.ProseMirror_ol]:list-decimal [&_.ProseMirror_ol]:pl-6 [&_.ProseMirror_ol]:my-2",
|
|
47
|
+
"[&_.ProseMirror_li]:text-body-1 [&_.ProseMirror_li]:text-foreground [&_.ProseMirror_li]:mb-1",
|
|
48
|
+
"[&_.ProseMirror_li_p]:my-0",
|
|
49
|
+
// Blockquote
|
|
50
|
+
"[&_.ProseMirror_blockquote]:border-l-4 [&_.ProseMirror_blockquote]:border-primary [&_.ProseMirror_blockquote]:pl-4 [&_.ProseMirror_blockquote]:italic [&_.ProseMirror_blockquote]:text-muted-foreground [&_.ProseMirror_blockquote]:my-3",
|
|
51
|
+
// Inline marks
|
|
52
|
+
"[&_.ProseMirror_strong]:font-semibold",
|
|
53
|
+
"[&_.ProseMirror_em]:italic",
|
|
54
|
+
"[&_.ProseMirror_u]:underline [&_.ProseMirror_u]:underline-offset-2",
|
|
55
|
+
"[&_.ProseMirror_s]:line-through",
|
|
56
|
+
// Links
|
|
57
|
+
"[&_.ProseMirror_a]:text-primary [&_.ProseMirror_a]:underline [&_.ProseMirror_a]:underline-offset-2 [&_.ProseMirror_a]:cursor-pointer [&_.ProseMirror_a]:transition-opacity [&_.ProseMirror_a:hover]:opacity-80",
|
|
58
|
+
// Horizontal rule
|
|
59
|
+
"[&_.ProseMirror_hr]:border-t [&_.ProseMirror_hr]:border-border [&_.ProseMirror_hr]:my-6",
|
|
60
|
+
// Placeholder via TipTap's .is-empty class on the paragraph node
|
|
61
|
+
"[&_.ProseMirror_p.is-empty:first-child::before]:content-[attr(data-placeholder)] [&_.ProseMirror_p.is-empty:first-child::before]:text-muted-foreground [&_.ProseMirror_p.is-empty:first-child::before]:pointer-events-none [&_.ProseMirror_p.is-empty:first-child::before]:float-left [&_.ProseMirror_p.is-empty:first-child::before]:h-0",
|
|
62
|
+
// Selected text
|
|
63
|
+
"[&_.ProseMirror_::selection]:bg-primary/20",
|
|
64
|
+
// First/last margin reset so the editor doesn't have extra outer gap
|
|
65
|
+
"[&_.ProseMirror_>*:first-child]:mt-0",
|
|
66
|
+
"[&_.ProseMirror_>*:last-child]:mb-0"
|
|
67
|
+
);
|
|
68
|
+
const proseViewerStyles = cn(
|
|
69
|
+
// Paragraph
|
|
70
|
+
"[&_p]:text-body-1 [&_p]:text-foreground [&_p]:my-2 [&_p]:leading-relaxed",
|
|
71
|
+
// Headings
|
|
72
|
+
"[&_h2]:font-display [&_h2]:text-h2 [&_h2]:font-normal [&_h2]:text-foreground [&_h2]:my-4 [&_h2]:leading-tight",
|
|
73
|
+
"[&_h3]:font-display [&_h3]:text-h3 [&_h3]:font-normal [&_h3]:text-foreground [&_h3]:my-3 [&_h3]:leading-tight",
|
|
74
|
+
// Lists
|
|
75
|
+
"[&_ul]:list-disc [&_ul]:pl-6 [&_ul]:my-2",
|
|
76
|
+
"[&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:my-2",
|
|
77
|
+
"[&_li]:text-body-1 [&_li]:text-foreground [&_li]:mb-1",
|
|
78
|
+
"[&_li_p]:my-0",
|
|
79
|
+
// Blockquote
|
|
80
|
+
"[&_blockquote]:border-l-4 [&_blockquote]:border-primary [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:text-muted-foreground [&_blockquote]:my-3",
|
|
81
|
+
// Inline marks
|
|
82
|
+
"[&_strong]:font-semibold",
|
|
83
|
+
"[&_em]:italic",
|
|
84
|
+
"[&_u]:underline [&_u]:underline-offset-2",
|
|
85
|
+
"[&_s]:line-through",
|
|
86
|
+
// Links
|
|
87
|
+
"[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2 [&_a]:cursor-pointer [&_a]:transition-opacity [&_a:hover]:opacity-80",
|
|
88
|
+
// Horizontal rule
|
|
89
|
+
"[&_hr]:border-t [&_hr]:border-border [&_hr]:my-6",
|
|
90
|
+
// First/last margin reset
|
|
91
|
+
"[&_>*:first-child]:mt-0",
|
|
92
|
+
"[&_>*:last-child]:mb-0"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
export { charCountVariants, editorWrapperVariants, proseEditorStyles, proseViewerStyles, toolbarVariants };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type * as React from 'react';
|
|
2
|
+
import type { Editor, JSONContent } from '@tiptap/core';
|
|
3
|
+
import type { MotionProps } from '../../types/motion';
|
|
4
|
+
export interface RichTextEditorProps extends MotionProps {
|
|
5
|
+
/** Controlled HTML string value */
|
|
6
|
+
value?: string;
|
|
7
|
+
/** Uncontrolled initial HTML string */
|
|
8
|
+
defaultValue?: string;
|
|
9
|
+
/** Called with the HTML string on every content change */
|
|
10
|
+
onChange?: (html: string) => void;
|
|
11
|
+
/** Called with TipTap's JSON document on every content change */
|
|
12
|
+
onChangeJson?: (json: JSONContent) => void;
|
|
13
|
+
onFocus?: () => void;
|
|
14
|
+
onBlur?: () => void;
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
/** Hard character limit; enables the CharacterCount extension limit */
|
|
17
|
+
maxCharacters?: number;
|
|
18
|
+
/** Show character count footer */
|
|
19
|
+
showCharacterCount?: boolean;
|
|
20
|
+
/** Show word count in the footer */
|
|
21
|
+
showWordCount?: boolean;
|
|
22
|
+
/** Show the toolbar — default true */
|
|
23
|
+
toolbar?: boolean;
|
|
24
|
+
toolbarClassName?: string;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
readOnly?: boolean;
|
|
27
|
+
className?: string;
|
|
28
|
+
editorClassName?: string;
|
|
29
|
+
style?: React.CSSProperties;
|
|
30
|
+
id?: string;
|
|
31
|
+
'aria-label'?: string;
|
|
32
|
+
'aria-labelledby'?: string;
|
|
33
|
+
'aria-describedby'?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface RichTextContentProps {
|
|
36
|
+
/** Raw HTML string to render */
|
|
37
|
+
content: string;
|
|
38
|
+
className?: string;
|
|
39
|
+
style?: React.CSSProperties;
|
|
40
|
+
}
|
|
41
|
+
export interface RichTextJsonContentProps {
|
|
42
|
+
/** TipTap JSONContent document to render */
|
|
43
|
+
content: JSONContent;
|
|
44
|
+
className?: string;
|
|
45
|
+
style?: React.CSSProperties;
|
|
46
|
+
}
|
|
47
|
+
export interface RteContextValue {
|
|
48
|
+
editor: Editor | null;
|
|
49
|
+
shouldAnimate: boolean;
|
|
50
|
+
disabled: boolean;
|
|
51
|
+
readOnly: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface RteToolbarButtonProps {
|
|
54
|
+
/** Tooltip label and aria-label */
|
|
55
|
+
label: string;
|
|
56
|
+
icon: React.ReactNode;
|
|
57
|
+
onClick: () => void;
|
|
58
|
+
isActive?: boolean;
|
|
59
|
+
disabled?: boolean;
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface RteToolbarGroupProps {
|
|
63
|
+
children: React.ReactNode;
|
|
64
|
+
/** Show leading vertical divider — default true */
|
|
65
|
+
showLeadingDivider?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export interface RteCharCountProps {
|
|
68
|
+
maxCharacters?: number;
|
|
69
|
+
showCharacterCount?: boolean;
|
|
70
|
+
showWordCount?: boolean;
|
|
71
|
+
className?: string;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=RichTextEditor.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextEditor.types.d.ts","sourceRoot":"","sources":["../../../src/components/RichTextEditor/RichTextEditor.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,iEAAiE;IACjE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kCAAkC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oCAAoC;IACpC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,4CAA4C;IAC5C,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,mDAAmD;IACnD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { RichTextJsonContentProps } from './RichTextEditor.types';
|
|
3
|
+
/**
|
|
4
|
+
* SSR-safe viewer for TipTap JSONContent.
|
|
5
|
+
* Uses generateHTML from @tiptap/core — a pure function with no browser dependency.
|
|
6
|
+
* Falls back silently on malformed input.
|
|
7
|
+
*/
|
|
8
|
+
export declare function RichTextJsonContent({ content, className, style }: RichTextJsonContentProps): React.JSX.Element | null;
|
|
9
|
+
//# sourceMappingURL=RichTextJsonContent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RichTextJsonContent.d.ts","sourceRoot":"","sources":["../../../src/components/RichTextEditor/RichTextJsonContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,wBAAwB,4BAqB1F"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { generateHTML } from '@tiptap/core';
|
|
3
|
+
import { cn } from '@timmbr/utils';
|
|
4
|
+
import { proseViewerStyles } from './RichTextEditor.styles.js';
|
|
5
|
+
import { getViewerExtensions } from './RichTextEditor.helpers.js';
|
|
6
|
+
|
|
7
|
+
function RichTextJsonContent({ content, className, style }) {
|
|
8
|
+
if (!content) return null;
|
|
9
|
+
let html = "";
|
|
10
|
+
try {
|
|
11
|
+
html = generateHTML(content, getViewerExtensions());
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (!html) return null;
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
"div",
|
|
18
|
+
{
|
|
19
|
+
"data-slot": "rte-json-content",
|
|
20
|
+
className: cn(proseViewerStyles, className),
|
|
21
|
+
style,
|
|
22
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { RichTextJsonContent };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './RichTextEditor';
|
|
2
|
+
export * from './RichTextContent';
|
|
3
|
+
export * from './RichTextJsonContent';
|
|
4
|
+
export * from './RichTextEditor.types';
|
|
5
|
+
export * from './RichTextEditor.styles';
|
|
6
|
+
export * from './RichTextEditor.helpers';
|
|
7
|
+
export * from './RichTextEditor.context';
|
|
8
|
+
export * from './subcomponents/RteToolbar';
|
|
9
|
+
export * from './subcomponents/RteToolbarButton';
|
|
10
|
+
export * from './subcomponents/RteToolbarGroup';
|
|
11
|
+
export * from './subcomponents/RteLinkPopover';
|
|
12
|
+
export * from './subcomponents/RteEmojiPicker';
|
|
13
|
+
export * from './subcomponents/RteCharCount';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/RichTextEditor/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { RteCharCountProps } from '../RichTextEditor.types';
|
|
3
|
+
export declare function RteCharCount({ maxCharacters, showCharacterCount, showWordCount, className, }: RteCharCountProps): React.JSX.Element | null;
|
|
4
|
+
//# sourceMappingURL=RteCharCount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RteCharCount.d.ts","sourceRoot":"","sources":["../../../../src/components/RichTextEditor/subcomponents/RteCharCount.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAOjE,wBAAgB,YAAY,CAAC,EAC3B,aAAa,EACb,kBAAyB,EACzB,aAAqB,EACrB,SAAS,GACV,EAAE,iBAAiB,4BA0BnB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs } from 'react/jsx-runtime';
|
|
3
|
+
import { cn } from '@timmbr/utils';
|
|
4
|
+
import { useRteContext } from '../RichTextEditor.context.js';
|
|
5
|
+
import { charCountVariants } from '../RichTextEditor.styles.js';
|
|
6
|
+
|
|
7
|
+
function RteCharCount({
|
|
8
|
+
maxCharacters,
|
|
9
|
+
showCharacterCount = true,
|
|
10
|
+
showWordCount = false,
|
|
11
|
+
className
|
|
12
|
+
}) {
|
|
13
|
+
const { editor } = useRteContext();
|
|
14
|
+
if (!editor) return null;
|
|
15
|
+
const storage = editor.storage.characterCount;
|
|
16
|
+
const charCount = storage?.characters?.() ?? 0;
|
|
17
|
+
const wordCount = storage?.words?.() ?? 0;
|
|
18
|
+
const isOverLimit = maxCharacters !== void 0 && charCount > maxCharacters;
|
|
19
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(charCountVariants({ overLimit: isOverLimit }), className), children: [
|
|
20
|
+
showWordCount && /* @__PURE__ */ jsxs("span", { children: [
|
|
21
|
+
wordCount,
|
|
22
|
+
" ",
|
|
23
|
+
wordCount === 1 ? "word" : "words"
|
|
24
|
+
] }),
|
|
25
|
+
showCharacterCount && /* @__PURE__ */ jsxs("span", { children: [
|
|
26
|
+
charCount,
|
|
27
|
+
maxCharacters !== void 0 && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
28
|
+
" / ",
|
|
29
|
+
maxCharacters
|
|
30
|
+
] }),
|
|
31
|
+
" ",
|
|
32
|
+
"chars"
|
|
33
|
+
] })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { RteCharCount };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RteEmojiPicker.d.ts","sourceRoot":"","sources":["../../../../src/components/RichTextEditor/subcomponents/RteEmojiPicker.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqL/B,wBAAgB,cAAc,sBA0H7B"}
|