@focus-reactive/payload-plugin-comments 1.1.0 → 1.2.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 (169) hide show
  1. package/README.md +8 -6
  2. package/dist/api/mutations/useAddCommentMutation.d.ts +18 -0
  3. package/dist/api/mutations/useAddCommentMutation.d.ts.map +1 -0
  4. package/dist/api/mutations/useAddCommentMutation.js +52 -0
  5. package/dist/api/mutations/useAddCommentMutation.js.map +1 -0
  6. package/dist/api/mutations/useDeleteCommentMutation.d.ts +11 -0
  7. package/dist/api/mutations/useDeleteCommentMutation.d.ts.map +1 -0
  8. package/dist/api/mutations/useDeleteCommentMutation.js +31 -0
  9. package/dist/api/mutations/useDeleteCommentMutation.js.map +1 -0
  10. package/dist/api/mutations/useResolveCommentMutation.d.ts +13 -0
  11. package/dist/api/mutations/useResolveCommentMutation.d.ts.map +1 -0
  12. package/dist/api/mutations/useResolveCommentMutation.js +41 -0
  13. package/dist/api/mutations/useResolveCommentMutation.js.map +1 -0
  14. package/dist/api/queries/useCommentsQuery.d.ts +3 -0
  15. package/dist/api/queries/useCommentsQuery.d.ts.map +1 -0
  16. package/dist/api/queries/useCommentsQuery.js +40 -0
  17. package/dist/api/queries/useCommentsQuery.js.map +1 -0
  18. package/dist/api/queries/useDocumentTitlesQuery.d.ts +3 -0
  19. package/dist/api/queries/useDocumentTitlesQuery.d.ts.map +1 -0
  20. package/dist/api/queries/useDocumentTitlesQuery.js +32 -0
  21. package/dist/api/queries/useDocumentTitlesQuery.js.map +1 -0
  22. package/dist/api/queries/useFieldLabelsQuery.d.ts +3 -0
  23. package/dist/api/queries/useFieldLabelsQuery.d.ts.map +1 -0
  24. package/dist/api/queries/useFieldLabelsQuery.js +23 -0
  25. package/dist/api/queries/useFieldLabelsQuery.js.map +1 -0
  26. package/dist/api/queries/useMentionableUsersQuery.d.ts +2 -0
  27. package/dist/api/queries/useMentionableUsersQuery.d.ts.map +1 -0
  28. package/dist/api/queries/useMentionableUsersQuery.js +25 -0
  29. package/dist/api/queries/useMentionableUsersQuery.js.map +1 -0
  30. package/dist/api/queryKeys.d.ts +23 -0
  31. package/dist/api/queryKeys.d.ts.map +1 -0
  32. package/dist/api/queryKeys.js +40 -0
  33. package/dist/api/queryKeys.js.map +1 -0
  34. package/dist/components/CommentEditor/ActionPanel.d.ts +1 -1
  35. package/dist/components/CommentEditor/ActionPanel.d.ts.map +1 -1
  36. package/dist/components/CommentEditor/ActionPanel.js +18 -3
  37. package/dist/components/CommentEditor/ActionPanel.js.map +1 -1
  38. package/dist/components/CommentEditor/index.d.ts.map +1 -1
  39. package/dist/components/CommentEditor/index.js +5 -3
  40. package/dist/components/CommentEditor/index.js.map +1 -1
  41. package/dist/components/CommentItem/StrikethoroughOverlay/clamp.d.ts +2 -0
  42. package/dist/components/CommentItem/StrikethoroughOverlay/clamp.d.ts.map +1 -0
  43. package/dist/components/CommentItem/StrikethoroughOverlay/clamp.js +7 -0
  44. package/dist/components/CommentItem/StrikethoroughOverlay/clamp.js.map +1 -0
  45. package/dist/components/CommentItem/StrikethoroughOverlay/index.d.ts +8 -0
  46. package/dist/components/CommentItem/StrikethoroughOverlay/index.d.ts.map +1 -0
  47. package/dist/components/CommentItem/StrikethoroughOverlay/index.js +24 -0
  48. package/dist/components/CommentItem/StrikethoroughOverlay/index.js.map +1 -0
  49. package/dist/components/CommentItem/StrikethoroughOverlay/measureLineRects.d.ts +3 -0
  50. package/dist/components/CommentItem/StrikethoroughOverlay/measureLineRects.d.ts.map +1 -0
  51. package/dist/components/CommentItem/StrikethoroughOverlay/measureLineRects.js +29 -0
  52. package/dist/components/CommentItem/StrikethoroughOverlay/measureLineRects.js.map +1 -0
  53. package/dist/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.d.ts +7 -0
  54. package/dist/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.d.ts.map +1 -0
  55. package/dist/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.js +114 -0
  56. package/dist/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.js.map +1 -0
  57. package/dist/components/CommentItem/index.d.ts.map +1 -1
  58. package/dist/components/CommentItem/index.js +37 -18
  59. package/dist/components/CommentItem/index.js.map +1 -1
  60. package/dist/components/CommentsDrawer/components/Header.d.ts.map +1 -1
  61. package/dist/components/CommentsDrawer/components/Header.js +4 -33
  62. package/dist/components/CommentsDrawer/components/Header.js.map +1 -1
  63. package/dist/components/CommentsHeaderButton/index.js +2 -2
  64. package/dist/components/CommentsHeaderButton/index.js.map +1 -1
  65. package/dist/components/CommentsPanel/components/CollapsibleGroup.d.ts +7 -2
  66. package/dist/components/CommentsPanel/components/CollapsibleGroup.d.ts.map +1 -1
  67. package/dist/components/CommentsPanel/components/CollapsibleGroup.js +7 -2
  68. package/dist/components/CommentsPanel/components/CollapsibleGroup.js.map +1 -1
  69. package/dist/components/CommentsPanel/components/DocumentView.d.ts.map +1 -1
  70. package/dist/components/CommentsPanel/components/DocumentView.js +11 -9
  71. package/dist/components/CommentsPanel/components/DocumentView.js.map +1 -1
  72. package/dist/components/CommentsPanel/components/FieldGroupSection.d.ts.map +1 -1
  73. package/dist/components/CommentsPanel/components/FieldGroupSection.js +44 -22
  74. package/dist/components/CommentsPanel/components/FieldGroupSection.js.map +1 -1
  75. package/dist/components/CommentsPanel/components/GlobalDocumentView.d.ts.map +1 -1
  76. package/dist/components/CommentsPanel/components/GlobalDocumentView.js +3 -9
  77. package/dist/components/CommentsPanel/components/GlobalDocumentView.js.map +1 -1
  78. package/dist/components/CommentsPanel/components/GlobalView.d.ts.map +1 -1
  79. package/dist/components/CommentsPanel/components/GlobalView.js +38 -41
  80. package/dist/components/CommentsPanel/components/GlobalView.js.map +1 -1
  81. package/dist/components/CommentsPanel/hooks/useCollapseState.d.ts +1 -1
  82. package/dist/components/CommentsPanel/hooks/useCollapseState.d.ts.map +1 -1
  83. package/dist/components/CommentsPanel/hooks/useCollapseState.js +4 -1
  84. package/dist/components/CommentsPanel/hooks/useCollapseState.js.map +1 -1
  85. package/dist/components/CommentsPanel/index.d.ts.map +1 -1
  86. package/dist/components/CommentsPanel/index.js +14 -11
  87. package/dist/components/CommentsPanel/index.js.map +1 -1
  88. package/dist/components/FieldCommentLabel/hooks/useFieldBreadcrumb.d.ts +2 -0
  89. package/dist/components/FieldCommentLabel/hooks/useFieldBreadcrumb.d.ts.map +1 -0
  90. package/dist/components/FieldCommentLabel/hooks/useFieldBreadcrumb.js +52 -0
  91. package/dist/components/FieldCommentLabel/hooks/useFieldBreadcrumb.js.map +1 -0
  92. package/dist/components/FieldCommentLabel/index.d.ts +1 -1
  93. package/dist/components/FieldCommentLabel/index.d.ts.map +1 -1
  94. package/dist/components/FieldCommentLabel/index.js +54 -41
  95. package/dist/components/FieldCommentLabel/index.js.map +1 -1
  96. package/dist/components/FieldCommentLabel/types.d.ts +1 -1
  97. package/dist/components/FieldCommentLabel/types.d.ts.map +1 -1
  98. package/dist/components/FieldCommentLabel/utils/exludeComments.js +2 -2
  99. package/dist/components/FieldCommentLabel/utils/exludeComments.js.map +1 -1
  100. package/dist/components/IconButton/index.d.ts +2 -2
  101. package/dist/components/IconButton/index.d.ts.map +1 -1
  102. package/dist/components/IconButton/index.js +11 -1
  103. package/dist/components/IconButton/index.js.map +1 -1
  104. package/dist/constants.d.ts +1 -0
  105. package/dist/constants.d.ts.map +1 -1
  106. package/dist/constants.js +3 -4
  107. package/dist/constants.js.map +1 -1
  108. package/dist/plugin.d.ts.map +1 -1
  109. package/dist/plugin.js +6 -2
  110. package/dist/plugin.js.map +1 -1
  111. package/dist/providers/CommentsDrawerProvider/index.d.ts +8 -0
  112. package/dist/providers/CommentsDrawerProvider/index.d.ts.map +1 -1
  113. package/dist/providers/CommentsDrawerProvider/index.js +22 -6
  114. package/dist/providers/CommentsDrawerProvider/index.js.map +1 -1
  115. package/dist/providers/CommentsProvider/index.d.ts +7 -17
  116. package/dist/providers/CommentsProvider/index.d.ts.map +1 -1
  117. package/dist/providers/CommentsProvider/index.js +105 -196
  118. package/dist/providers/CommentsProvider/index.js.map +1 -1
  119. package/dist/providers/CommentsProviderWrapper/index.d.ts +1 -1
  120. package/dist/providers/CommentsProviderWrapper/index.d.ts.map +1 -1
  121. package/dist/providers/CommentsProviderWrapper/index.js +11 -1
  122. package/dist/providers/CommentsProviderWrapper/index.js.map +1 -1
  123. package/dist/services/findAllComments.d.ts +4 -1
  124. package/dist/services/findAllComments.d.ts.map +1 -1
  125. package/dist/services/findAllComments.js +36 -10
  126. package/dist/services/findAllComments.js.map +1 -1
  127. package/dist/styles.css +1 -1
  128. package/dist/translations/en.d.ts.map +1 -1
  129. package/dist/translations/en.js +3 -7
  130. package/dist/translations/en.js.map +1 -1
  131. package/dist/translations/types.d.ts +2 -6
  132. package/dist/translations/types.d.ts.map +1 -1
  133. package/dist/types/general.d.ts +0 -2
  134. package/dist/types/general.d.ts.map +1 -1
  135. package/dist/types/index.d.ts +3 -2
  136. package/dist/types/index.d.ts.map +1 -1
  137. package/dist/types/query.d.ts +11 -0
  138. package/dist/types/query.d.ts.map +1 -0
  139. package/dist/types/query.js +1 -0
  140. package/dist/types/query.js.map +1 -0
  141. package/dist/utils/query/toQueryContext.d.ts +3 -0
  142. package/dist/utils/query/toQueryContext.d.ts.map +1 -0
  143. package/dist/utils/query/toQueryContext.js +20 -0
  144. package/dist/utils/query/toQueryContext.js.map +1 -0
  145. package/package.json +3 -6
  146. package/dist/components/CommentsPanel/constants.d.ts +0 -3
  147. package/dist/components/CommentsPanel/constants.d.ts.map +0 -1
  148. package/dist/components/CommentsPanel/constants.js +0 -9
  149. package/dist/components/CommentsPanel/constants.js.map +0 -1
  150. package/dist/components/CommentsPanel/utils/filterComments.d.ts +0 -9
  151. package/dist/components/CommentsPanel/utils/filterComments.d.ts.map +0 -1
  152. package/dist/components/CommentsPanel/utils/filterComments.js +0 -17
  153. package/dist/components/CommentsPanel/utils/filterComments.js.map +0 -1
  154. package/dist/components/FieldCommentLabel/AddCommentPopup.d.ts +0 -8
  155. package/dist/components/FieldCommentLabel/AddCommentPopup.d.ts.map +0 -1
  156. package/dist/components/FieldCommentLabel/AddCommentPopup.js +0 -50
  157. package/dist/components/FieldCommentLabel/AddCommentPopup.js.map +0 -1
  158. package/dist/providers/GlobalCommentsLoader/GlobalCommentsHydrator.d.ts +0 -13
  159. package/dist/providers/GlobalCommentsLoader/GlobalCommentsHydrator.d.ts.map +0 -1
  160. package/dist/providers/GlobalCommentsLoader/GlobalCommentsHydrator.js +0 -22
  161. package/dist/providers/GlobalCommentsLoader/GlobalCommentsHydrator.js.map +0 -1
  162. package/dist/providers/GlobalCommentsLoader/index.d.ts +0 -10
  163. package/dist/providers/GlobalCommentsLoader/index.d.ts.map +0 -1
  164. package/dist/providers/GlobalCommentsLoader/index.js +0 -31
  165. package/dist/providers/GlobalCommentsLoader/index.js.map +0 -1
  166. package/dist/services/syncAllCommentsData.d.ts +0 -12
  167. package/dist/services/syncAllCommentsData.d.ts.map +0 -1
  168. package/dist/services/syncAllCommentsData.js +0 -48
  169. package/dist/services/syncAllCommentsData.js.map +0 -1
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useState, useRef, useImperativeHandle, startTransition } from "react";
4
4
  import { useAuth, useLocale, useTranslation } from "@payloadcms/ui";
