@hamzasaleemorg/convex-comments 1.0.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 (114) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +201 -0
  3. package/README.md +581 -0
  4. package/dist/client/_generated/_ignore.d.ts +1 -0
  5. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  6. package/dist/client/_generated/_ignore.js +3 -0
  7. package/dist/client/_generated/_ignore.js.map +1 -0
  8. package/dist/client/index.d.ts +745 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client/index.js +579 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/component/_generated/api.d.ts +44 -0
  13. package/dist/component/_generated/api.d.ts.map +1 -0
  14. package/dist/component/_generated/api.js +31 -0
  15. package/dist/component/_generated/api.js.map +1 -0
  16. package/dist/component/_generated/component.d.ts +673 -0
  17. package/dist/component/_generated/component.d.ts.map +1 -0
  18. package/dist/component/_generated/component.js +11 -0
  19. package/dist/component/_generated/component.js.map +1 -0
  20. package/dist/component/_generated/dataModel.d.ts +46 -0
  21. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  22. package/dist/component/_generated/dataModel.js +11 -0
  23. package/dist/component/_generated/dataModel.js.map +1 -0
  24. package/dist/component/_generated/server.d.ts +121 -0
  25. package/dist/component/_generated/server.d.ts.map +1 -0
  26. package/dist/component/_generated/server.js +78 -0
  27. package/dist/component/_generated/server.js.map +1 -0
  28. package/dist/component/convex.config.d.ts +3 -0
  29. package/dist/component/convex.config.d.ts.map +1 -0
  30. package/dist/component/convex.config.js +3 -0
  31. package/dist/component/convex.config.js.map +1 -0
  32. package/dist/component/lib.d.ts +17 -0
  33. package/dist/component/lib.d.ts.map +1 -0
  34. package/dist/component/lib.js +18 -0
  35. package/dist/component/lib.js.map +1 -0
  36. package/dist/component/messages.d.ts +173 -0
  37. package/dist/component/messages.d.ts.map +1 -0
  38. package/dist/component/messages.js +410 -0
  39. package/dist/component/messages.js.map +1 -0
  40. package/dist/component/reactions.d.ts +51 -0
  41. package/dist/component/reactions.d.ts.map +1 -0
  42. package/dist/component/reactions.js +191 -0
  43. package/dist/component/reactions.js.map +1 -0
  44. package/dist/component/schema.d.ts +274 -0
  45. package/dist/component/schema.d.ts.map +1 -0
  46. package/dist/component/schema.js +159 -0
  47. package/dist/component/schema.js.map +1 -0
  48. package/dist/component/threads.d.ts +110 -0
  49. package/dist/component/threads.d.ts.map +1 -0
  50. package/dist/component/threads.js +276 -0
  51. package/dist/component/threads.js.map +1 -0
  52. package/dist/component/typing.d.ts +31 -0
  53. package/dist/component/typing.d.ts.map +1 -0
  54. package/dist/component/typing.js +147 -0
  55. package/dist/component/typing.js.map +1 -0
  56. package/dist/component/zones.d.ts +63 -0
  57. package/dist/component/zones.d.ts.map +1 -0
  58. package/dist/component/zones.js +159 -0
  59. package/dist/component/zones.js.map +1 -0
  60. package/dist/react/AddComment.d.ts +57 -0
  61. package/dist/react/AddComment.d.ts.map +1 -0
  62. package/dist/react/AddComment.js +285 -0
  63. package/dist/react/AddComment.js.map +1 -0
  64. package/dist/react/Comment.d.ts +70 -0
  65. package/dist/react/Comment.d.ts.map +1 -0
  66. package/dist/react/Comment.js +259 -0
  67. package/dist/react/Comment.js.map +1 -0
  68. package/dist/react/Comments.d.ts +74 -0
  69. package/dist/react/Comments.d.ts.map +1 -0
  70. package/dist/react/Comments.js +108 -0
  71. package/dist/react/Comments.js.map +1 -0
  72. package/dist/react/CommentsProvider.d.ts +104 -0
  73. package/dist/react/CommentsProvider.d.ts.map +1 -0
  74. package/dist/react/CommentsProvider.js +98 -0
  75. package/dist/react/CommentsProvider.js.map +1 -0
  76. package/dist/react/ReactionPicker.d.ts +28 -0
  77. package/dist/react/ReactionPicker.d.ts.map +1 -0
  78. package/dist/react/ReactionPicker.js +56 -0
  79. package/dist/react/ReactionPicker.js.map +1 -0
  80. package/dist/react/Thread.d.ts +84 -0
  81. package/dist/react/Thread.d.ts.map +1 -0
  82. package/dist/react/Thread.js +124 -0
  83. package/dist/react/Thread.js.map +1 -0
  84. package/dist/react/TypingIndicator.d.ts +25 -0
  85. package/dist/react/TypingIndicator.d.ts.map +1 -0
  86. package/dist/react/TypingIndicator.js +99 -0
  87. package/dist/react/TypingIndicator.js.map +1 -0
  88. package/dist/react/index.d.ts +15 -0
  89. package/dist/react/index.d.ts.map +1 -0
  90. package/dist/react/index.js +15 -0
  91. package/dist/react/index.js.map +1 -0
  92. package/package.json +106 -0
  93. package/src/client/_generated/_ignore.ts +1 -0
  94. package/src/client/index.ts +813 -0
  95. package/src/component/_generated/api.ts +60 -0
  96. package/src/component/_generated/component.ts +784 -0
  97. package/src/component/_generated/dataModel.ts +60 -0
  98. package/src/component/_generated/server.ts +156 -0
  99. package/src/component/convex.config.ts +3 -0
  100. package/src/component/lib.ts +57 -0
  101. package/src/component/messages.ts +476 -0
  102. package/src/component/reactions.ts +222 -0
  103. package/src/component/schema.ts +169 -0
  104. package/src/component/threads.ts +319 -0
  105. package/src/component/typing.ts +168 -0
  106. package/src/component/zones.ts +180 -0
  107. package/src/react/AddComment.tsx +463 -0
  108. package/src/react/Comment.tsx +519 -0
  109. package/src/react/Comments.tsx +276 -0
  110. package/src/react/CommentsProvider.tsx +197 -0
  111. package/src/react/ReactionPicker.tsx +95 -0
  112. package/src/react/Thread.tsx +336 -0
  113. package/src/react/TypingIndicator.tsx +144 -0
  114. package/src/react/index.ts +45 -0
