@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.
Files changed (64) hide show
  1. package/dist/components/AttributeValueField/AttributeValueField.d.ts +20 -0
  2. package/dist/components/AttributeValueField/AttributeValueField.d.ts.map +1 -0
  3. package/dist/components/AttributeValueField/AttributeValueField.js +130 -0
  4. package/dist/components/AttributeValueField/index.d.ts +2 -0
  5. package/dist/components/AttributeValueField/index.d.ts.map +1 -0
  6. package/dist/components/DataTable/DataTable.d.ts +10 -0
  7. package/dist/components/DataTable/DataTable.d.ts.map +1 -0
  8. package/dist/components/DataTable/DataTable.js +177 -0
  9. package/dist/components/DataTable/DataTable.types.d.ts +42 -0
  10. package/dist/components/DataTable/DataTable.types.d.ts.map +1 -0
  11. package/dist/components/DataTable/index.d.ts +3 -0
  12. package/dist/components/DataTable/index.d.ts.map +1 -0
  13. package/dist/components/RichTextEditor/RichTextContent.d.ts +8 -0
  14. package/dist/components/RichTextEditor/RichTextContent.d.ts.map +1 -0
  15. package/dist/components/RichTextEditor/RichTextContent.js +18 -0
  16. package/dist/components/RichTextEditor/RichTextEditor.context.d.ts +6 -0
  17. package/dist/components/RichTextEditor/RichTextEditor.context.d.ts.map +1 -0
  18. package/dist/components/RichTextEditor/RichTextEditor.context.js +14 -0
  19. package/dist/components/RichTextEditor/RichTextEditor.d.ts +4 -0
  20. package/dist/components/RichTextEditor/RichTextEditor.d.ts.map +1 -0
  21. package/dist/components/RichTextEditor/RichTextEditor.helpers.d.ts +28 -0
  22. package/dist/components/RichTextEditor/RichTextEditor.helpers.d.ts.map +1 -0
  23. package/dist/components/RichTextEditor/RichTextEditor.helpers.js +52 -0
  24. package/dist/components/RichTextEditor/RichTextEditor.js +156 -0
  25. package/dist/components/RichTextEditor/RichTextEditor.styles.d.ts +10 -0
  26. package/dist/components/RichTextEditor/RichTextEditor.styles.d.ts.map +1 -0
  27. package/dist/components/RichTextEditor/RichTextEditor.styles.js +95 -0
  28. package/dist/components/RichTextEditor/RichTextEditor.types.d.ts +73 -0
  29. package/dist/components/RichTextEditor/RichTextEditor.types.d.ts.map +1 -0
  30. package/dist/components/RichTextEditor/RichTextJsonContent.d.ts +9 -0
  31. package/dist/components/RichTextEditor/RichTextJsonContent.d.ts.map +1 -0
  32. package/dist/components/RichTextEditor/RichTextJsonContent.js +27 -0
  33. package/dist/components/RichTextEditor/index.d.ts +14 -0
  34. package/dist/components/RichTextEditor/index.d.ts.map +1 -0
  35. package/dist/components/RichTextEditor/subcomponents/RteCharCount.d.ts +4 -0
  36. package/dist/components/RichTextEditor/subcomponents/RteCharCount.d.ts.map +1 -0
  37. package/dist/components/RichTextEditor/subcomponents/RteCharCount.js +37 -0
  38. package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.d.ts +3 -0
  39. package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.d.ts.map +1 -0
  40. package/dist/components/RichTextEditor/subcomponents/RteEmojiPicker.js +274 -0
  41. package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.d.ts +3 -0
  42. package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.d.ts.map +1 -0
  43. package/dist/components/RichTextEditor/subcomponents/RteLinkPopover.js +93 -0
  44. package/dist/components/RichTextEditor/subcomponents/RteToolbar.d.ts +7 -0
  45. package/dist/components/RichTextEditor/subcomponents/RteToolbar.d.ts.map +1 -0
  46. package/dist/components/RichTextEditor/subcomponents/RteToolbar.js +234 -0
  47. package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.d.ts +4 -0
  48. package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.d.ts.map +1 -0
  49. package/dist/components/RichTextEditor/subcomponents/RteToolbarButton.js +37 -0
  50. package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.d.ts +4 -0
  51. package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.d.ts.map +1 -0
  52. package/dist/components/RichTextEditor/subcomponents/RteToolbarGroup.js +13 -0
  53. package/dist/components/Table/Table.d.ts.map +1 -1
  54. package/dist/components/Table/Table.js +14 -7
  55. package/dist/components/Table/Table.styles.js +2 -2
  56. package/dist/components/Table/Table.types.d.ts +1 -0
  57. package/dist/components/Table/Table.types.d.ts.map +1 -1
  58. package/dist/components/Table/TablePagination.js +3 -3
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +15 -0
  62. package/dist/node_modules/.pnpm/@tanstack_react-table@8.21._59ea1c5c486c125464e3b5e1938c78d1/node_modules/@tanstack/react-table/build/lib/index.js +74 -0
  63. package/dist/node_modules/.pnpm/@tanstack_table-core@8.21.3/node_modules/@tanstack/table-core/build/lib/index.js +3027 -0
  64. 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,3 @@
1
+ import * as React from 'react';
2
+ export declare function RteEmojiPicker(): React.JSX.Element;
3
+ //# sourceMappingURL=RteEmojiPicker.d.ts.map
@@ -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"}