5
5
  import { useComments } from "../../providers/CommentsProvider";
6
+ import { useMentionableUsersQuery } from "../../api/queries/useMentionableUsersQuery";
6
7
  import { MentionDropdown } from "../MentionDropdown";
7
8
  import { MentionLabel } from "../MentionLabel";
8
9
  import { serializeEditor } from "../../utils/comment/serializeEditor";
@@ -25,7 +26,8 @@ function CommentEditor({
25
26
  onEnterPress,
26
27
  onEscapePress
27
28
  }) {
28
- const { mentionUsers: users, addComment } = useComments();
29
+ const { addComment } = useComments();
30
+ const { data: users = [] } = useMentionableUsersQuery();
29
31
  const { code: locale } = useLocale();
30
32
  const { user } = useAuth();
31
33
  const [mentionQuery, setMentionQuery] = useState(null);
@@ -210,13 +212,13 @@ function CommentEditor({
210
212
  "div",
211
213
  {
212
214
  ref: editorWrapperRef,
213
- className: "relative flex-1 group px-2.5 py-2 rounded-md border border-transparent focus-within:border-(--theme-elevation-150) bg-transparent transition-colors",
215
+ className: "relative flex-1 min-w-0 group px-2.5 py-2 rounded-md border border-transparent focus-within:border-(--theme-elevation-150) bg-transparent",
214
216
  children: [
215
217
  /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
216
218
  "div",
217
219
  {
218
220
  className: `
219
- is-empty w-full min-h-5 leading-5
221
+ is-empty w-full min-h-5 max-h-32 overflow-y-auto whitespace-pre-wrap wrap-break-word
220
222
  text-(--theme-text) text-[13px] outline-none box-border
221
223
  [&.is-empty]:before:content-[attr(data-placeholder)] [&.is-empty]:before:text-(--theme-elevation-450)
222
224
  [&.is-empty]:before:pointer-events-none [&.is-empty]:before:absolute
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/CommentEditor/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useRef, useImperativeHandle, type RefObject, startTransition } from \"react\";\nimport { useAuth, useLocale, useTranslation } from \"@payloadcms/ui\";\nimport { useComments } from \"../../providers/CommentsProvider\";\nimport { MentionDropdown } from \"../MentionDropdown\";\nimport { MentionLabel } from \"../MentionLabel\";\nimport { serializeEditor } from \"../../utils/comment/serializeEditor\";\nimport { isSelfMention } from \"../../utils/mention/isSelfMention\";\nimport { createRoot } from \"react-dom/client\";\nimport type { User } from \"../../types\";\nimport { FALLBACK_USERNAME } from \"../../constants\";\nimport { resolveUsername } from \"../../utils/user/resolveUsername\";\nimport { ActionPanel } from \"./ActionPanel\";\nimport { Avatar } from \"../Avatar\";\n\nexport interface CommentEditorHandle {\n getValue: () => string;\n clear: () => void;\n focus: () => void;\n insertAt: () => void;\n}\n\ninterface CommentEditorProps {\n disabled?: boolean;\n autoFocus?: boolean;\n placeholder?: string;\n ref?: RefObject<CommentEditorHandle | null>;\n fieldPath?: string | null;\n documentId?: number;\n collectionSlug?: string;\n globalSlug?: string;\n onSuccessAddComment?: () => void;\n onEnterPress?: () => void;\n onEscapePress?: () => void;\n}\n\nexport function CommentEditor({\n disabled,\n autoFocus,\n placeholder: placeholderProp,\n ref,\n fieldPath,\n documentId,\n collectionSlug,\n globalSlug,\n onSuccessAddComment,\n onEnterPress,\n onEscapePress,\n}: CommentEditorProps) {\n const { mentionUsers: users, addComment } = useComments();\n const { code: locale } = useLocale();\n const { user } = useAuth();\n\n const [mentionQuery, setMentionQuery] = useState<string | null>(null);\n const [triggerRange, setTriggerRange] = useState<Range | null>(null);\n const [filteredUsers, setFilteredUsers] = useState<User[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n const { usernameFieldPath } = useComments();\n const { t } = useTranslation();\n\n const editorRef = useRef<HTMLDivElement>(null);\n const editorWrapperRef = useRef<HTMLDivElement>(null);\n\n const unknownLabel = t(\"comments:unknownAuthor\" as never) ?? FALLBACK_USERNAME;\n const currentUserId = user?.id;\n const placeholder = placeholderProp ?? t(\"comments:writeComment\" as never) ?? \"Add a comment\";\n\n // Methods\n const detectMention = () => {\n const sel = window.getSelection();\n\n if (!sel || !sel.rangeCount) {\n setMentionQuery(null);\n\n return;\n }\n\n const range = sel.getRangeAt(0);\n const node = range.startContainer;\n\n if (node.nodeType !== Node.TEXT_NODE) {\n setMentionQuery(null);\n\n return;\n }\n\n const text = node.textContent ?? \"\";\n const offset = range.startOffset;\n\n let i = offset - 1;\n while (i >= 0 && text[i] !== \" \" && text[i] !== \"\\n\") {\n if (text[i] === \"@\") {\n const query = text.slice(i + 1, offset);\n if (query.includes(\" \")) {\n setMentionQuery(null);\n return;\n }\n\n const atRange = document.createRange();\n atRange.setStart(node, i);\n atRange.setEnd(node, offset);\n setTriggerRange(atRange.cloneRange());\n setMentionQuery(query);\n setFilteredUsers(\n users.filter((u) =>\n resolveUsername(u, usernameFieldPath, unknownLabel).toLowerCase().includes(query.toLowerCase()),\n ),\n );\n setSelectedIndex(0);\n\n return;\n }\n\n i--;\n }\n\n setMentionQuery(null);\n };\n\n const insertMention = (user: User) => {\n if (!triggerRange || !editorRef.current) return;\n\n const sel = window.getSelection();\n if (!sel) return;\n\n sel.removeAllRanges();\n sel.addRange(triggerRange);\n sel.deleteFromDocument();\n\n const { id: userId } = user;\n\n const mentionLabelContainer = document.createElement(\"span\");\n const mentionLabelContainerRoot = createRoot(mentionLabelContainer);\n\n mentionLabelContainerRoot.render(\n <MentionLabel\n name={resolveUsername(user, usernameFieldPath, unknownLabel)}\n isSelf={isSelfMention(currentUserId, userId)}\n />,\n );\n\n mentionLabelContainer.contentEditable = \"false\";\n mentionLabelContainer.dataset.mentionId = String(userId);\n\n const range2 = sel.getRangeAt(0);\n range2.insertNode(mentionLabelContainer);\n\n const zws = document.createTextNode(\"\\u200b\");\n mentionLabelContainer.after(zws);\n\n const newRange = document.createRange();\n newRange.setStartAfter(zws);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n\n setMentionQuery(null);\n setTriggerRange(null);\n editorRef.current?.focus();\n };\n\n const insertAt = () => {\n const editor = editorRef.current;\n if (!editor) return;\n\n editor.focus();\n\n const sel = window.getSelection();\n if (!sel || !sel.rangeCount) {\n const range = document.createRange();\n range.selectNodeContents(editor);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n document.execCommand(\"insertText\", false, \"@\");\n detectMention();\n };\n\n const updateEmptyClass = () => {\n const editor = editorRef.current;\n if (!editor) return;\n\n const isEmpty = editor.innerHTML === \"\" || editor.innerHTML === \"<br>\";\n\n editor.classList.toggle(\"is-empty\", isEmpty);\n };\n\n // Editor API\n const getEditorValue = () => {\n if (!editorRef.current) return \"\";\n\n return serializeEditor(editorRef.current).trim();\n };\n\n const clearEditor = () => {\n setMentionQuery(null);\n setTriggerRange(null);\n\n if (editorRef.current) {\n editorRef.current.innerHTML = \"\";\n editorRef.current.classList.add(\"is-empty\");\n }\n };\n\n const focusEditor = () => {\n editorRef.current?.focus();\n };\n\n useImperativeHandle(ref, () => ({\n getValue: getEditorValue,\n clear: clearEditor,\n focus: focusEditor,\n insertAt,\n }));\n\n // Handlers\n const handleAddComment = () => {\n const serialized = getEditorValue();\n if (!serialized) return;\n\n clearEditor();\n\n startTransition(async () => {\n const res = await addComment(serialized, fieldPath, documentId, collectionSlug, locale, globalSlug);\n\n if (res.success) {\n onSuccessAddComment?.();\n }\n });\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (mentionQuery !== null) {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n e.stopPropagation();\n setSelectedIndex((prev) => (prev + 1) % filteredUsers.length);\n return;\n }\n if (e.key === \"ArrowUp\") {\n e.preventDefault();\n e.stopPropagation();\n setSelectedIndex((prev) => (prev - 1 + filteredUsers.length) % filteredUsers.length);\n return;\n }\n if (e.key === \"Enter\" && filteredUsers.length > 0) {\n e.preventDefault();\n const selectedUser = filteredUsers[selectedIndex];\n if (selectedUser) insertMention(selectedUser);\n return;\n }\n if (e.key === \"Escape\") {\n e.stopPropagation();\n setMentionQuery(null);\n setTriggerRange(null);\n return;\n }\n }\n\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n\n handleAddComment();\n onEnterPress?.();\n } else if (e.key === \"Escape\") {\n onEscapePress?.();\n }\n };\n\n const handleInput = () => {\n detectMention();\n updateEmptyClass();\n };\n\n return (\n <div className=\"relative\">\n <div className=\"flex gap-2.5 items-start\">\n <Avatar className=\"shrink-0\" user={user} usernameFieldPath={usernameFieldPath} fallbackName={unknownLabel} />\n\n <div\n ref={editorWrapperRef}\n className=\"relative flex-1 group px-2.5 py-2 rounded-md border border-transparent focus-within:border-(--theme-elevation-150) bg-transparent transition-colors\">\n <div className=\"relative\">\n <div\n className={`\n is-empty w-full min-h-5 leading-5\n text-(--theme-text) text-[13px] outline-none box-border\n [&.is-empty]:before:content-[attr(data-placeholder)] [&.is-empty]:before:text-(--theme-elevation-450)\n [&.is-empty]:before:pointer-events-none [&.is-empty]:before:absolute\n `}\n ref={editorRef}\n contentEditable={!disabled}\n autoFocus={autoFocus}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-label={placeholder}\n data-placeholder={placeholder}\n onInput={handleInput}\n onKeyDown={handleKeyDown}\n />\n </div>\n\n {mentionQuery !== null && (\n <MentionDropdown\n users={filteredUsers}\n selectedIndex={selectedIndex}\n onSelect={insertMention}\n onClose={() => {\n setMentionQuery(null);\n setTriggerRange(null);\n editorRef.current?.focus();\n }}\n anchorRef={editorWrapperRef}\n />\n )}\n\n <ActionPanel\n className=\"hidden group-focus-within:flex\"\n onMention={() => insertAt()}\n onAddComment={handleAddComment}\n />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["user"]}
1
+ {"version":3,"sources":["../../../src/components/CommentEditor/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useRef, useImperativeHandle, type RefObject, startTransition } from \"react\";\nimport { useAuth, useLocale, useTranslation } from \"@payloadcms/ui\";\nimport { useComments } from \"../../providers/CommentsProvider\";\nimport { useMentionableUsersQuery } from \"../../api/queries/useMentionableUsersQuery\";\nimport { MentionDropdown } from \"../MentionDropdown\";\nimport { MentionLabel } from \"../MentionLabel\";\nimport { serializeEditor } from \"../../utils/comment/serializeEditor\";\nimport { isSelfMention } from \"../../utils/mention/isSelfMention\";\nimport { createRoot } from \"react-dom/client\";\nimport type { User } from \"../../types\";\nimport { FALLBACK_USERNAME } from \"../../constants\";\nimport { resolveUsername } from \"../../utils/user/resolveUsername\";\nimport { ActionPanel } from \"./ActionPanel\";\nimport { Avatar } from \"../Avatar\";\n\nexport interface CommentEditorHandle {\n getValue: () => string;\n clear: () => void;\n focus: () => void;\n insertAt: () => void;\n}\n\ninterface CommentEditorProps {\n disabled?: boolean;\n autoFocus?: boolean;\n placeholder?: string;\n ref?: RefObject<CommentEditorHandle | null>;\n fieldPath?: string | null;\n documentId?: number;\n collectionSlug?: string;\n globalSlug?: string;\n onSuccessAddComment?: () => void;\n onEnterPress?: () => void;\n onEscapePress?: () => void;\n}\n\nexport function CommentEditor({\n disabled,\n autoFocus,\n placeholder: placeholderProp,\n ref,\n fieldPath,\n documentId,\n collectionSlug,\n globalSlug,\n onSuccessAddComment,\n onEnterPress,\n onEscapePress,\n}: CommentEditorProps) {\n const { addComment } = useComments();\n const { data: users = [] } = useMentionableUsersQuery();\n const { code: locale } = useLocale();\n const { user } = useAuth();\n\n const [mentionQuery, setMentionQuery] = useState<string | null>(null);\n const [triggerRange, setTriggerRange] = useState<Range | null>(null);\n const [filteredUsers, setFilteredUsers] = useState<User[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n const { usernameFieldPath } = useComments();\n const { t } = useTranslation();\n\n const editorRef = useRef<HTMLDivElement>(null);\n const editorWrapperRef = useRef<HTMLDivElement>(null);\n\n const unknownLabel = t(\"comments:unknownAuthor\" as never) ?? FALLBACK_USERNAME;\n const currentUserId = user?.id;\n const placeholder = placeholderProp ?? t(\"comments:writeComment\" as never) ?? \"Add a comment\";\n\n // Methods\n const detectMention = () => {\n const sel = window.getSelection();\n\n if (!sel || !sel.rangeCount) {\n setMentionQuery(null);\n\n return;\n }\n\n const range = sel.getRangeAt(0);\n const node = range.startContainer;\n\n if (node.nodeType !== Node.TEXT_NODE) {\n setMentionQuery(null);\n\n return;\n }\n\n const text = node.textContent ?? \"\";\n const offset = range.startOffset;\n\n let i = offset - 1;\n while (i >= 0 && text[i] !== \" \" && text[i] !== \"\\n\") {\n if (text[i] === \"@\") {\n const query = text.slice(i + 1, offset);\n if (query.includes(\" \")) {\n setMentionQuery(null);\n return;\n }\n\n const atRange = document.createRange();\n atRange.setStart(node, i);\n atRange.setEnd(node, offset);\n setTriggerRange(atRange.cloneRange());\n setMentionQuery(query);\n setFilteredUsers(\n users.filter((u) =>\n resolveUsername(u, usernameFieldPath, unknownLabel).toLowerCase().includes(query.toLowerCase()),\n ),\n );\n setSelectedIndex(0);\n\n return;\n }\n\n i--;\n }\n\n setMentionQuery(null);\n };\n\n const insertMention = (user: User) => {\n if (!triggerRange || !editorRef.current) return;\n\n const sel = window.getSelection();\n if (!sel) return;\n\n sel.removeAllRanges();\n sel.addRange(triggerRange);\n sel.deleteFromDocument();\n\n const { id: userId } = user;\n\n const mentionLabelContainer = document.createElement(\"span\");\n const mentionLabelContainerRoot = createRoot(mentionLabelContainer);\n\n mentionLabelContainerRoot.render(\n <MentionLabel\n name={resolveUsername(user, usernameFieldPath, unknownLabel)}\n isSelf={isSelfMention(currentUserId, userId)}\n />,\n );\n\n mentionLabelContainer.contentEditable = \"false\";\n mentionLabelContainer.dataset.mentionId = String(userId);\n\n const range2 = sel.getRangeAt(0);\n range2.insertNode(mentionLabelContainer);\n\n const zws = document.createTextNode(\"\\u200b\");\n mentionLabelContainer.after(zws);\n\n const newRange = document.createRange();\n newRange.setStartAfter(zws);\n newRange.collapse(true);\n sel.removeAllRanges();\n sel.addRange(newRange);\n\n setMentionQuery(null);\n setTriggerRange(null);\n editorRef.current?.focus();\n };\n\n const insertAt = () => {\n const editor = editorRef.current;\n if (!editor) return;\n\n editor.focus();\n\n const sel = window.getSelection();\n if (!sel || !sel.rangeCount) {\n const range = document.createRange();\n range.selectNodeContents(editor);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n document.execCommand(\"insertText\", false, \"@\");\n detectMention();\n };\n\n const updateEmptyClass = () => {\n const editor = editorRef.current;\n if (!editor) return;\n\n const isEmpty = editor.innerHTML === \"\" || editor.innerHTML === \"<br>\";\n\n editor.classList.toggle(\"is-empty\", isEmpty);\n };\n\n // Editor API\n const getEditorValue = () => {\n if (!editorRef.current) return \"\";\n\n return serializeEditor(editorRef.current).trim();\n };\n\n const clearEditor = () => {\n setMentionQuery(null);\n setTriggerRange(null);\n\n if (editorRef.current) {\n editorRef.current.innerHTML = \"\";\n editorRef.current.classList.add(\"is-empty\");\n }\n };\n\n const focusEditor = () => {\n editorRef.current?.focus();\n };\n\n useImperativeHandle(ref, () => ({\n getValue: getEditorValue,\n clear: clearEditor,\n focus: focusEditor,\n insertAt,\n }));\n\n // Handlers\n const handleAddComment = () => {\n const serialized = getEditorValue();\n if (!serialized) return;\n\n clearEditor();\n\n startTransition(async () => {\n const res = await addComment(serialized, fieldPath, documentId, collectionSlug, locale, globalSlug);\n\n if (res.success) {\n onSuccessAddComment?.();\n }\n });\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (mentionQuery !== null) {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n e.stopPropagation();\n setSelectedIndex((prev) => (prev + 1) % filteredUsers.length);\n return;\n }\n if (e.key === \"ArrowUp\") {\n e.preventDefault();\n e.stopPropagation();\n setSelectedIndex((prev) => (prev - 1 + filteredUsers.length) % filteredUsers.length);\n return;\n }\n if (e.key === \"Enter\" && filteredUsers.length > 0) {\n e.preventDefault();\n const selectedUser = filteredUsers[selectedIndex];\n if (selectedUser) insertMention(selectedUser);\n return;\n }\n if (e.key === \"Escape\") {\n e.stopPropagation();\n setMentionQuery(null);\n setTriggerRange(null);\n return;\n }\n }\n\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n\n handleAddComment();\n onEnterPress?.();\n } else if (e.key === \"Escape\") {\n onEscapePress?.();\n }\n };\n\n const handleInput = () => {\n detectMention();\n updateEmptyClass();\n };\n\n return (\n <div className=\"relative\">\n <div className=\"flex gap-2.5 items-start\">\n <Avatar className=\"shrink-0\" user={user} usernameFieldPath={usernameFieldPath} fallbackName={unknownLabel} />\n\n <div\n ref={editorWrapperRef}\n className=\"relative flex-1 min-w-0 group px-2.5 py-2 rounded-md border border-transparent focus-within:border-(--theme-elevation-150) bg-transparent\">\n <div className=\"relative\">\n <div\n className={`\n is-empty w-full min-h-5 max-h-32 overflow-y-auto whitespace-pre-wrap wrap-break-word\n text-(--theme-text) text-[13px] outline-none box-border\n [&.is-empty]:before:content-[attr(data-placeholder)] [&.is-empty]:before:text-(--theme-elevation-450)\n [&.is-empty]:before:pointer-events-none [&.is-empty]:before:absolute\n `}\n ref={editorRef}\n contentEditable={!disabled}\n autoFocus={autoFocus}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-label={placeholder}\n data-placeholder={placeholder}\n onInput={handleInput}\n onKeyDown={handleKeyDown}\n />\n </div>\n\n {mentionQuery !== null && (\n <MentionDropdown\n users={filteredUsers}\n selectedIndex={selectedIndex}\n onSelect={insertMention}\n onClose={() => {\n setMentionQuery(null);\n setTriggerRange(null);\n editorRef.current?.focus();\n }}\n anchorRef={editorWrapperRef}\n />\n )}\n\n <ActionPanel\n className=\"hidden group-focus-within:flex\"\n onMention={() => insertAt()}\n onAddComment={handleAddComment}\n />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["user"]}
@@ -0,0 +1,2 @@
1
+ export declare function clamp(min: number, value: number, max: number): number;
2
+ //# sourceMappingURL=clamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clamp.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/clamp.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAE5D"}
@@ -0,0 +1,7 @@
1
+ function clamp(min, value, max) {
2
+ return Math.min(Math.max(value, min), max);
3
+ }
4
+ export {
5
+ clamp
6
+ };
7
+ //# sourceMappingURL=clamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/clamp.ts"],"sourcesContent":["export function clamp(min: number, value: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n"],"mappings":"AAAO,SAAS,MAAM,KAAa,OAAe,KAAa;AAC7D,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;","names":[]}
@@ -0,0 +1,8 @@
1
+ import type { RefObject } from "react";
2
+ interface Props {
3
+ isResolved: boolean;
4
+ contentRef: RefObject<HTMLElement | null>;
5
+ }
6
+ export declare function StrikethoroughOverlay({ isResolved, contentRef }: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,UAAU,KAAK;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC3C;AAED,wBAAgB,qBAAqB,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,KAAK,kDAuBtE"}
@@ -0,0 +1,24 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useStrikethroughAnimation } from "./useStrikethroughAnimation";
3
+ function StrikethoroughOverlay({ isResolved, contentRef }) {
4
+ const { lineRects, lineContentRect, getLineByIndexRef } = useStrikethroughAnimation(isResolved, contentRef);
5
+ if (!lineRects.length || !lineContentRect) return null;
6
+ const lineHeight = lineContentRect.height / lineRects.length;
7
+ return /* @__PURE__ */ jsx("span", { className: "block absolute inset-0 pointer-events-none", "aria-hidden": "true", children: lineRects.map((rect, i) => /* @__PURE__ */ jsx(
8
+ "span",
9
+ {
10
+ className: "absolute h-px bg-current",
11
+ ref: getLineByIndexRef(i),
12
+ style: {
13
+ width: 0,
14
+ top: Math.round(lineHeight * i + lineHeight / 2) - 1,
15
+ left: rect.left - lineContentRect.left
16
+ }
17
+ },
18
+ `line-${i}`
19
+ )) });
20
+ }
21
+ export {
22
+ StrikethoroughOverlay
23
+ };
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/index.tsx"],"sourcesContent":["import type { RefObject } from \"react\";\nimport { useStrikethroughAnimation } from \"./useStrikethroughAnimation\";\n\ninterface Props {\n isResolved: boolean;\n contentRef: RefObject<HTMLElement | null>;\n}\n\nexport function StrikethoroughOverlay({ isResolved, contentRef }: Props) {\n const { lineRects, lineContentRect, getLineByIndexRef } = useStrikethroughAnimation(isResolved, contentRef);\n\n if (!lineRects.length || !lineContentRect) return null;\n\n const lineHeight = lineContentRect.height / lineRects.length;\n\n return (\n <span className=\"block absolute inset-0 pointer-events-none\" aria-hidden=\"true\">\n {lineRects.map((rect, i) => (\n <span\n key={`line-${i}`}\n className=\"absolute h-px bg-current\"\n ref={getLineByIndexRef(i)}\n style={{\n width: 0,\n top: Math.round(lineHeight * i + lineHeight / 2) - 1,\n left: rect.left - lineContentRect.left,\n }}\n />\n ))}\n </span>\n );\n}\n"],"mappings":"AAkBQ;AAjBR,SAAS,iCAAiC;AAOnC,SAAS,sBAAsB,EAAE,YAAY,WAAW,GAAU;AACvE,QAAM,EAAE,WAAW,iBAAiB,kBAAkB,IAAI,0BAA0B,YAAY,UAAU;AAE1G,MAAI,CAAC,UAAU,UAAU,CAAC,gBAAiB,QAAO;AAElD,QAAM,aAAa,gBAAgB,SAAS,UAAU;AAEtD,SACE,oBAAC,UAAK,WAAU,8CAA6C,eAAY,QACtE,oBAAU,IAAI,CAAC,MAAM,MACpB;AAAA,IAAC;AAAA;AAAA,MAEC,WAAU;AAAA,MACV,KAAK,kBAAkB,CAAC;AAAA,MACxB,OAAO;AAAA,QACL,OAAO;AAAA,QACP,KAAK,KAAK,MAAM,aAAa,IAAI,aAAa,CAAC,IAAI;AAAA,QACnD,MAAM,KAAK,OAAO,gBAAgB;AAAA,MACpC;AAAA;AAAA,IAPK,QAAQ,CAAC;AAAA,EAQhB,CACD,GACH;AAEJ;","names":[]}
@@ -0,0 +1,3 @@
1
+ import type { RefObject } from "react";
2
+ export declare function measureLineRects(contentRef: RefObject<HTMLElement | null>): DOMRect[];
3
+ //# sourceMappingURL=measureLineRects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measureLineRects.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/measureLineRects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,aA8BzE"}
@@ -0,0 +1,29 @@
1
+ function measureLineRects(contentRef) {
2
+ const el = contentRef.current;
3
+ if (!el) return [];
4
+ const delEl = el.querySelector("del") ?? el;
5
+ const range = document.createRange();
6
+ range.selectNodeContents(delEl);
7
+ const rawRects = Array.from(range.getClientRects());
8
+ const groups = /* @__PURE__ */ new Map();
9
+ for (const rect of rawRects) {
10
+ const key = Math.round(rect.top);
11
+ const group = groups.get(key);
12
+ if (group) {
13
+ group.push(rect);
14
+ } else {
15
+ groups.set(key, [rect]);
16
+ }
17
+ }
18
+ return Array.from(groups.values()).map((rects) => {
19
+ const left = Math.min(...rects.map((r) => r.left));
20
+ const right = Math.max(...rects.map((r) => r.right));
21
+ const top = Math.min(...rects.map((r) => r.top));
22
+ const bottom = Math.max(...rects.map((r) => r.bottom));
23
+ return new DOMRect(left, top, right - left, bottom - top);
24
+ });
25
+ }
26
+ export {
27
+ measureLineRects
28
+ };
29
+ //# sourceMappingURL=measureLineRects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/measureLineRects.ts"],"sourcesContent":["import type { RefObject } from \"react\";\n\nexport function measureLineRects(contentRef: RefObject<HTMLElement | null>) {\n const el = contentRef.current;\n if (!el) return [];\n\n const delEl = el.querySelector(\"del\") ?? el;\n\n const range = document.createRange();\n range.selectNodeContents(delEl);\n const rawRects = Array.from(range.getClientRects());\n\n const groups = new Map<number, DOMRect[]>();\n for (const rect of rawRects) {\n const key = Math.round(rect.top);\n const group = groups.get(key);\n\n if (group) {\n group.push(rect);\n } else {\n groups.set(key, [rect]);\n }\n }\n\n return Array.from(groups.values()).map((rects) => {\n const left = Math.min(...rects.map((r) => r.left));\n const right = Math.max(...rects.map((r) => r.right));\n const top = Math.min(...rects.map((r) => r.top));\n const bottom = Math.max(...rects.map((r) => r.bottom));\n\n return new DOMRect(left, top, right - left, bottom - top);\n });\n}\n"],"mappings":"AAEO,SAAS,iBAAiB,YAA2C;AAC1E,QAAM,KAAK,WAAW;AACtB,MAAI,CAAC,GAAI,QAAO,CAAC;AAEjB,QAAM,QAAQ,GAAG,cAAc,KAAK,KAAK;AAEzC,QAAM,QAAQ,SAAS,YAAY;AACnC,QAAM,mBAAmB,KAAK;AAC9B,QAAM,WAAW,MAAM,KAAK,MAAM,eAAe,CAAC;AAElD,QAAM,SAAS,oBAAI,IAAuB;AAC1C,aAAW,QAAQ,UAAU;AAC3B,UAAM,MAAM,KAAK,MAAM,KAAK,GAAG;AAC/B,UAAM,QAAQ,OAAO,IAAI,GAAG;AAE5B,QAAI,OAAO;AACT,YAAM,KAAK,IAAI;AAAA,IACjB,OAAO;AACL,aAAO,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,IACxB;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,IAAI,CAAC,UAAU;AAChD,UAAM,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACjD,UAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AACnD,UAAM,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;AAC/C,UAAM,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AAErD,WAAO,IAAI,QAAQ,MAAM,KAAK,QAAQ,MAAM,SAAS,GAAG;AAAA,EAC1D,CAAC;AACH;","names":[]}
@@ -0,0 +1,7 @@
1
+ import { type RefObject } from "react";
2
+ export declare function useStrikethroughAnimation(isResolved: boolean, contentRect: RefObject<HTMLElement | null>): {
3
+ lineRects: DOMRect[];
4
+ lineContentRect: DOMRect | null;
5
+ getLineByIndexRef: (i: number) => (el: HTMLSpanElement | null) => void;
6
+ };
7
+ //# sourceMappingURL=useStrikethroughAnimation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStrikethroughAnimation.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAgD,MAAM,OAAO,CAAC;AAMrF,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;;;2BAezE,MAAM,UAAU,eAAe,GAAG,IAAI;EAqIrE"}
@@ -0,0 +1,114 @@
1
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
2
+ import { clamp } from "./clamp";
3
+ import { measureLineRects } from "./measureLineRects";
4
+ const SPEED = 20;
5
+ function useStrikethroughAnimation(isResolved, contentRect) {
6
+ const [lineRects, setLineRects] = useState([]);
7
+ const [lineContentRect, setLineContentRect] = useState(null);
8
+ const progressRef = useRef(0);
9
+ const directionRef = useRef(1);
10
+ const animationIdRef = useRef(0);
11
+ const measuredRef = useRef(false);
12
+ const mountedRef = useRef(false);
13
+ const lineRefs = useRef([]);
14
+ const lineRectsRef = useRef([]);
15
+ const cumulativePixelStartPerLineRef = useRef([]);
16
+ const getLineByIndexRef = (i) => (el) => {
17
+ lineRefs.current[i] = el;
18
+ };
19
+ const getTotalLineWidth = () => {
20
+ return lineRectsRef.current.reduce((sum, r) => sum + r.width, 0) || 1;
21
+ };
22
+ const measureLines = () => {
23
+ const el = contentRect.current;
24
+ if (!el) return;
25
+ const lineRects2 = measureLineRects(contentRect);
26
+ const lineContentRect2 = el.getBoundingClientRect();
27
+ const precomputeCumulativePixelStartPerLine = () => {
28
+ let acc = 0;
29
+ return lineRects2.map((rect) => {
30
+ const start = acc;
31
+ acc += rect.width;
32
+ return start;
33
+ });
34
+ };
35
+ lineRectsRef.current = lineRects2;
36
+ cumulativePixelStartPerLineRef.current = precomputeCumulativePixelStartPerLine();
37
+ setLineRects(lineRects2);
38
+ setLineContentRect(lineContentRect2);
39
+ };
40
+ const updateLines = (progress) => {
41
+ const rects = lineRectsRef.current;
42
+ const starts = cumulativePixelStartPerLineRef.current;
43
+ lineRefs.current.forEach((line, i) => {
44
+ if (!line || !rects[i]) return;
45
+ const pixelStart = starts[i] ?? 0;
46
+ const width = rects[i].width;
47
+ const currentWidth = clamp(0, (progress - pixelStart) / width, 1) * width;
48
+ line.style.width = `${currentWidth}px`;
49
+ });
50
+ };
51
+ const startLoop = (n) => {
52
+ cancelAnimationFrame(animationIdRef.current);
53
+ const tick = () => {
54
+ const direction = directionRef.current;
55
+ progressRef.current = clamp(0, progressRef.current + direction * SPEED, n);
56
+ updateLines(progressRef.current);
57
+ const done = direction === 1 && progressRef.current >= n || direction === -1 && progressRef.current <= 0;
58
+ if (!done) {
59
+ animationIdRef.current = requestAnimationFrame(tick);
60
+ }
61
+ };
62
+ animationIdRef.current = requestAnimationFrame(tick);
63
+ };
64
+ useLayoutEffect(() => {
65
+ updateLines(progressRef.current);
66
+ }, [lineRects]);
67
+ useEffect(() => {
68
+ const isFirstRun = !mountedRef.current;
69
+ mountedRef.current = true;
70
+ const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
71
+ if (isResolved && !measuredRef.current) {
72
+ measureLines();
73
+ measuredRef.current = true;
74
+ }
75
+ const totalWidth = getTotalLineWidth();
76
+ directionRef.current = isResolved ? 1 : -1;
77
+ cancelAnimationFrame(animationIdRef.current);
78
+ if (isFirstRun && isResolved) {
79
+ progressRef.current = totalWidth;
80
+ return;
81
+ }
82
+ if (isFirstRun) return;
83
+ if (prefersReducedMotion) {
84
+ const targetProgress = isResolved ? totalWidth : 0;
85
+ progressRef.current = targetProgress;
86
+ updateLines(targetProgress);
87
+ return;
88
+ }
89
+ startLoop(totalWidth);
90
+ }, [isResolved]);
91
+ useEffect(() => {
92
+ const el = contentRect.current;
93
+ if (!el) return;
94
+ const observer = new ResizeObserver(() => {
95
+ if (!measuredRef.current) return;
96
+ measureLines();
97
+ const totalWidth = getTotalLineWidth();
98
+ if (directionRef.current === 1 && progressRef.current >= totalWidth) {
99
+ progressRef.current = totalWidth;
100
+ updateLines(totalWidth);
101
+ }
102
+ });
103
+ observer.observe(el);
104
+ return () => observer.disconnect();
105
+ }, [contentRect]);
106
+ useEffect(() => {
107
+ return () => cancelAnimationFrame(animationIdRef.current);
108
+ }, []);
109
+ return { lineRects, lineContentRect, getLineByIndexRef };
110
+ }
111
+ export {
112
+ useStrikethroughAnimation
113
+ };
114
+ //# sourceMappingURL=useStrikethroughAnimation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/CommentItem/StrikethoroughOverlay/useStrikethroughAnimation.ts"],"sourcesContent":["import { type RefObject, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { clamp } from \"./clamp\";\nimport { measureLineRects } from \"./measureLineRects\";\n\nconst SPEED = 20;\n\nexport function useStrikethroughAnimation(isResolved: boolean, contentRect: RefObject<HTMLElement | null>) {\n const [lineRects, setLineRects] = useState<DOMRect[]>([]);\n const [lineContentRect, setLineContentRect] = useState<DOMRect | null>(null);\n\n const progressRef = useRef<number>(0);\n const directionRef = useRef<1 | -1>(1);\n const animationIdRef = useRef<number>(0);\n\n const measuredRef = useRef<boolean>(false);\n const mountedRef = useRef<boolean>(false);\n\n const lineRefs = useRef<(HTMLSpanElement | null)[]>([]);\n const lineRectsRef = useRef<DOMRect[]>([]);\n const cumulativePixelStartPerLineRef = useRef<number[]>([]);\n\n const getLineByIndexRef = (i: number) => (el: HTMLSpanElement | null) => {\n lineRefs.current[i] = el;\n };\n\n const getTotalLineWidth = () => {\n return lineRectsRef.current.reduce((sum, r) => sum + r.width, 0) || 1;\n };\n\n const measureLines = () => {\n const el = contentRect.current;\n if (!el) return;\n\n const lineRects = measureLineRects(contentRect);\n const lineContentRect = el.getBoundingClientRect();\n\n const precomputeCumulativePixelStartPerLine = () => {\n let acc = 0;\n\n return lineRects.map((rect) => {\n const start = acc;\n acc += rect.width;\n return start;\n });\n };\n\n lineRectsRef.current = lineRects;\n cumulativePixelStartPerLineRef.current = precomputeCumulativePixelStartPerLine();\n\n setLineRects(lineRects);\n setLineContentRect(lineContentRect);\n };\n\n const updateLines = (progress: number) => {\n const rects = lineRectsRef.current;\n const starts = cumulativePixelStartPerLineRef.current;\n\n lineRefs.current.forEach((line, i) => {\n if (!line || !rects[i]) return;\n\n const pixelStart = starts[i] ?? 0;\n const width = rects[i].width;\n\n const currentWidth = clamp(0, (progress - pixelStart) / width, 1) * width;\n line.style.width = `${currentWidth}px`;\n });\n };\n\n const startLoop = (n: number) => {\n cancelAnimationFrame(animationIdRef.current);\n\n const tick = () => {\n const direction = directionRef.current;\n progressRef.current = clamp(0, progressRef.current + direction * SPEED, n);\n updateLines(progressRef.current);\n\n const done = (direction === 1 && progressRef.current >= n) || (direction === -1 && progressRef.current <= 0);\n\n if (!done) {\n animationIdRef.current = requestAnimationFrame(tick);\n }\n };\n\n animationIdRef.current = requestAnimationFrame(tick);\n };\n\n useLayoutEffect(() => {\n updateLines(progressRef.current);\n }, [lineRects]);\n\n useEffect(() => {\n const isFirstRun = !mountedRef.current;\n mountedRef.current = true;\n\n const prefersReducedMotion =\n typeof window !== \"undefined\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n if (isResolved && !measuredRef.current) {\n measureLines();\n measuredRef.current = true;\n }\n\n const totalWidth = getTotalLineWidth();\n directionRef.current = isResolved ? 1 : -1;\n\n cancelAnimationFrame(animationIdRef.current);\n\n if (isFirstRun && isResolved) {\n progressRef.current = totalWidth;\n\n return;\n }\n\n if (isFirstRun) return;\n\n if (prefersReducedMotion) {\n const targetProgress = isResolved ? totalWidth : 0;\n\n progressRef.current = targetProgress;\n updateLines(targetProgress);\n\n return;\n }\n\n startLoop(totalWidth);\n }, [isResolved]);\n\n useEffect(() => {\n const el = contentRect.current;\n if (!el) return;\n\n const observer = new ResizeObserver(() => {\n if (!measuredRef.current) return;\n\n measureLines();\n\n const totalWidth = getTotalLineWidth();\n\n if (directionRef.current === 1 && progressRef.current >= totalWidth) {\n progressRef.current = totalWidth;\n updateLines(totalWidth);\n }\n });\n\n observer.observe(el);\n\n return () => observer.disconnect();\n }, [contentRect]);\n\n useEffect(() => {\n return () => cancelAnimationFrame(animationIdRef.current);\n }, []);\n\n return { lineRects, lineContentRect, getLineByIndexRef };\n}\n"],"mappings":"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","names":["lineRects","lineContentRect"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CommentItem/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAS3C,UAAU,KAAK;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,KAAK,2CA2D5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CommentItem/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAU3C,UAAU,KAAK;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,KAAK,2CA+E5D"}
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { startTransition } from "react";
3
+ import { startTransition, useRef } from "react";
4
4
  import { useTranslation } from "@payloadcms/ui";
5
+ import { CircleCheck } from "lucide-react";
5
6
  import { cn } from "../../utils/general/cn";
6
7
  import { useComments } from "../../providers/CommentsProvider";
7
8
  import { renderCommentText } from "../../utils/comment/renderCommentText";
@@ -10,10 +11,12 @@ import { FALLBACK_DELETED_USERNAME, FALLBACK_USERNAME } from "../../constants";
10
11
  import { ToolsPanel } from "./ToolsPanel";
11
12
  import { useRelativeDate } from "../../hooks/useRelativeDate";
12
13
  import { Avatar } from "../Avatar";
14
+ import { StrikethoroughOverlay } from "./StrikethoroughOverlay";
13
15
  function CommentItem({ comment, currentUserId }) {
14
16
  const { resolveComment, removeComment, usernameFieldPath } = useComments();
15
17
  const { t } = useTranslation();
16
18
  const createdAtRelativeDate = useRelativeDate(comment.createdAt);
19
+ const contentRef = useRef(null);
17
20
  const deletedUserLabel = t("comments:deletedUser") ?? FALLBACK_DELETED_USERNAME;
18
21
  const unknownLabel = t("comments:unknownAuthor") ?? FALLBACK_USERNAME;
19
22
  const narrowedAuthor = typeof comment.author === "object" ? comment.author : null;
@@ -21,6 +24,13 @@ function CommentItem({ comment, currentUserId }) {
21
24
  const isResolved = comment.isResolved ?? false;
22
25
  const authorId = comment.author && typeof comment.author === "object" && "id" in comment.author ? comment.author.id : null;
23
26
  const canDelete = currentUserId !== null && authorId === currentUserId;
27
+ const renderedText = renderCommentText({
28
+ text: comment.text,
29
+ mentions: comment.mentions,
30
+ currentUserId,
31
+ usernameFieldPath,
32
+ fallbackDeletedUsername: deletedUserLabel
33
+ });
24
34
  const handleToggleResolve = () => {
25
35
  startTransition(async () => {
26
36
  await resolveComment(comment.id, !isResolved);
@@ -32,6 +42,31 @@ function CommentItem({ comment, currentUserId }) {
32
42
  });
33
43
  };
34
44
  return /* @__PURE__ */ jsxs("div", { className: cn("group relative"), children: [
45
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
46
+ /* @__PURE__ */ jsx(Avatar, { user: narrowedAuthor, usernameFieldPath, fallbackName: unknownLabel }),
47
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
48
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
49
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-[13px] text-(--theme-text) truncate", children: authorName }),
50
+ isResolved && /* @__PURE__ */ jsx(CircleCheck, { size: 14, className: "text-green-500 shrink-0", "aria-label": t("comments:resolved") }),
51
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-(--theme-elevation-450) shrink-0", children: createdAtRelativeDate })
52
+ ] }),
53
+ /* @__PURE__ */ jsxs(
54
+ "p",
55
+ {
56
+ ref: contentRef,
57
+ className: cn(
58
+ "relative m-0 text-[13px] text-(--theme-text) leading-normal whitespace-pre-wrap wrap-break-word transition-opacity motion-reduce:transition-none",
59
+ isResolved && "opacity-60"
60
+ ),
61
+ children: [
62
+ isResolved && /* @__PURE__ */ jsx("del", { style: { textDecoration: "none" }, dateTime: comment.resolvedAt ?? void 0, children: renderedText }),
63
+ !isResolved && renderedText,
64
+ /* @__PURE__ */ jsx(StrikethoroughOverlay, { isResolved, contentRef })
65
+ ]
66
+ }
67
+ )
68
+ ] })
69
+ ] }),
35
70
  /* @__PURE__ */ jsx(
36
71
  ToolsPanel,
37
72
  {
@@ -40,23 +75,7 @@ function CommentItem({ comment, currentUserId }) {
40
75
  onDelete: handleDelete,
41
76
  onResolve: handleToggleResolve
42
77
  }
43
- ),
44
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2.5 items-start", children: [
45
- /* @__PURE__ */ jsx(Avatar, { user: narrowedAuthor, usernameFieldPath, fallbackName: unknownLabel }),
46
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
47
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
48
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-[13px] text-(--theme-text)", children: authorName }),
49
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-(--theme-elevation-450)", children: createdAtRelativeDate })
50
- ] }),
51
- /* @__PURE__ */ jsx("p", { className: "m-0 text-[13px] leading-normal text-(--theme-text) whitespace-pre-wrap wrap-break-word", children: renderCommentText({
52
- text: comment.text,
53
- mentions: comment.mentions,
54
- currentUserId,
55
- usernameFieldPath,
56
- fallbackDeletedUsername: deletedUserLabel
57
- }) })
58
- ] })
59
- ] })
78
+ )
60
79
  ] });
61
80
  }
62
81
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/CommentItem/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { startTransition } from \"react\";\nimport { useTranslation } from \"@payloadcms/ui\";\nimport { cn } from \"../../utils/general/cn\";\nimport type { Comment } from \"../../types\";\nimport { useComments } from \"../../providers/CommentsProvider\";\nimport { renderCommentText } from \"../../utils/comment/renderCommentText\";\nimport { resolveUsername } from \"../../utils/user/resolveUsername\";\nimport { FALLBACK_DELETED_USERNAME, FALLBACK_USERNAME } from \"../../constants\";\nimport { ToolsPanel } from \"./ToolsPanel\";\nimport { useRelativeDate } from \"../../hooks/useRelativeDate\";\nimport { Avatar } from \"../Avatar\";\n\ninterface Props {\n comment: Comment;\n currentUserId: number | null;\n}\n\nexport function CommentItem({ comment, currentUserId }: Props) {\n const { resolveComment, removeComment, usernameFieldPath } = useComments();\n const { t } = useTranslation();\n const createdAtRelativeDate = useRelativeDate(comment.createdAt);\n\n const deletedUserLabel = t(\"comments:deletedUser\" as never) ?? FALLBACK_DELETED_USERNAME;\n const unknownLabel = t(\"comments:unknownAuthor\" as never) ?? FALLBACK_USERNAME;\n const narrowedAuthor = typeof comment.author === \"object\" ? comment.author : null;\n const authorName = resolveUsername(narrowedAuthor, usernameFieldPath, unknownLabel);\n\n const isResolved = comment.isResolved ?? false;\n\n const authorId =\n comment.author && typeof comment.author === \"object\" && \"id\" in comment.author ? comment.author.id : null;\n const canDelete = currentUserId !== null && authorId === currentUserId;\n\n const handleToggleResolve = () => {\n startTransition(async () => {\n await resolveComment(comment.id, !isResolved);\n });\n };\n\n const handleDelete = () => {\n startTransition(async () => {\n await removeComment(comment.id);\n });\n };\n\n return (\n <div className={cn(\"group relative\")}>\n <ToolsPanel\n isResolved={isResolved}\n canDelete={canDelete}\n onDelete={handleDelete}\n onResolve={handleToggleResolve}\n />\n\n <div className=\"flex gap-2.5 items-start\">\n <Avatar user={narrowedAuthor} usernameFieldPath={usernameFieldPath} fallbackName={unknownLabel} />\n\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-semibold text-[13px] text-(--theme-text)\">{authorName}</span>\n <span className=\"text-[11px] text-(--theme-elevation-450)\">{createdAtRelativeDate}</span>\n </div>\n\n <p className=\"m-0 text-[13px] leading-normal text-(--theme-text) whitespace-pre-wrap wrap-break-word\">\n {renderCommentText({\n text: comment.text,\n mentions: comment.mentions,\n currentUserId,\n usernameFieldPath,\n fallbackDeletedUsername: deletedUserLabel,\n })}\n </p>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";AAiDM,cAWI,YAXJ;AA/CN,SAAS,uBAAuB;AAChC,SAAS,sBAAsB;AAC/B,SAAS,UAAU;AAEnB,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,2BAA2B,yBAAyB;AAC7D,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,cAAc;AAOhB,SAAS,YAAY,EAAE,SAAS,cAAc,GAAU;AAC7D,QAAM,EAAE,gBAAgB,eAAe,kBAAkB,IAAI,YAAY;AACzE,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,wBAAwB,gBAAgB,QAAQ,SAAS;AAE/D,QAAM,mBAAmB,EAAE,sBAA+B,KAAK;AAC/D,QAAM,eAAe,EAAE,wBAAiC,KAAK;AAC7D,QAAM,iBAAiB,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AAC7E,QAAM,aAAa,gBAAgB,gBAAgB,mBAAmB,YAAY;AAElF,QAAM,aAAa,QAAQ,cAAc;AAEzC,QAAM,WACJ,QAAQ,UAAU,OAAO,QAAQ,WAAW,YAAY,QAAQ,QAAQ,SAAS,QAAQ,OAAO,KAAK;AACvG,QAAM,YAAY,kBAAkB,QAAQ,aAAa;AAEzD,QAAM,sBAAsB,MAAM;AAChC,oBAAgB,YAAY;AAC1B,YAAM,eAAe,QAAQ,IAAI,CAAC,UAAU;AAAA,IAC9C,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM;AACzB,oBAAgB,YAAY;AAC1B,YAAM,cAAc,QAAQ,EAAE;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,gBAAgB,GACjC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,WAAW;AAAA;AAAA,IACb;AAAA,IAEA,qBAAC,SAAI,WAAU,4BACb;AAAA,0BAAC,UAAO,MAAM,gBAAgB,mBAAsC,cAAc,cAAc;AAAA,MAEhG,qBAAC,SAAI,WAAU,UACb;AAAA,6BAAC,SAAI,WAAU,gCACb;AAAA,8BAAC,UAAK,WAAU,iDAAiD,sBAAW;AAAA,UAC5E,oBAAC,UAAK,WAAU,4CAA4C,iCAAsB;AAAA,WACpF;AAAA,QAEA,oBAAC,OAAE,WAAU,0FACV,4BAAkB;AAAA,UACjB,MAAM,QAAQ;AAAA,UACd,UAAU,QAAQ;AAAA,UAClB;AAAA,UACA;AAAA,UACA,yBAAyB;AAAA,QAC3B,CAAC,GACH;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/CommentItem/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { startTransition, useRef } from \"react\";\nimport { useTranslation } from \"@payloadcms/ui\";\nimport { CircleCheck } from \"lucide-react\";\nimport { cn } from \"../../utils/general/cn\";\nimport type { Comment } from \"../../types\";\nimport { useComments } from \"../../providers/CommentsProvider\";\nimport { renderCommentText } from \"../../utils/comment/renderCommentText\";\nimport { resolveUsername } from \"../../utils/user/resolveUsername\";\nimport { FALLBACK_DELETED_USERNAME, FALLBACK_USERNAME } from \"../../constants\";\nimport { ToolsPanel } from \"./ToolsPanel\";\nimport { useRelativeDate } from \"../../hooks/useRelativeDate\";\nimport { Avatar } from \"../Avatar\";\nimport { StrikethoroughOverlay } from \"./StrikethoroughOverlay\";\n\ninterface Props {\n comment: Comment;\n currentUserId: number | null;\n}\n\nexport function CommentItem({ comment, currentUserId }: Props) {\n const { resolveComment, removeComment, usernameFieldPath } = useComments();\n const { t } = useTranslation();\n const createdAtRelativeDate = useRelativeDate(comment.createdAt);\n\n const contentRef = useRef<HTMLParagraphElement>(null);\n\n const deletedUserLabel = t(\"comments:deletedUser\" as never) ?? FALLBACK_DELETED_USERNAME;\n const unknownLabel = t(\"comments:unknownAuthor\" as never) ?? FALLBACK_USERNAME;\n const narrowedAuthor = typeof comment.author === \"object\" ? comment.author : null;\n const authorName = resolveUsername(narrowedAuthor, usernameFieldPath, unknownLabel);\n\n const isResolved = comment.isResolved ?? false;\n const authorId =\n comment.author && typeof comment.author === \"object\" && \"id\" in comment.author ? comment.author.id : null;\n const canDelete = currentUserId !== null && authorId === currentUserId;\n\n const renderedText = renderCommentText({\n text: comment.text,\n mentions: comment.mentions,\n currentUserId,\n usernameFieldPath,\n fallbackDeletedUsername: deletedUserLabel,\n });\n\n const handleToggleResolve = () => {\n startTransition(async () => {\n await resolveComment(comment.id, !isResolved);\n });\n };\n\n const handleDelete = () => {\n startTransition(async () => {\n await removeComment(comment.id);\n });\n };\n\n return (\n <div className={cn(\"group relative\")}>\n <div className=\"flex gap-2.5 items-start\">\n <Avatar user={narrowedAuthor} usernameFieldPath={usernameFieldPath} fallbackName={unknownLabel} />\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-semibold text-[13px] text-(--theme-text) truncate\">{authorName}</span>\n\n {isResolved && (\n <CircleCheck size={14} className=\"text-green-500 shrink-0\" aria-label={t(\"comments:resolved\" as never)} />\n )}\n\n <span className=\"text-[11px] text-(--theme-elevation-450) shrink-0\">{createdAtRelativeDate}</span>\n </div>\n\n <p\n ref={contentRef}\n className={cn(\n \"relative m-0 text-[13px] text-(--theme-text) leading-normal whitespace-pre-wrap wrap-break-word transition-opacity motion-reduce:transition-none\",\n isResolved && \"opacity-60\",\n )}>\n {isResolved && (\n <del style={{ textDecoration: \"none\" }} dateTime={comment.resolvedAt ?? undefined}>\n {renderedText}\n </del>\n )}\n {!isResolved && renderedText}\n\n <StrikethoroughOverlay isResolved={isResolved} contentRef={contentRef} />\n </p>\n </div>\n </div>\n\n <ToolsPanel\n isResolved={isResolved}\n canDelete={canDelete}\n onDelete={handleDelete}\n onResolve={handleToggleResolve}\n />\n </div>\n );\n}\n"],"mappings":";AA6DQ,cAGE,YAHF;AA3DR,SAAS,iBAAiB,cAAc;AACxC,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAEnB,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,2BAA2B,yBAAyB;AAC7D,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,SAAS,6BAA6B;AAO/B,SAAS,YAAY,EAAE,SAAS,cAAc,GAAU;AAC7D,QAAM,EAAE,gBAAgB,eAAe,kBAAkB,IAAI,YAAY;AACzE,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,wBAAwB,gBAAgB,QAAQ,SAAS;AAE/D,QAAM,aAAa,OAA6B,IAAI;AAEpD,QAAM,mBAAmB,EAAE,sBAA+B,KAAK;AAC/D,QAAM,eAAe,EAAE,wBAAiC,KAAK;AAC7D,QAAM,iBAAiB,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS;AAC7E,QAAM,aAAa,gBAAgB,gBAAgB,mBAAmB,YAAY;AAElF,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,WACJ,QAAQ,UAAU,OAAO,QAAQ,WAAW,YAAY,QAAQ,QAAQ,SAAS,QAAQ,OAAO,KAAK;AACvG,QAAM,YAAY,kBAAkB,QAAQ,aAAa;AAEzD,QAAM,eAAe,kBAAkB;AAAA,IACrC,MAAM,QAAQ;AAAA,IACd,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA,yBAAyB;AAAA,EAC3B,CAAC;AAED,QAAM,sBAAsB,MAAM;AAChC,oBAAgB,YAAY;AAC1B,YAAM,eAAe,QAAQ,IAAI,CAAC,UAAU;AAAA,IAC9C,CAAC;AAAA,EACH;AAEA,QAAM,eAAe,MAAM;AACzB,oBAAgB,YAAY;AAC1B,YAAM,cAAc,QAAQ,EAAE;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,gBAAgB,GACjC;AAAA,yBAAC,SAAI,WAAU,4BACb;AAAA,0BAAC,UAAO,MAAM,gBAAgB,mBAAsC,cAAc,cAAc;AAAA,MAEhG,qBAAC,SAAI,WAAU,kBACb;AAAA,6BAAC,SAAI,WAAU,gCACb;AAAA,8BAAC,UAAK,WAAU,0DAA0D,sBAAW;AAAA,UAEpF,cACC,oBAAC,eAAY,MAAM,IAAI,WAAU,2BAA0B,cAAY,EAAE,mBAA4B,GAAG;AAAA,UAG1G,oBAAC,UAAK,WAAU,qDAAqD,iCAAsB;AAAA,WAC7F;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAW;AAAA,cACT;AAAA,cACA,cAAc;AAAA,YAChB;AAAA,YACC;AAAA,4BACC,oBAAC,SAAI,OAAO,EAAE,gBAAgB,OAAO,GAAG,UAAU,QAAQ,cAAc,QACrE,wBACH;AAAA,cAED,CAAC,cAAc;AAAA,cAEhB,oBAAC,yBAAsB,YAAwB,YAAwB;AAAA;AAAA;AAAA,QACzE;AAAA,SACF;AAAA,OACF;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,WAAW;AAAA;AAAA,IACb;AAAA,KACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentsDrawer/components/Header.tsx"],"names":[],"mappings":"AASA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW,2CAwC3C"}
1
+ {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../src/components/CommentsDrawer/components/Header.tsx"],"names":[],"mappings":"AAMA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,MAAM,CAAC,EAAE,IAAI,EAAE,EAAE,WAAW,2CAe3C"}
@@ -1,43 +1,14 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { SelectInput, useModal, useTranslation } from "@payloadcms/ui";
4
- import { useComments } from "../../../providers/CommentsProvider";
5
- import { LoaderCircleIcon, XIcon } from "lucide-react";
3
+ import { useModal, useTranslation } from "@payloadcms/ui";
4
+ import { XIcon } from "lucide-react";
6
5
  import { IconButton } from "../../IconButton";
7
6
  function Header({ slug }) {
8
7
  const { t } = useTranslation();
9
8
  const { closeModal } = useModal();
10
- const { filter, setFilter, syncCommentsStatus } = useComments();
11
- const options = [
12
- { label: t("comments:filterOpen"), value: "open" },
13
- { label: t("comments:filterResolved"), value: "resolved" },
14
- { label: t("comments:filterMentioned"), value: "mentioned" }
15
- ];
16
- return /* @__PURE__ */ jsxs("header", { className: "sticky top-0 flex items-center gap-3 py-5 bg-(--theme-bg)", children: [
9
+ return /* @__PURE__ */ jsxs("header", { className: "sticky top-0 z-10 flex items-center gap-3 py-5 bg-(--theme-bg)", children: [
17
10
  /* @__PURE__ */ jsx("h2", { className: "m-0 text-2xl font-bold", children: t("comments:label") }),
18
- syncCommentsStatus === "loading" && /* @__PURE__ */ jsx(
19
- LoaderCircleIcon,
20
- {
21
- width: 14,
22
- height: 14,
23
- className: "text-(--theme-elevation-450) animate-spin shrink-0",
24
- "aria-label": t("comments:syncingComments")
25
- }
26
- ),
27
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
28
- /* @__PURE__ */ jsx(
29
- SelectInput,
30
- {
31
- name: "filter",
32
- path: "filter",
33
- options,
34
- value: filter,
35
- onChange: (option) => setFilter(option.value),
36
- isClearable: false
37
- }
38
- ),
39
- /* @__PURE__ */ jsx(IconButton, { onClick: () => closeModal(slug), title: t("comments:close"), children: /* @__PURE__ */ jsx(XIcon, { width: 16, height: 16 }) })
40
- ] })
11
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 ml-auto", children: /* @__PURE__ */ jsx(IconButton, { onClick: () => closeModal(slug), title: t("comments:close"), children: /* @__PURE__ */ jsx(XIcon, { width: 16, height: 16 }) }) })
41
12
  ] });
42
13
  }
43
14
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/CommentsDrawer/components/Header.tsx"],"sourcesContent":["\"use client\";\n\nimport { SelectInput, useModal, useTranslation } from \"@payloadcms/ui\";\nimport { useComments } from \"../../../providers/CommentsProvider\";\nimport type { FilterMode } from \"../../../types\";\nimport { LoaderCircleIcon, XIcon } from \"lucide-react\";\nimport type { OptionObject } from \"payload\";\nimport { IconButton } from \"../../IconButton\";\n\ninterface HeaderProps {\n slug: string;\n}\n\nexport function Header({ slug }: HeaderProps) {\n const { t } = useTranslation();\n const { closeModal } = useModal();\n const { filter, setFilter, syncCommentsStatus } = useComments();\n\n const options = [\n { label: t(\"comments:filterOpen\" as never), value: \"open\" },\n { label: t(\"comments:filterResolved\" as never), value: \"resolved\" },\n { label: t(\"comments:filterMentioned\" as never), value: \"mentioned\" },\n ];\n\n return (\n <header className=\"sticky top-0 flex items-center gap-3 py-5 bg-(--theme-bg)\">\n <h2 className=\"m-0 text-2xl font-bold\">{t(\"comments:label\" as never)}</h2>\n\n {syncCommentsStatus === \"loading\" && (\n <LoaderCircleIcon\n width={14}\n height={14}\n className=\"text-(--theme-elevation-450) animate-spin shrink-0\"\n aria-label={t(\"comments:syncingComments\" as never)}\n />\n )}\n\n <div className=\"flex items-center gap-2 ml-auto\">\n <SelectInput\n name=\"filter\"\n path=\"filter\"\n options={options}\n value={filter}\n onChange={(option) => setFilter((option as OptionObject).value as FilterMode)}\n isClearable={false}\n />\n\n <IconButton onClick={() => closeModal(slug)} title={t(\"comments:close\" as never)}>\n <XIcon width={16} height={16} />\n </IconButton>\n </div>\n </header>\n );\n}\n"],"mappings":";AA0BM,cAWA,YAXA;AAxBN,SAAS,aAAa,UAAU,sBAAsB;AACtD,SAAS,mBAAmB;AAE5B,SAAS,kBAAkB,aAAa;AAExC,SAAS,kBAAkB;AAMpB,SAAS,OAAO,EAAE,KAAK,GAAgB;AAC5C,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,EAAE,WAAW,IAAI,SAAS;AAChC,QAAM,EAAE,QAAQ,WAAW,mBAAmB,IAAI,YAAY;AAE9D,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,EAAE,qBAA8B,GAAG,OAAO,OAAO;AAAA,IAC1D,EAAE,OAAO,EAAE,yBAAkC,GAAG,OAAO,WAAW;AAAA,IAClE,EAAE,OAAO,EAAE,0BAAmC,GAAG,OAAO,YAAY;AAAA,EACtE;AAEA,SACE,qBAAC,YAAO,WAAU,6DAChB;AAAA,wBAAC,QAAG,WAAU,0BAA0B,YAAE,gBAAyB,GAAE;AAAA,IAEpE,uBAAuB,aACtB;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAU;AAAA,QACV,cAAY,EAAE,0BAAmC;AAAA;AAAA,IACnD;AAAA,IAGF,qBAAC,SAAI,WAAU,mCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,MAAK;AAAA,UACL;AAAA,UACA,OAAO;AAAA,UACP,UAAU,CAAC,WAAW,UAAW,OAAwB,KAAmB;AAAA,UAC5E,aAAa;AAAA;AAAA,MACf;AAAA,MAEA,oBAAC,cAAW,SAAS,MAAM,WAAW,IAAI,GAAG,OAAO,EAAE,gBAAyB,GAC7E,8BAAC,SAAM,OAAO,IAAI,QAAQ,IAAI,GAChC;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/CommentsDrawer/components/Header.tsx"],"sourcesContent":["\"use client\";\n\nimport { useModal, useTranslation } from \"@payloadcms/ui\";\nimport { XIcon } from \"lucide-react\";\nimport { IconButton } from \"../../IconButton\";\n\ninterface HeaderProps {\n slug: string;\n}\n\nexport function Header({ slug }: HeaderProps) {\n const { t } = useTranslation();\n const { closeModal } = useModal();\n\n return (\n <header className=\"sticky top-0 z-10 flex items-center gap-3 py-5 bg-(--theme-bg)\">\n <h2 className=\"m-0 text-2xl font-bold\">{t(\"comments:label\" as never)}</h2>\n\n <div className=\"flex items-center gap-2 ml-auto\">\n <IconButton onClick={() => closeModal(slug)} title={t(\"comments:close\" as never)}>\n <XIcon width={16} height={16} />\n </IconButton>\n </div>\n </header>\n );\n}\n"],"mappings":";AAeI,SACE,KADF;AAbJ,SAAS,UAAU,sBAAsB;AACzC,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAMpB,SAAS,OAAO,EAAE,KAAK,GAAgB;AAC5C,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,EAAE,WAAW,IAAI,SAAS;AAEhC,SACE,qBAAC,YAAO,WAAU,kEAChB;AAAA,wBAAC,QAAG,WAAU,0BAA0B,YAAE,gBAAyB,GAAE;AAAA,IAErE,oBAAC,SAAI,WAAU,mCACb,8BAAC,cAAW,SAAS,MAAM,WAAW,IAAI,GAAG,OAAO,EAAE,gBAAyB,GAC7E,8BAAC,SAAM,OAAO,IAAI,QAAQ,IAAI,GAChC,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -1,15 +1,15 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useTranslation } from "@payloadcms/ui";
4
- import { MessageSquare } from "lucide-react";
5
4
  import { CommentsDrawer } from "../CommentsDrawer";
6
5
  import { useCommentsDrawer } from "../../providers/CommentsDrawerProvider";
7
6
  import { IconButton } from "../IconButton";
7
+ import { Bell } from "lucide-react";
8
8
  function CommentsHeaderButton() {
9
9
  const { slug, open } = useCommentsDrawer();
10
10
  const { t } = useTranslation();
11
11
  return /* @__PURE__ */ jsxs(Fragment, { children: [
12
- /* @__PURE__ */ jsx(IconButton, { variant: "neutralSecondary", onClick: () => open(), title: t("comments:openCommentsAria"), children: /* @__PURE__ */ jsx(MessageSquare, { size: 16 }) }),
12
+ /* @__PURE__ */ jsx(IconButton, { variant: "neutralSecondary", onClick: () => open(), title: t("comments:openCommentsAria"), children: /* @__PURE__ */ jsx(Bell, { size: 16 }) }),
13
13
  /* @__PURE__ */ jsx(CommentsDrawer, { slug })
14
14
  ] });
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/CommentsHeaderButton/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useTranslation } from \"@payloadcms/ui\";\nimport { MessageSquare } from \"lucide-react\";\nimport { CommentsDrawer } from \"../CommentsDrawer\";\nimport { useCommentsDrawer } from \"../../providers/CommentsDrawerProvider\";\nimport { IconButton } from \"../IconButton\";\n\nexport function CommentsHeaderButton() {\n const { slug, open } = useCommentsDrawer();\n const { t } = useTranslation();\n\n return (\n <>\n <IconButton variant=\"neutralSecondary\" onClick={() => open()} title={t(\"comments:openCommentsAria\" as never)}>\n <MessageSquare size={16} />\n </IconButton>\n\n <CommentsDrawer slug={slug} />\n </>\n );\n}\n"],"mappings":";AAaI,mBAEI,KAFJ;AAXJ,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAEpB,SAAS,uBAAuB;AACrC,QAAM,EAAE,MAAM,KAAK,IAAI,kBAAkB;AACzC,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,SACE,iCACE;AAAA,wBAAC,cAAW,SAAQ,oBAAmB,SAAS,MAAM,KAAK,GAAG,OAAO,EAAE,2BAAoC,GACzG,8BAAC,iBAAc,MAAM,IAAI,GAC3B;AAAA,IAEA,oBAAC,kBAAe,MAAY;AAAA,KAC9B;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/CommentsHeaderButton/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { useTranslation } from \"@payloadcms/ui\";\nimport { CommentsDrawer } from \"../CommentsDrawer\";\nimport { useCommentsDrawer } from \"../../providers/CommentsDrawerProvider\";\nimport { IconButton } from \"../IconButton\";\nimport { Bell } from \"lucide-react\";\n\nexport function CommentsHeaderButton() {\n const { slug, open } = useCommentsDrawer();\n const { t } = useTranslation();\n\n return (\n <>\n <IconButton variant=\"neutralSecondary\" onClick={() => open()} title={t(\"comments:openCommentsAria\" as never)}>\n <Bell size={16} />\n </IconButton>\n\n <CommentsDrawer slug={slug} />\n </>\n );\n}\n"],"mappings":";AAaI,mBAEI,KAFJ;AAXJ,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAEd,SAAS,uBAAuB;AACrC,QAAM,EAAE,MAAM,KAAK,IAAI,kBAAkB;AACzC,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,SACE,iCACE;AAAA,wBAAC,cAAW,SAAQ,oBAAmB,SAAS,MAAM,KAAK,GAAG,OAAO,EAAE,2BAAoC,GACzG,8BAAC,QAAK,MAAM,IAAI,GAClB;AAAA,IAEA,oBAAC,kBAAe,MAAY;AAAA,KAC9B;AAEJ;","names":[]}
@@ -1,10 +1,15 @@
1
- import type { ReactNode } from "react";
1
+ import { type ReactNode, type RefObject } from "react";
2
+ export interface CollapsibleGroupHandle {
3
+ open: () => void;
4
+ toggle: () => void;
5
+ }
2
6
  interface CollapsibleGroupProps {
3
7
  groupKey: string;
4
8
  label: string;
5
9
  children: ReactNode;
6
10
  level: "collection" | "document" | "field";
11
+ ref?: RefObject<CollapsibleGroupHandle | null>;
7
12
  }
8
- export declare function CollapsibleGroup({ groupKey, label, children, level }: CollapsibleGroupProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function CollapsibleGroup({ groupKey, label, children, level, ref }: CollapsibleGroupProps): import("react/jsx-runtime").JSX.Element;
9
14
  export {};
10
15
  //# sourceMappingURL=CollapsibleGroup.d.ts.map