@@ -0,0 +1,98 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Comments Context Provider
4
+ *
5
+ * Provides the Comments API instance to all child components.
6
+ */
7
+ import { createContext, useContext, useMemo } from "react";
8
+ // ============================================================================
9
+ // Default Theme
10
+ // ============================================================================
11
+ const defaultStyles = {
12
+ accentColor: "#3b82f6",
13
+ backgroundColor: "#ffffff",
14
+ textColor: "#1f2937",
15
+ textColorMuted: "#6b7280",
16
+ borderColor: "#e5e7eb",
17
+ borderRadius: "8px",
18
+ fontFamily: "system-ui, -apple-system, sans-serif",
19
+ fontSize: "14px",
20
+ };
21
+ // ============================================================================
22
+ // Context
23
+ // ============================================================================
24
+ const CommentsContext = createContext(null);
25
+ /**
26
+ * Provider component that supplies comments configuration to all child components.
27
+ *
28
+ * Usage:
29
+ * ```tsx
30
+ * <CommentsProvider
31
+ * userId={currentUserId}
32
+ * resolveUser={async (id) => ({ name: users[id].name, avatar: users[id].avatar })}
33
+ * reactionChoices={["👍", "❤️", "😄", "🎉", "😮", "😢"]}
34
+ * styles={{
35
+ * accentColor: "#8b5cf6",
36
+ * borderRadius: "12px",
37
+ * }}
38
+ * >
39
+ * <Comments threads={threads} />
40
+ * </CommentsProvider>
41
+ * ```
42
+ *
43
+ * CSS Variables (can be set in your CSS):
44
+ * - --comments-accent-color
45
+ * - --comments-bg-color
46
+ * - --comments-text-color
47
+ * - --comments-text-color-muted
48
+ * - --comments-border-color
49
+ * - --comments-border-radius
50
+ * - --comments-font-family
51
+ * - --comments-font-size
52
+ */
53
+ export function CommentsProvider({ userId, resolveUser, reactionChoices = ["👍", "❤️", "😄", "🎉", "😮", "😢", "👀", "🚀"], canModerate = false, styles, className, children, }) {
54
+ const mergedStyles = useMemo(() => ({
55
+ ...defaultStyles,
56
+ ...styles,
57
+ }), [styles]);
58
+ const cssVars = useMemo(() => ({
59
+ "--comments-accent-color": mergedStyles.accentColor,
60
+ "--comments-bg-color": mergedStyles.backgroundColor,
61
+ "--comments-text-color": mergedStyles.textColor,
62
+ "--comments-text-color-muted": mergedStyles.textColorMuted,
63
+ "--comments-border-color": mergedStyles.borderColor,
64
+ "--comments-border-radius": mergedStyles.borderRadius,
65
+ "--comments-font-family": mergedStyles.fontFamily,
66
+ "--comments-font-size": mergedStyles.fontSize,
67
+ }), [mergedStyles]);
68
+ const value = useMemo(() => ({
69
+ userId,
70
+ resolveUser,
71
+ reactionChoices,
72
+ canModerate,
73
+ styles: mergedStyles,
74
+ cssVars,
75
+ }), [userId, resolveUser, reactionChoices, canModerate, mergedStyles, cssVars]);
76
+ const wrapperStyle = {
77
+ ...cssVars,
78
+ fontFamily: mergedStyles.fontFamily,
79
+ fontSize: mergedStyles.fontSize,
80
+ color: mergedStyles.textColor,
81
+ };
82
+ return (_jsx(CommentsContext.Provider, { value: value, children: _jsx("div", { className: `comments-provider ${className ?? ""}`, style: wrapperStyle, "data-comments-provider": "", children: children }) }));
83
+ }
84
+ // ============================================================================
85
+ // Hook
86
+ // ============================================================================
87
+ /**
88
+ * Hook to access the comments context.
89
+ * Must be used within a CommentsProvider.
90
+ */
91
+ export function useComments() {
92
+ const context = useContext(CommentsContext);
93
+ if (!context) {
94
+ throw new Error("useComments must be used within a CommentsProvider");
95
+ }
96
+ return context;
97
+ }
98
+ //# sourceMappingURL=CommentsProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommentsProvider.js","sourceRoot":"","sources":["../../src/react/CommentsProvider.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAsC,MAAM,OAAO,CAAC;AAwC/F,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,MAAM,aAAa,GAA6B;IAC5C,WAAW,EAAE,SAAS;IACtB,eAAe,EAAE,SAAS;IAC1B,SAAS,EAAE,SAAS;IACpB,cAAc,EAAE,SAAS;IACzB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,KAAK;IACnB,UAAU,EAAE,sCAAsC;IAClD,QAAQ,EAAE,MAAM;CACnB,CAAC;AAEF,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,MAAM,eAAe,GAAG,aAAa,CAA8B,IAAI,CAAC,CAAC;AA0BzE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC7B,MAAM,EACN,WAAW,EACX,eAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,EAClE,WAAW,GAAG,KAAK,EACnB,MAAM,EACN,SAAS,EACT,QAAQ,GACY;IACpB,MAAM,YAAY,GAAG,OAAO,CACxB,GAAG,EAAE,CAAC,CAAC;QACH,GAAG,aAAa;QAChB,GAAG,MAAM;KACZ,CAAC,EACF,CAAC,MAAM,CAAC,CACX,CAAC;IAEF,MAAM,OAAO,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACH,yBAAyB,EAAE,YAAY,CAAC,WAAW;QACnD,qBAAqB,EAAE,YAAY,CAAC,eAAe;QACnD,uBAAuB,EAAE,YAAY,CAAC,SAAS;QAC/C,6BAA6B,EAAE,YAAY,CAAC,cAAc;QAC1D,yBAAyB,EAAE,YAAY,CAAC,WAAW;QACnD,0BAA0B,EAAE,YAAY,CAAC,YAAY;QACrD,wBAAwB,EAAE,YAAY,CAAC,UAAU;QACjD,sBAAsB,EAAE,YAAY,CAAC,QAAQ;KAChD,CAAC,EACF,CAAC,YAAY,CAAC,CACjB,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CACjB,GAAG,EAAE,CAAC,CAAC;QACH,MAAM;QACN,WAAW;QACX,eAAe;QACf,WAAW;QACX,MAAM,EAAE,YAAY;QACpB,OAAO;KACV,CAAC,EACF,CAAC,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAC7E,CAAC;IAEF,MAAM,YAAY,GAAkB;QAChC,GAAG,OAAwB;QAC3B,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,QAAQ,EAAE,YAAY,CAAC,QAAQ;QAC/B,KAAK,EAAE,YAAY,CAAC,SAAS;KAChC,CAAC;IAEF,OAAO,CACH,KAAC,eAAe,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAClC,cACI,SAAS,EAAE,qBAAqB,SAAS,IAAI,EAAE,EAAE,EACjD,KAAK,EAAE,YAAY,4BACI,EAAE,YAExB,QAAQ,GACP,GACiB,CAC9B,CAAC;AACN,CAAC;AAED,+EAA+E;AAC/E,OAAO;AACP,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,UAAU,WAAW;IACvB,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ReactionPicker Component
3
+ *
4
+ * A popup picker for selecting emoji reactions.
5
+ */
6
+ export interface ReactionPickerProps {
7
+ /** Callback when an emoji is selected */
8
+ onSelect: (emoji: string) => void;
9
+ /** Callback when the picker is closed */
10
+ onClose?: () => void;
11
+ /** Custom reaction choices (overrides provider) */
12
+ reactionChoices?: string[];
13
+ /** CSS class name */
14
+ className?: string;
15
+ }
16
+ /**
17
+ * Emoji reaction picker popup.
18
+ *
19
+ * Usage:
20
+ * ```tsx
21
+ * <ReactionPicker
22
+ * onSelect={(emoji) => toggleReaction({ messageId, emoji })}
23
+ * onClose={() => setShowPicker(false)}
24
+ * />
25
+ * ```
26
+ */
27
+ export declare function ReactionPicker({ onSelect, onClose, reactionChoices: customChoices, className, }: ReactionPickerProps): import("react/jsx-runtime").JSX.Element;
28
+ //# sourceMappingURL=ReactionPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactionPicker.d.ts","sourceRoot":"","sources":["../../src/react/ReactionPicker.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,MAAM,WAAW,mBAAmB;IAChC,yCAAyC;IACzC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mDAAmD;IACnD,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,eAAe,EAAE,aAAa,EAC9B,SAAc,GACjB,EAAE,mBAAmB,2CAmDrB"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * ReactionPicker Component
4
+ *
5
+ * A popup picker for selecting emoji reactions.
6
+ */
7
+ import { useComments } from "./CommentsProvider";
8
+ // ============================================================================
9
+ // Component
10
+ // ============================================================================
11
+ /**
12
+ * Emoji reaction picker popup.
13
+ *
14
+ * Usage:
15
+ * ```tsx
16
+ * <ReactionPicker
17
+ * onSelect={(emoji) => toggleReaction({ messageId, emoji })}
18
+ * onClose={() => setShowPicker(false)}
19
+ * />
20
+ * ```
21
+ */
22
+ export function ReactionPicker({ onSelect, onClose, reactionChoices: customChoices, className = "", }) {
23
+ const { reactionChoices: providerChoices, styles } = useComments();
24
+ const choices = customChoices ?? providerChoices;
25
+ return (_jsx("div", { className: `reaction-picker ${className}`, style: {
26
+ display: "flex",
27
+ flexWrap: "wrap",
28
+ gap: "4px",
29
+ padding: "8px",
30
+ backgroundColor: "white",
31
+ borderRadius: styles?.borderRadius ?? "8px",
32
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
33
+ border: "1px solid #e5e7eb",
34
+ maxWidth: "200px",
35
+ }, children: choices.map((emoji) => (_jsx("button", { onClick: () => {
36
+ onSelect(emoji);
37
+ onClose?.();
38
+ }, style: {
39
+ width: "32px",
40
+ height: "32px",
41
+ display: "flex",
42
+ alignItems: "center",
43
+ justifyContent: "center",
44
+ border: "none",
45
+ background: "transparent",
46
+ borderRadius: "4px",
47
+ cursor: "pointer",
48
+ fontSize: "18px",
49
+ transition: "background-color 0.15s",
50
+ }, onMouseEnter: (e) => {
51
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
52
+ }, onMouseLeave: (e) => {
53
+ e.currentTarget.style.backgroundColor = "transparent";
54
+ }, children: emoji }, emoji))) }));
55
+ }
56
+ //# sourceMappingURL=ReactionPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactionPicker.js","sourceRoot":"","sources":["../../src/react/ReactionPicker.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAiBjD,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,eAAe,EAAE,aAAa,EAC9B,SAAS,GAAG,EAAE,GACI;IAClB,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IACnE,MAAM,OAAO,GAAG,aAAa,IAAI,eAAe,CAAC;IAEjD,OAAO,CACH,cACI,SAAS,EAAE,mBAAmB,SAAS,EAAE,EACzC,KAAK,EAAE;YACH,OAAO,EAAE,MAAM;YACf,QAAQ,EAAE,MAAM;YAChB,GAAG,EAAE,KAAK;YACV,OAAO,EAAE,KAAK;YACd,eAAe,EAAE,OAAO;YACxB,YAAY,EAAE,MAAM,EAAE,YAAY,IAAI,KAAK;YAC3C,SAAS,EAAE,6BAA6B;YACxC,MAAM,EAAE,mBAAmB;YAC3B,QAAQ,EAAE,OAAO;SACpB,YAEA,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACpB,iBAEI,OAAO,EAAE,GAAG,EAAE;gBACV,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAChB,OAAO,EAAE,EAAE,CAAC;YAChB,CAAC,EACD,KAAK,EAAE;gBACH,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,MAAM;gBACf,UAAU,EAAE,QAAQ;gBACpB,cAAc,EAAE,QAAQ;gBACxB,MAAM,EAAE,MAAM;gBACd,UAAU,EAAE,aAAa;gBACzB,YAAY,EAAE,KAAK;gBACnB,MAAM,EAAE,SAAS;gBACjB,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,wBAAwB;aACvC,EACD,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE;gBAChB,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,GAAG,SAAS,CAAC;YACtD,CAAC,EACD,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE;gBAChB,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,GAAG,aAAa,CAAC;YAC1D,CAAC,YAEA,KAAK,IAzBD,KAAK,CA0BL,CACZ,CAAC,GACA,CACT,CAAC;AACN,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Thread Component
3
+ *
4
+ * Displays a single thread with all its messages.
5
+ */
6
+ import { type ReactNode } from "react";
7
+ import { type MessageData } from "./Comment";
8
+ export interface ThreadInfo {
9
+ _id: string;
10
+ zoneId: string;
11
+ resolved: boolean;
12
+ resolvedBy?: string;
13
+ resolvedAt?: number;
14
+ createdAt: number;
15
+ lastActivityAt: number;
16
+ position?: {
17
+ x: number;
18
+ y: number;
19
+ anchor?: string;
20
+ };
21
+ metadata?: unknown;
22
+ }
23
+ export interface ThreadProps {
24
+ /** Thread data */
25
+ thread: ThreadInfo;
26
+ /** Messages in this thread */
27
+ messages: MessageData[];
28
+ /** Typing users in this thread */
29
+ typingUsers?: Array<{
30
+ userId: string;
31
+ updatedAt: number;
32
+ }>;
33
+ /** Whether there are more messages to load */
34
+ hasMore?: boolean;
35
+ /** Loading state */
36
+ isLoading?: boolean;
37
+ /** Callback when load more is triggered */
38
+ onLoadMore?: () => void;
39
+ /** Callback when a new message is submitted */
40
+ onSubmit?: (body: string, attachments?: Array<{
41
+ type: "url" | "file" | "image";
42
+ url: string;
43
+ name?: string;
44
+ }>) => void;
45
+ /** Callback when typing state changes */
46
+ onTypingChange?: (isTyping: boolean) => void;
47
+ /** Callback when a reaction is toggled */
48
+ onToggleReaction?: (messageId: string, emoji: string) => void;
49
+ /** Callback when a message is edited */
50
+ onEditMessage?: (messageId: string, newBody: string) => void;
51
+ /** Callback when a message is deleted */
52
+ onDeleteMessage?: (messageId: string) => void;
53
+ /** Callback to resolve the thread */
54
+ onResolve?: () => void;
55
+ /** Callback to unresolve the thread */
56
+ onUnresolve?: () => void;
57
+ /** Callback to close/go back */
58
+ onClose?: () => void;
59
+ /** Whether editing is allowed */
60
+ allowEditing?: boolean;
61
+ /** Whether to auto-scroll to bottom on new messages */
62
+ autoScroll?: boolean;
63
+ /** Custom message renderer */
64
+ renderMessage?: (message: MessageData, mine: boolean) => ReactNode;
65
+ /** CSS class name */
66
+ className?: string;
67
+ }
68
+ /**
69
+ * Component to display a single thread with its messages.
70
+ *
71
+ * Usage:
72
+ * ```tsx
73
+ * <Thread
74
+ * thread={thread}
75
+ * messages={messages}
76
+ * typingUsers={typingUsers}
77
+ * onSubmit={(body) => addComment({ threadId: thread._id, body })}
78
+ * onTypingChange={(isTyping) => setIsTyping({ threadId: thread._id, isTyping })}
79
+ * onToggleReaction={(messageId, emoji) => toggleReaction({ messageId, emoji })}
80
+ * />
81
+ * ```
82
+ */
83
+ export declare function Thread({ thread, messages, typingUsers, hasMore, isLoading, onLoadMore, onSubmit, onTypingChange, onToggleReaction, onEditMessage, onDeleteMessage, onResolve, onUnresolve, onClose, allowEditing, autoScroll, renderMessage, className, }: ThreadProps): import("react/jsx-runtime").JSX.Element;
84
+ //# sourceMappingURL=Thread.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Thread.d.ts","sourceRoot":"","sources":["../../src/react/Thread.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1D,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAQtD,MAAM,WAAW,UAAU;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,kBAAkB;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,8BAA8B;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,kCAAkC;IAClC,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,8CAA8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IACvH,yCAAyC;IACzC,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9D,wCAAwC;IACxC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,yCAAyC;IACzC,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC;IACnE,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,EACnB,MAAM,EACN,QAAQ,EACR,WAAgB,EAChB,OAAe,EACf,SAAiB,EACjB,UAAU,EACV,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,SAAS,EACT,WAAW,EACX,OAAO,EACP,YAAmB,EACnB,UAAiB,EACjB,aAAa,EACb,SAAc,GACjB,EAAE,WAAW,2CAsOb"}
@@ -0,0 +1,124 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Thread Component
4
+ *
5
+ * Displays a single thread with all its messages.
6
+ */
7
+ import { useEffect, useRef } from "react";
8
+ import { useComments } from "./CommentsProvider";
9
+ import { Comment } from "./Comment";
10
+ import { AddComment } from "./AddComment";
11
+ import { TypingIndicator } from "./TypingIndicator";
12
+ // ============================================================================
13
+ // Component
14
+ // ============================================================================
15
+ /**
16
+ * Component to display a single thread with its messages.
17
+ *
18
+ * Usage:
19
+ * ```tsx
20
+ * <Thread
21
+ * thread={thread}
22
+ * messages={messages}
23
+ * typingUsers={typingUsers}
24
+ * onSubmit={(body) => addComment({ threadId: thread._id, body })}
25
+ * onTypingChange={(isTyping) => setIsTyping({ threadId: thread._id, isTyping })}
26
+ * onToggleReaction={(messageId, emoji) => toggleReaction({ messageId, emoji })}
27
+ * />
28
+ * ```
29
+ */
30
+ export function Thread({ thread, messages, typingUsers = [], hasMore = false, isLoading = false, onLoadMore, onSubmit, onTypingChange, onToggleReaction, onEditMessage, onDeleteMessage, onResolve, onUnresolve, onClose, allowEditing = true, autoScroll = true, renderMessage, className = "", }) {
31
+ const { userId, styles } = useComments();
32
+ const messagesEndRef = useRef(null);
33
+ const prevMessageCount = useRef(messages.length);
34
+ // Auto-scroll to bottom on new messages
35
+ useEffect(() => {
36
+ if (autoScroll && messages.length > prevMessageCount.current) {
37
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
38
+ }
39
+ prevMessageCount.current = messages.length;
40
+ }, [messages.length, autoScroll]);
41
+ const handleSubmit = (body, attachments) => {
42
+ onSubmit?.(body, attachments);
43
+ };
44
+ return (_jsxs("div", { className: `thread-container ${className}`, style: {
45
+ fontFamily: styles?.fontFamily ?? "system-ui, -apple-system, sans-serif",
46
+ display: "flex",
47
+ flexDirection: "column",
48
+ height: "100%",
49
+ border: "1px solid #e5e7eb",
50
+ borderRadius: styles?.borderRadius ?? "8px",
51
+ overflow: "hidden",
52
+ }, children: [_jsxs("div", { style: {
53
+ padding: "12px 16px",
54
+ borderBottom: "1px solid #e5e7eb",
55
+ display: "flex",
56
+ justifyContent: "space-between",
57
+ alignItems: "center",
58
+ backgroundColor: thread.resolved ? "#f0fdf4" : "#f9fafb",
59
+ }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [onClose && (_jsx("button", { onClick: onClose, style: {
60
+ padding: "4px 8px",
61
+ border: "none",
62
+ background: "transparent",
63
+ cursor: "pointer",
64
+ fontSize: "16px",
65
+ }, children: "\u2190" })), _jsxs("span", { style: { fontWeight: 600, fontSize: "14px" }, children: ["Thread", thread.resolved && (_jsx("span", { style: { color: "#16a34a", marginLeft: "8px", fontWeight: 400 }, children: "\u2713 Resolved" }))] })] }), _jsxs("div", { style: { display: "flex", gap: "8px" }, children: [thread.resolved && onUnresolve && (_jsx("button", { onClick: onUnresolve, style: {
66
+ padding: "6px 12px",
67
+ fontSize: "13px",
68
+ border: "1px solid #d1d5db",
69
+ borderRadius: "6px",
70
+ background: "white",
71
+ cursor: "pointer",
72
+ }, children: "Reopen" })), !thread.resolved && onResolve && userId && (_jsx("button", { onClick: onResolve, style: {
73
+ padding: "6px 12px",
74
+ fontSize: "13px",
75
+ fontWeight: 500,
76
+ border: "none",
77
+ borderRadius: "6px",
78
+ backgroundColor: "#16a34a",
79
+ color: "white",
80
+ cursor: "pointer",
81
+ }, children: "Resolve" }))] })] }), _jsxs("div", { style: {
82
+ flex: 1,
83
+ overflowY: "auto",
84
+ padding: "16px",
85
+ }, children: [hasMore && (_jsx("div", { style: { textAlign: "center", marginBottom: "16px" }, children: _jsx("button", { onClick: onLoadMore, disabled: isLoading, style: {
86
+ padding: "8px 16px",
87
+ fontSize: "13px",
88
+ border: "1px solid #d1d5db",
89
+ borderRadius: "6px",
90
+ background: "white",
91
+ cursor: isLoading ? "not-allowed" : "pointer",
92
+ opacity: isLoading ? 0.6 : 1,
93
+ }, children: isLoading ? "Loading..." : "Load Earlier Messages" }) })), messages.length === 0 ? (_jsx("div", { style: {
94
+ textAlign: "center",
95
+ color: "#6b7280",
96
+ fontSize: "14px",
97
+ padding: "40px 0",
98
+ }, children: "No messages yet. Start the conversation!" })) : (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px" }, children: messages.map((message) => {
99
+ const isMine = userId === message.message.authorId;
100
+ return renderMessage ? (renderMessage(message, isMine)) : (_jsx(Comment, { comment: message, mine: isMine, onToggleReaction: onToggleReaction
101
+ ? (emoji) => onToggleReaction(message.message._id, emoji)
102
+ : undefined, onEdit: onEditMessage && isMine
103
+ ? (newBody) => onEditMessage(message.message._id, newBody)
104
+ : undefined, onDelete: onDeleteMessage && isMine
105
+ ? () => onDeleteMessage(message.message._id)
106
+ : undefined }, message.message._id));
107
+ }) })), typingUsers.length > 0 && (_jsx("div", { style: { marginTop: "12px" }, children: _jsx(TypingIndicator, { users: typingUsers.map((u) => u.userId) }) })), _jsx("div", { ref: messagesEndRef })] }), userId && !thread.resolved && (_jsx("div", { style: { borderTop: "1px solid #e5e7eb" }, children: _jsx(AddComment, { onSubmit: handleSubmit, onTypingChange: onTypingChange, allowEditing: allowEditing, placeholder: "Write a reply..." }) })), thread.resolved && (_jsxs("div", { style: {
108
+ padding: "12px 16px",
109
+ borderTop: "1px solid #e5e7eb",
110
+ textAlign: "center",
111
+ color: "#6b7280",
112
+ fontSize: "13px",
113
+ backgroundColor: "#f9fafb",
114
+ }, children: ["This thread has been resolved.", onUnresolve && (_jsx("button", { onClick: onUnresolve, style: {
115
+ marginLeft: "8px",
116
+ padding: "0",
117
+ border: "none",
118
+ background: "transparent",
119
+ color: styles?.accentColor ?? "#3b82f6",
120
+ cursor: "pointer",
121
+ textDecoration: "underline",
122
+ }, children: "Reopen" }))] }))] }));
123
+ }
124
+ //# sourceMappingURL=Thread.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Thread.js","sourceRoot":"","sources":["../../src/react/Thread.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * TypingIndicator Component
3
+ *
4
+ * Shows who is currently typing in a thread.
5
+ */
6
+ export interface TypingIndicatorProps {
7
+ /** User IDs currently typing */
8
+ users: string[];
9
+ /** Max number of names to show before "and X others" */
10
+ maxNamesToShow?: number;
11
+ /** CSS class name */
12
+ className?: string;
13
+ }
14
+ /**
15
+ * Animated typing indicator showing who is typing.
16
+ *
17
+ * Usage:
18
+ * ```tsx
19
+ * const typingUsers = useQuery(api.comments.getTypingUsers, { threadId });
20
+ *
21
+ * <TypingIndicator users={typingUsers.map(u => u.userId)} />
22
+ * ```
23
+ */
24
+ export declare function TypingIndicator({ users, maxNamesToShow, className, }: TypingIndicatorProps): import("react/jsx-runtime").JSX.Element | null;
25
+ //# sourceMappingURL=TypingIndicator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TypingIndicator.d.ts","sourceRoot":"","sources":["../../src/react/TypingIndicator.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,WAAW,oBAAoB;IACjC,gCAAgC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,EAC5B,KAAK,EACL,cAAkB,EAClB,SAAc,GACjB,EAAE,oBAAoB,kDAuGtB"}
@@ -0,0 +1,99 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * TypingIndicator Component
4
+ *
5
+ * Shows who is currently typing in a thread.
6
+ */
7
+ import { useState, useEffect } from "react";
8
+ import { useComments } from "./CommentsProvider";
9
+ // ============================================================================
10
+ // Component
11
+ // ============================================================================
12
+ /**
13
+ * Animated typing indicator showing who is typing.
14
+ *
15
+ * Usage:
16
+ * ```tsx
17
+ * const typingUsers = useQuery(api.comments.getTypingUsers, { threadId });
18
+ *
19
+ * <TypingIndicator users={typingUsers.map(u => u.userId)} />
20
+ * ```
21
+ */
22
+ export function TypingIndicator({ users, maxNamesToShow = 3, className = "", }) {
23
+ const { resolveUser } = useComments();
24
+ const [userNames, setUserNames] = useState({});
25
+ // Resolve user names
26
+ useEffect(() => {
27
+ if (resolveUser) {
28
+ users.forEach(async (userId) => {
29
+ if (!userNames[userId]) {
30
+ const info = await resolveUser(userId);
31
+ setUserNames((prev) => ({ ...prev, [userId]: info.name }));
32
+ }
33
+ });
34
+ }
35
+ }, [users, resolveUser, userNames]);
36
+ if (users.length === 0) {
37
+ return null;
38
+ }
39
+ // Format the typing message
40
+ const names = users.map((id) => userNames[id] ?? id);
41
+ let message;
42
+ if (names.length === 1) {
43
+ message = `${names[0]} is typing`;
44
+ }
45
+ else if (names.length <= maxNamesToShow) {
46
+ const lastN = names[names.length - 1];
47
+ const restNames = names.slice(0, -1).join(", ");
48
+ message = `${restNames} and ${lastN} are typing`;
49
+ }
50
+ else {
51
+ const shown = names.slice(0, maxNamesToShow).join(", ");
52
+ const remaining = names.length - maxNamesToShow;
53
+ message = `${shown} and ${remaining} others are typing`;
54
+ }
55
+ return (_jsxs("div", { className: `typing-indicator ${className}`, style: {
56
+ display: "flex",
57
+ alignItems: "center",
58
+ gap: "8px",
59
+ fontSize: "13px",
60
+ color: "#6b7280",
61
+ fontStyle: "italic",
62
+ }, children: [_jsxs("span", { style: {
63
+ display: "inline-flex",
64
+ gap: "2px",
65
+ }, children: [_jsx("span", { style: {
66
+ width: "4px",
67
+ height: "4px",
68
+ borderRadius: "50%",
69
+ backgroundColor: "#9ca3af",
70
+ animation: "typingDot 1.4s infinite ease-in-out",
71
+ animationDelay: "0s",
72
+ } }), _jsx("span", { style: {
73
+ width: "4px",
74
+ height: "4px",
75
+ borderRadius: "50%",
76
+ backgroundColor: "#9ca3af",
77
+ animation: "typingDot 1.4s infinite ease-in-out",
78
+ animationDelay: "0.2s",
79
+ } }), _jsx("span", { style: {
80
+ width: "4px",
81
+ height: "4px",
82
+ borderRadius: "50%",
83
+ backgroundColor: "#9ca3af",
84
+ animation: "typingDot 1.4s infinite ease-in-out",
85
+ animationDelay: "0.4s",
86
+ } })] }), _jsx("span", { children: message }), _jsx("style", { children: `
87
+ @keyframes typingDot {
88
+ 0%, 80%, 100% {
89
+ transform: scale(1);
90
+ opacity: 0.5;
91
+ }
92
+ 40% {
93
+ transform: scale(1.2);
94
+ opacity: 1;
95
+ }
96
+ }
97
+ ` })] }));
98
+ }
99
+ //# sourceMappingURL=TypingIndicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TypingIndicator.js","sourceRoot":"","sources":["../../src/react/TypingIndicator.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Comments Component - React UI
3
+ *
4
+ * Drop-in React components for the comments system.
5
+ * These components provide a fully managed UI for comments,
6
+ * threads, reactions, and typing indicators.
7
+ */
8
+ export { CommentsProvider, useComments, type CommentsContextValue, type CommentsStyles, type CommentsProviderProps, } from "./CommentsProvider";
9
+ export { Comments, type CommentsProps, } from "./Comments";
10
+ export { Thread, type ThreadProps, } from "./Thread";
11
+ export { Comment, type CommentProps, } from "./Comment";
12
+ export { AddComment, type AddCommentProps, } from "./AddComment";
13
+ export { ReactionPicker, type ReactionPickerProps, } from "./ReactionPicker";
14
+ export { TypingIndicator, type TypingIndicatorProps, } from "./TypingIndicator";
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,qBAAqB,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,QAAQ,EACR,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,MAAM,EACN,KAAK,WAAW,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,OAAO,EACP,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,UAAU,EACV,KAAK,eAAe,GACrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,GACzB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Comments Component - React UI
3
+ *
4
+ * Drop-in React components for the comments system.
5
+ * These components provide a fully managed UI for comments,
6
+ * threads, reactions, and typing indicators.
7
+ */
8
+ export { CommentsProvider, useComments, } from "./CommentsProvider";
9
+ export { Comments, } from "./Comments";
10
+ export { Thread, } from "./Thread";
11
+ export { Comment, } from "./Comment";
12
+ export { AddComment, } from "./AddComment";
13
+ export { ReactionPicker, } from "./ReactionPicker";
14
+ export { TypingIndicator, } from "./TypingIndicator";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,gBAAgB,EAChB,WAAW,GAIZ,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,QAAQ,GAET,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,MAAM,GAEP,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,OAAO,GAER,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,UAAU,GAEX,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,cAAc,GAEf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,eAAe,GAEhB,MAAM,mBAAmB,CAAC"}