@wordpress/editor 14.10.0 → 14.11.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 (292) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -2
  3. package/build/bindings/post-meta.js +2 -1
  4. package/build/bindings/post-meta.js.map +1 -1
  5. package/build/components/collab-sidebar/add-comment.js +120 -0
  6. package/build/components/collab-sidebar/add-comment.js.map +1 -0
  7. package/build/components/collab-sidebar/comment-button-toolbar.js +37 -0
  8. package/build/components/collab-sidebar/comment-button-toolbar.js.map +1 -0
  9. package/build/components/collab-sidebar/comment-button.js +37 -0
  10. package/build/components/collab-sidebar/comment-button.js.map +1 -0
  11. package/build/components/collab-sidebar/comments.js +321 -0
  12. package/build/components/collab-sidebar/comments.js.map +1 -0
  13. package/build/components/collab-sidebar/constants.js +8 -0
  14. package/build/components/collab-sidebar/constants.js.map +1 -0
  15. package/build/components/collab-sidebar/index.js +272 -0
  16. package/build/components/collab-sidebar/index.js.map +1 -0
  17. package/build/components/collab-sidebar/utils.js +16 -0
  18. package/build/components/collab-sidebar/utils.js.map +1 -0
  19. package/build/components/commands/index.js +6 -6
  20. package/build/components/commands/index.js.map +1 -1
  21. package/build/components/document-bar/index.js +8 -7
  22. package/build/components/document-bar/index.js.map +1 -1
  23. package/build/components/document-tools/index.js +1 -5
  24. package/build/components/document-tools/index.js.map +1 -1
  25. package/build/components/editor-interface/index.js +3 -2
  26. package/build/components/editor-interface/index.js.map +1 -1
  27. package/build/components/entities-saved-states/index.js +1 -1
  28. package/build/components/entities-saved-states/index.js.map +1 -1
  29. package/build/components/global-keyboard-shortcuts/register-shortcuts.js +3 -3
  30. package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  31. package/build/components/global-styles-provider/index.js +13 -3
  32. package/build/components/global-styles-provider/index.js.map +1 -1
  33. package/build/components/header/index.js +5 -2
  34. package/build/components/header/index.js.map +1 -1
  35. package/build/components/inserter-sidebar/index.js +2 -2
  36. package/build/components/inserter-sidebar/index.js.map +1 -1
  37. package/build/components/page-attributes/parent.js +5 -5
  38. package/build/components/page-attributes/parent.js.map +1 -1
  39. package/build/components/plugin-more-menu-item/index.js +13 -11
  40. package/build/components/plugin-more-menu-item/index.js.map +1 -1
  41. package/build/components/plugin-preview-menu-item/index.js +15 -13
  42. package/build/components/plugin-preview-menu-item/index.js.map +1 -1
  43. package/build/components/plugin-sidebar/index.js +0 -15
  44. package/build/components/plugin-sidebar/index.js.map +1 -1
  45. package/build/components/post-actions/index.js +5 -5
  46. package/build/components/post-actions/index.js.map +1 -1
  47. package/build/components/post-author/panel.js +4 -4
  48. package/build/components/post-author/panel.js.map +1 -1
  49. package/build/components/post-card-panel/index.js +7 -36
  50. package/build/components/post-card-panel/index.js.map +1 -1
  51. package/build/components/post-content-information/index.js +1 -2
  52. package/build/components/post-content-information/index.js.map +1 -1
  53. package/build/components/post-featured-image/index.js +26 -7
  54. package/build/components/post-featured-image/index.js.map +1 -1
  55. package/build/components/post-last-revision/index.js +1 -1
  56. package/build/components/post-last-revision/index.js.map +1 -1
  57. package/build/components/post-schedule/label.js +1 -1
  58. package/build/components/post-schedule/label.js.map +1 -1
  59. package/build/components/post-taxonomies/flat-term-selector.js +9 -1
  60. package/build/components/post-taxonomies/flat-term-selector.js.map +1 -1
  61. package/build/components/post-taxonomies/hierarchical-term-selector.js +2 -2
  62. package/build/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
  63. package/build/components/post-url/index.js +1 -1
  64. package/build/components/post-url/index.js.map +1 -1
  65. package/build/components/post-url/panel.js +3 -3
  66. package/build/components/post-url/panel.js.map +1 -1
  67. package/build/components/preview-dropdown/index.js +0 -4
  68. package/build/components/preview-dropdown/index.js.map +1 -1
  69. package/build/components/provider/index.js +10 -12
  70. package/build/components/provider/index.js.map +1 -1
  71. package/build/components/provider/use-block-editor-settings.js +1 -1
  72. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  73. package/build/components/sidebar/header.js +2 -1
  74. package/build/components/sidebar/header.js.map +1 -1
  75. package/build/components/sidebar/index.js +3 -3
  76. package/build/components/sidebar/index.js.map +1 -1
  77. package/build/components/time-to-read/index.js +2 -2
  78. package/build/components/time-to-read/index.js.map +1 -1
  79. package/build/components/visual-editor/index.js +25 -27
  80. package/build/components/visual-editor/index.js.map +1 -1
  81. package/build/components/visual-editor/use-zoom-out-mode-exit.js +52 -0
  82. package/build/components/visual-editor/use-zoom-out-mode-exit.js.map +1 -0
  83. package/build/components/zoom-out-toggle/index.js +37 -5
  84. package/build/components/zoom-out-toggle/index.js.map +1 -1
  85. package/build/dataviews/actions/duplicate-template-part.js +2 -2
  86. package/build/dataviews/actions/duplicate-template-part.js.map +1 -1
  87. package/build/store/actions.js +4 -3
  88. package/build/store/actions.js.map +1 -1
  89. package/build/store/private-actions.js +2 -2
  90. package/build/store/private-actions.js.map +1 -1
  91. package/build/store/selectors.js +3 -2
  92. package/build/store/selectors.js.map +1 -1
  93. package/build/utils/pageTypeBadge.js +51 -0
  94. package/build/utils/pageTypeBadge.js.map +1 -0
  95. package/build/utils/terms.js +2 -2
  96. package/build/utils/terms.js.map +1 -1
  97. package/build-module/bindings/post-meta.js +2 -1
  98. package/build-module/bindings/post-meta.js.map +1 -1
  99. package/build-module/components/collab-sidebar/add-comment.js +114 -0
  100. package/build-module/components/collab-sidebar/add-comment.js.map +1 -0
  101. package/build-module/components/collab-sidebar/comment-button-toolbar.js +30 -0
  102. package/build-module/components/collab-sidebar/comment-button-toolbar.js.map +1 -0
  103. package/build-module/components/collab-sidebar/comment-button.js +30 -0
  104. package/build-module/components/collab-sidebar/comment-button.js.map +1 -0
  105. package/build-module/components/collab-sidebar/comments.js +314 -0
  106. package/build-module/components/collab-sidebar/comments.js.map +1 -0
  107. package/build-module/components/collab-sidebar/constants.js +2 -0
  108. package/build-module/components/collab-sidebar/constants.js.map +1 -0
  109. package/build-module/components/collab-sidebar/index.js +264 -0
  110. package/build-module/components/collab-sidebar/index.js.map +1 -0
  111. package/build-module/components/collab-sidebar/utils.js +10 -0
  112. package/build-module/components/collab-sidebar/utils.js.map +1 -0
  113. package/build-module/components/commands/index.js +6 -6
  114. package/build-module/components/commands/index.js.map +1 -1
  115. package/build-module/components/document-bar/index.js +9 -8
  116. package/build-module/components/document-bar/index.js.map +1 -1
  117. package/build-module/components/document-tools/index.js +2 -6
  118. package/build-module/components/document-tools/index.js.map +1 -1
  119. package/build-module/components/editor-interface/index.js +3 -2
  120. package/build-module/components/editor-interface/index.js.map +1 -1
  121. package/build-module/components/entities-saved-states/index.js +1 -1
  122. package/build-module/components/entities-saved-states/index.js.map +1 -1
  123. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +3 -3
  124. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  125. package/build-module/components/global-styles-provider/index.js +13 -3
  126. package/build-module/components/global-styles-provider/index.js.map +1 -1
  127. package/build-module/components/header/index.js +5 -2
  128. package/build-module/components/header/index.js.map +1 -1
  129. package/build-module/components/inserter-sidebar/index.js +2 -2
  130. package/build-module/components/inserter-sidebar/index.js.map +1 -1
  131. package/build-module/components/page-attributes/parent.js +5 -5
  132. package/build-module/components/page-attributes/parent.js.map +1 -1
  133. package/build-module/components/plugin-more-menu-item/index.js +12 -10
  134. package/build-module/components/plugin-more-menu-item/index.js.map +1 -1
  135. package/build-module/components/plugin-preview-menu-item/index.js +14 -12
  136. package/build-module/components/plugin-preview-menu-item/index.js.map +1 -1
  137. package/build-module/components/plugin-sidebar/index.js +0 -15
  138. package/build-module/components/plugin-sidebar/index.js.map +1 -1
  139. package/build-module/components/post-actions/index.js +5 -5
  140. package/build-module/components/post-actions/index.js.map +1 -1
  141. package/build-module/components/post-author/panel.js +4 -4
  142. package/build-module/components/post-author/panel.js.map +1 -1
  143. package/build-module/components/post-card-panel/index.js +8 -36
  144. package/build-module/components/post-card-panel/index.js.map +1 -1
  145. package/build-module/components/post-content-information/index.js +1 -2
  146. package/build-module/components/post-content-information/index.js.map +1 -1
  147. package/build-module/components/post-featured-image/index.js +26 -7
  148. package/build-module/components/post-featured-image/index.js.map +1 -1
  149. package/build-module/components/post-last-revision/index.js +1 -1
  150. package/build-module/components/post-last-revision/index.js.map +1 -1
  151. package/build-module/components/post-schedule/label.js +1 -1
  152. package/build-module/components/post-schedule/label.js.map +1 -1
  153. package/build-module/components/post-taxonomies/flat-term-selector.js +12 -4
  154. package/build-module/components/post-taxonomies/flat-term-selector.js.map +1 -1
  155. package/build-module/components/post-taxonomies/hierarchical-term-selector.js +2 -2
  156. package/build-module/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
  157. package/build-module/components/post-url/index.js +1 -1
  158. package/build-module/components/post-url/index.js.map +1 -1
  159. package/build-module/components/post-url/panel.js +3 -3
  160. package/build-module/components/post-url/panel.js.map +1 -1
  161. package/build-module/components/preview-dropdown/index.js +0 -4
  162. package/build-module/components/preview-dropdown/index.js.map +1 -1
  163. package/build-module/components/provider/index.js +10 -12
  164. package/build-module/components/provider/index.js.map +1 -1
  165. package/build-module/components/provider/use-block-editor-settings.js +1 -1
  166. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  167. package/build-module/components/sidebar/header.js +2 -1
  168. package/build-module/components/sidebar/header.js.map +1 -1
  169. package/build-module/components/sidebar/index.js +4 -4
  170. package/build-module/components/sidebar/index.js.map +1 -1
  171. package/build-module/components/time-to-read/index.js +2 -2
  172. package/build-module/components/time-to-read/index.js.map +1 -1
  173. package/build-module/components/visual-editor/index.js +26 -28
  174. package/build-module/components/visual-editor/index.js.map +1 -1
  175. package/build-module/components/visual-editor/use-zoom-out-mode-exit.js +46 -0
  176. package/build-module/components/visual-editor/use-zoom-out-mode-exit.js.map +1 -0
  177. package/build-module/components/zoom-out-toggle/index.js +37 -5
  178. package/build-module/components/zoom-out-toggle/index.js.map +1 -1
  179. package/build-module/dataviews/actions/duplicate-template-part.js +3 -3
  180. package/build-module/dataviews/actions/duplicate-template-part.js.map +1 -1
  181. package/build-module/store/actions.js +4 -3
  182. package/build-module/store/actions.js.map +1 -1
  183. package/build-module/store/private-actions.js +3 -3
  184. package/build-module/store/private-actions.js.map +1 -1
  185. package/build-module/store/selectors.js +3 -2
  186. package/build-module/store/selectors.js.map +1 -1
  187. package/build-module/utils/pageTypeBadge.js +45 -0
  188. package/build-module/utils/pageTypeBadge.js.map +1 -0
  189. package/build-module/utils/terms.js +2 -2
  190. package/build-module/utils/terms.js.map +1 -1
  191. package/build-style/style-rtl.css +96 -54
  192. package/build-style/style.css +96 -54
  193. package/build-types/bindings/post-meta.d.ts.map +1 -1
  194. package/build-types/components/collab-sidebar/add-comment.d.ts +15 -0
  195. package/build-types/components/collab-sidebar/add-comment.d.ts.map +1 -0
  196. package/build-types/components/collab-sidebar/comment-button-toolbar.d.ts +5 -0
  197. package/build-types/components/collab-sidebar/comment-button-toolbar.d.ts.map +1 -0
  198. package/build-types/components/collab-sidebar/comment-button.d.ts +5 -0
  199. package/build-types/components/collab-sidebar/comment-button.d.ts.map +1 -0
  200. package/build-types/components/collab-sidebar/comments.d.ts +19 -0
  201. package/build-types/components/collab-sidebar/comments.d.ts.map +1 -0
  202. package/build-types/components/collab-sidebar/constants.d.ts +2 -0
  203. package/build-types/components/collab-sidebar/constants.d.ts.map +1 -0
  204. package/build-types/components/collab-sidebar/index.d.ts +5 -0
  205. package/build-types/components/collab-sidebar/index.d.ts.map +1 -0
  206. package/build-types/components/collab-sidebar/utils.d.ts +8 -0
  207. package/build-types/components/collab-sidebar/utils.d.ts.map +1 -0
  208. package/build-types/components/commands/index.d.ts.map +1 -1
  209. package/build-types/components/document-bar/index.d.ts.map +1 -1
  210. package/build-types/components/document-tools/index.d.ts.map +1 -1
  211. package/build-types/components/editor-interface/index.d.ts.map +1 -1
  212. package/build-types/components/global-styles-provider/index.d.ts.map +1 -1
  213. package/build-types/components/header/index.d.ts.map +1 -1
  214. package/build-types/components/inserter-sidebar/index.d.ts.map +1 -1
  215. package/build-types/components/page-attributes/parent.d.ts.map +1 -1
  216. package/build-types/components/plugin-more-menu-item/index.d.ts +62 -2
  217. package/build-types/components/plugin-more-menu-item/index.d.ts.map +1 -1
  218. package/build-types/components/plugin-preview-menu-item/index.d.ts +41 -2
  219. package/build-types/components/plugin-preview-menu-item/index.d.ts.map +1 -1
  220. package/build-types/components/plugin-sidebar/index.d.ts.map +1 -1
  221. package/build-types/components/post-author/panel.d.ts.map +1 -1
  222. package/build-types/components/post-card-panel/index.d.ts.map +1 -1
  223. package/build-types/components/post-taxonomies/flat-term-selector.d.ts.map +1 -1
  224. package/build-types/components/preview-dropdown/index.d.ts.map +1 -1
  225. package/build-types/components/provider/index.d.ts.map +1 -1
  226. package/build-types/components/sidebar/index.d.ts.map +1 -1
  227. package/build-types/components/visual-editor/index.d.ts.map +1 -1
  228. package/build-types/components/visual-editor/use-zoom-out-mode-exit.d.ts +5 -0
  229. package/build-types/components/visual-editor/use-zoom-out-mode-exit.d.ts.map +1 -0
  230. package/build-types/components/zoom-out-toggle/index.d.ts +3 -1
  231. package/build-types/components/zoom-out-toggle/index.d.ts.map +1 -1
  232. package/build-types/store/actions.d.ts.map +1 -1
  233. package/build-types/store/selectors.d.ts.map +1 -1
  234. package/build-types/utils/pageTypeBadge.d.ts +5 -0
  235. package/build-types/utils/pageTypeBadge.d.ts.map +1 -0
  236. package/build-types/utils/terms.d.ts.map +1 -1
  237. package/package.json +38 -37
  238. package/src/bindings/post-meta.js +1 -0
  239. package/src/components/collab-sidebar/add-comment.js +124 -0
  240. package/src/components/collab-sidebar/comment-button-toolbar.js +29 -0
  241. package/src/components/collab-sidebar/comment-button.js +31 -0
  242. package/src/components/collab-sidebar/comments.js +404 -0
  243. package/src/components/collab-sidebar/constants.js +1 -0
  244. package/src/components/collab-sidebar/index.js +307 -0
  245. package/src/components/collab-sidebar/style.scss +111 -0
  246. package/src/components/collab-sidebar/utils.js +9 -0
  247. package/src/components/commands/index.js +9 -7
  248. package/src/components/document-bar/index.js +15 -7
  249. package/src/components/document-bar/style.scss +0 -4
  250. package/src/components/document-tools/index.js +1 -7
  251. package/src/components/editor-interface/index.js +4 -3
  252. package/src/components/entities-saved-states/index.js +2 -2
  253. package/src/components/global-keyboard-shortcuts/register-shortcuts.js +3 -3
  254. package/src/components/global-styles-provider/index.js +20 -6
  255. package/src/components/header/index.js +3 -1
  256. package/src/components/inserter-sidebar/index.js +3 -6
  257. package/src/components/page-attributes/parent.js +6 -4
  258. package/src/components/plugin-more-menu-item/index.js +12 -11
  259. package/src/components/plugin-preview-menu-item/index.js +14 -13
  260. package/src/components/plugin-sidebar/index.js +0 -13
  261. package/src/components/post-actions/index.js +8 -11
  262. package/src/components/post-author/panel.js +4 -2
  263. package/src/components/post-card-panel/index.js +9 -43
  264. package/src/components/post-card-panel/style.scss +1 -9
  265. package/src/components/post-content-information/index.js +1 -1
  266. package/src/components/post-featured-image/index.js +31 -16
  267. package/src/components/post-featured-image/style.scss +5 -2
  268. package/src/components/post-last-revision/index.js +1 -1
  269. package/src/components/post-schedule/label.js +1 -1
  270. package/src/components/post-sticky/test/index.js +6 -6
  271. package/src/components/post-taxonomies/flat-term-selector.js +15 -4
  272. package/src/components/post-taxonomies/hierarchical-term-selector.js +2 -2
  273. package/src/components/post-url/index.js +1 -1
  274. package/src/components/post-url/panel.js +4 -2
  275. package/src/components/preview-dropdown/index.js +0 -2
  276. package/src/components/provider/index.js +31 -24
  277. package/src/components/provider/use-block-editor-settings.js +1 -1
  278. package/src/components/sidebar/header.js +3 -2
  279. package/src/components/sidebar/index.js +5 -3
  280. package/src/components/time-to-read/index.js +3 -3
  281. package/src/components/visual-editor/index.js +32 -41
  282. package/src/components/visual-editor/use-zoom-out-mode-exit.js +49 -0
  283. package/src/components/zoom-out-toggle/index.js +39 -5
  284. package/src/dataviews/actions/duplicate-template-part.tsx +3 -3
  285. package/src/store/actions.js +5 -3
  286. package/src/store/private-actions.js +3 -3
  287. package/src/store/selectors.js +3 -2
  288. package/src/style.scss +1 -0
  289. package/src/utils/pageTypeBadge.js +41 -0
  290. package/src/utils/terms.js +4 -2
  291. package/src/utils/test/terms.js +3 -3
  292. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,404 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useState, RawHTML } from '@wordpress/element';
10
+ import {
11
+ __experimentalHStack as HStack,
12
+ __experimentalVStack as VStack,
13
+ __experimentalConfirmDialog as ConfirmDialog,
14
+ Button,
15
+ DropdownMenu,
16
+ TextareaControl,
17
+ Tooltip,
18
+ } from '@wordpress/components';
19
+ import {
20
+ dateI18n,
21
+ format,
22
+ getSettings as getDateSettings,
23
+ } from '@wordpress/date';
24
+ import { Icon, check, published, moreVertical } from '@wordpress/icons';
25
+ import { __, _x } from '@wordpress/i18n';
26
+ import { useSelect } from '@wordpress/data';
27
+ import { useEntityProp } from '@wordpress/core-data';
28
+ import { store as blockEditorStore } from '@wordpress/block-editor';
29
+
30
+ /**
31
+ * Internal dependencies
32
+ */
33
+ import { sanitizeCommentString } from './utils';
34
+
35
+ /**
36
+ * Renders the Comments component.
37
+ *
38
+ * @param {Object} props - The component props.
39
+ * @param {Array} props.threads - The array of comment threads.
40
+ * @param {Function} props.onEditComment - The function to handle comment editing.
41
+ * @param {Function} props.onAddReply - The function to add a reply to a comment.
42
+ * @param {Function} props.onCommentDelete - The function to delete a comment.
43
+ * @param {Function} props.onCommentResolve - The function to mark a comment as resolved.
44
+ * @return {JSX.Element} The rendered Comments component.
45
+ */
46
+ export function Comments( {
47
+ threads,
48
+ onEditComment,
49
+ onAddReply,
50
+ onCommentDelete,
51
+ onCommentResolve,
52
+ } ) {
53
+ const [ actionState, setActionState ] = useState( false );
54
+ const [ isConfirmDialogOpen, setIsConfirmDialogOpen ] = useState( false );
55
+
56
+ const handleConfirmDelete = () => {
57
+ onCommentDelete( actionState.id );
58
+ setActionState( false );
59
+ setIsConfirmDialogOpen( false );
60
+ };
61
+
62
+ const handleConfirmResolve = () => {
63
+ onCommentResolve( actionState.id );
64
+ setActionState( false );
65
+ setIsConfirmDialogOpen( false );
66
+ };
67
+
68
+ const handleCancelDelete = () => {
69
+ setActionState( false );
70
+ setIsConfirmDialogOpen( false );
71
+ };
72
+
73
+ const blockCommentId = useSelect( ( select ) => {
74
+ const clientID = select( blockEditorStore ).getSelectedBlockClientId();
75
+ return (
76
+ select( blockEditorStore ).getBlock( clientID )?.attributes
77
+ ?.blockCommentId ?? false
78
+ );
79
+ }, [] );
80
+
81
+ const CommentBoard = ( { thread, parentThread } ) => {
82
+ return (
83
+ <>
84
+ <CommentHeader
85
+ thread={ thread }
86
+ onResolve={ () => {
87
+ setActionState( {
88
+ action: 'resolve',
89
+ id: parentThread?.id ?? thread.id,
90
+ } );
91
+ setIsConfirmDialogOpen( true );
92
+ } }
93
+ onEdit={ () =>
94
+ setActionState( { action: 'edit', id: thread.id } )
95
+ }
96
+ onDelete={ () => {
97
+ setActionState( { action: 'delete', id: thread.id } );
98
+ setIsConfirmDialogOpen( true );
99
+ } }
100
+ onReply={
101
+ ! parentThread
102
+ ? () =>
103
+ setActionState( {
104
+ action: 'reply',
105
+ id: thread.id,
106
+ } )
107
+ : undefined
108
+ }
109
+ status={ parentThread?.status ?? thread.status }
110
+ />
111
+ <HStack
112
+ alignment="left"
113
+ spacing="3"
114
+ justify="flex-start"
115
+ className="editor-collab-sidebar-panel__user-comment"
116
+ >
117
+ <VStack
118
+ spacing="3"
119
+ className="editor-collab-sidebar-panel__comment-field"
120
+ >
121
+ { 'edit' === actionState?.action &&
122
+ thread.id === actionState?.id && (
123
+ <CommentForm
124
+ onSubmit={ ( value ) => {
125
+ onEditComment( thread.id, value );
126
+ setActionState( false );
127
+ } }
128
+ onCancel={ () => setActionState( false ) }
129
+ thread={ thread }
130
+ />
131
+ ) }
132
+ { ( ! actionState ||
133
+ 'edit' !== actionState?.action ) && (
134
+ <RawHTML>{ thread?.content?.raw }</RawHTML>
135
+ ) }
136
+ </VStack>
137
+ </HStack>
138
+ { 'resolve' === actionState?.action &&
139
+ thread.id === actionState?.id && (
140
+ <ConfirmDialog
141
+ isOpen={ isConfirmDialogOpen }
142
+ onConfirm={ handleConfirmResolve }
143
+ onCancel={ handleCancelDelete }
144
+ confirmButtonText="Yes"
145
+ cancelButtonText="No"
146
+ >
147
+ {
148
+ // translators: message displayed when confirming an action
149
+ __(
150
+ 'Are you sure you want to mark this comment as resolved?'
151
+ )
152
+ }
153
+ </ConfirmDialog>
154
+ ) }
155
+ { 'delete' === actionState?.action &&
156
+ thread.id === actionState?.id && (
157
+ <ConfirmDialog
158
+ isOpen={ isConfirmDialogOpen }
159
+ onConfirm={ handleConfirmDelete }
160
+ onCancel={ handleCancelDelete }
161
+ confirmButtonText="Yes"
162
+ cancelButtonText="No"
163
+ >
164
+ {
165
+ // translators: message displayed when confirming an action
166
+ __(
167
+ 'Are you sure you want to delete this comment?'
168
+ )
169
+ }
170
+ </ConfirmDialog>
171
+ ) }
172
+ </>
173
+ );
174
+ };
175
+
176
+ return (
177
+ <>
178
+ {
179
+ // If there are no comments, show a message indicating no comments are available.
180
+ ( ! Array.isArray( threads ) || threads.length === 0 ) && (
181
+ <VStack
182
+ alignment="left"
183
+ className="editor-collab-sidebar-panel__thread"
184
+ justify="flex-start"
185
+ spacing="3"
186
+ >
187
+ {
188
+ // translators: message displayed when there are no comments available
189
+ __( 'No comments available' )
190
+ }
191
+ </VStack>
192
+ )
193
+ }
194
+
195
+ { Array.isArray( threads ) &&
196
+ threads.length > 0 &&
197
+ threads.map( ( thread ) => (
198
+ <VStack
199
+ key={ thread.id }
200
+ className={ clsx(
201
+ 'editor-collab-sidebar-panel__thread',
202
+ {
203
+ 'editor-collab-sidebar-panel__active-thread':
204
+ blockCommentId &&
205
+ blockCommentId === thread.id,
206
+ }
207
+ ) }
208
+ id={ thread.id }
209
+ spacing="3"
210
+ >
211
+ <CommentBoard thread={ thread } />
212
+ { 'reply' === actionState?.action &&
213
+ thread.id === actionState?.id && (
214
+ <HStack
215
+ alignment="left"
216
+ spacing="3"
217
+ justify="flex-start"
218
+ className="editor-collab-sidebar-panel__user-comment"
219
+ >
220
+ <VStack
221
+ spacing="3"
222
+ className="editor-collab-sidebar-panel__comment-field"
223
+ >
224
+ <CommentForm
225
+ onSubmit={ ( inputComment ) => {
226
+ onAddReply(
227
+ inputComment,
228
+ thread.id
229
+ );
230
+ setActionState( false );
231
+ } }
232
+ onCancel={ () =>
233
+ setActionState( false )
234
+ }
235
+ />
236
+ </VStack>
237
+ </HStack>
238
+ ) }
239
+ { 0 < thread?.reply?.length &&
240
+ thread.reply.map( ( reply ) => (
241
+ <VStack
242
+ key={ reply.id }
243
+ className="editor-collab-sidebar-panel__child-thread"
244
+ id={ reply.id }
245
+ spacing="2"
246
+ >
247
+ <CommentBoard
248
+ thread={ reply }
249
+ parentThread={ thread }
250
+ />
251
+ </VStack>
252
+ ) ) }
253
+ </VStack>
254
+ ) ) }
255
+ </>
256
+ );
257
+ }
258
+
259
+ /**
260
+ * EditComment component.
261
+ *
262
+ * @param {Object} props - The component props.
263
+ * @param {Function} props.onSubmit - The function to call when updating the comment.
264
+ * @param {Function} props.onCancel - The function to call when canceling the comment update.
265
+ * @param {Object} props.thread - The comment thread object.
266
+ * @return {JSX.Element} The CommentForm component.
267
+ */
268
+ function CommentForm( { onSubmit, onCancel, thread } ) {
269
+ const [ inputComment, setInputComment ] = useState(
270
+ thread?.content?.raw ?? ''
271
+ );
272
+
273
+ return (
274
+ <>
275
+ <TextareaControl
276
+ __nextHasNoMarginBottom
277
+ value={ inputComment ?? '' }
278
+ onChange={ setInputComment }
279
+ />
280
+ <VStack alignment="left" spacing="3" justify="flex-start">
281
+ <HStack alignment="left" spacing="3" justify="flex-start">
282
+ <Button
283
+ __next40pxDefaultSize
284
+ accessibleWhenDisabled
285
+ variant="primary"
286
+ onClick={ () => onSubmit( inputComment ) }
287
+ disabled={
288
+ 0 === sanitizeCommentString( inputComment ).length
289
+ }
290
+ >
291
+ { thread
292
+ ? _x( 'Update', 'verb' )
293
+ : _x( 'Reply', 'Add reply comment' ) }
294
+ </Button>
295
+ <Button __next40pxDefaultSize onClick={ onCancel }>
296
+ { _x( 'Cancel', 'Cancel comment edit' ) }
297
+ </Button>
298
+ </HStack>
299
+ </VStack>
300
+ </>
301
+ );
302
+ }
303
+
304
+ /**
305
+ * Renders the header of a comment in the collaboration sidebar.
306
+ *
307
+ * @param {Object} props - The component props.
308
+ * @param {Object} props.thread - The comment thread object.
309
+ * @param {Function} props.onResolve - The function to resolve the comment.
310
+ * @param {Function} props.onEdit - The function to edit the comment.
311
+ * @param {Function} props.onDelete - The function to delete the comment.
312
+ * @param {Function} props.onReply - The function to reply to the comment.
313
+ * @param {string} props.status - The status of the comment.
314
+ * @return {JSX.Element} The rendered comment header.
315
+ */
316
+ function CommentHeader( {
317
+ thread,
318
+ onResolve,
319
+ onEdit,
320
+ onDelete,
321
+ onReply,
322
+ status,
323
+ } ) {
324
+ const dateSettings = getDateSettings();
325
+ const [ dateTimeFormat = dateSettings.formats.time ] = useEntityProp(
326
+ 'root',
327
+ 'site',
328
+ 'time_format'
329
+ );
330
+
331
+ const actions = [
332
+ {
333
+ title: _x( 'Edit', 'Edit comment' ),
334
+ onClick: onEdit,
335
+ },
336
+ {
337
+ title: _x( 'Delete', 'Delete comment' ),
338
+ onClick: onDelete,
339
+ },
340
+ {
341
+ title: _x( 'Reply', 'Reply on a comment' ),
342
+ onClick: onReply,
343
+ },
344
+ ];
345
+
346
+ const moreActions = actions.filter( ( item ) => item.onClick );
347
+
348
+ return (
349
+ <HStack alignment="left" spacing="3" justify="flex-start">
350
+ <img
351
+ src={ thread?.author_avatar_urls?.[ 48 ] }
352
+ className="editor-collab-sidebar-panel__user-avatar"
353
+ // translators: alt text for user avatar image
354
+ alt={ __( 'User avatar' ) }
355
+ width={ 32 }
356
+ height={ 32 }
357
+ />
358
+ <VStack spacing="0">
359
+ <span className="editor-collab-sidebar-panel__user-name">
360
+ { thread.author_name }
361
+ </span>
362
+ <time
363
+ dateTime={ format( 'h:i A', thread.date ) }
364
+ className="editor-collab-sidebar-panel__user-time"
365
+ >
366
+ { dateI18n( dateTimeFormat, thread.date ) }
367
+ </time>
368
+ </VStack>
369
+ <span className="editor-collab-sidebar-panel__comment-status">
370
+ { status !== 'approved' && (
371
+ <HStack alignment="right" justify="flex-end" spacing="0">
372
+ { 0 === thread.parent && onResolve && (
373
+ <Button
374
+ label={ _x(
375
+ 'Resolve',
376
+ 'Mark comment as resolved'
377
+ ) }
378
+ __next40pxDefaultSize
379
+ icon={ published }
380
+ onClick={ onResolve }
381
+ showTooltip
382
+ />
383
+ ) }
384
+ <DropdownMenu
385
+ icon={ moreVertical }
386
+ label={ _x(
387
+ 'Select an action',
388
+ 'Select comment action'
389
+ ) }
390
+ className="editor-collab-sidebar-panel__comment-dropdown-menu"
391
+ controls={ moreActions }
392
+ />
393
+ </HStack>
394
+ ) }
395
+ { status === 'approved' && (
396
+ // translators: tooltip for resolved comment
397
+ <Tooltip text={ __( 'Resolved' ) }>
398
+ <Icon icon={ check } />
399
+ </Tooltip>
400
+ ) }
401
+ </span>
402
+ </HStack>
403
+ );
404
+ }
@@ -0,0 +1 @@
1
+ export const collabSidebarName = 'edit-post/collab-sidebar';
@@ -0,0 +1,307 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { useSelect, useDispatch, resolveSelect } from '@wordpress/data';
6
+ import { useState, useEffect, useMemo } from '@wordpress/element';
7
+ import { comment as commentIcon } from '@wordpress/icons';
8
+ import { addFilter } from '@wordpress/hooks';
9
+ import { store as noticesStore } from '@wordpress/notices';
10
+ import { store as coreStore } from '@wordpress/core-data';
11
+ import { store as blockEditorStore } from '@wordpress/block-editor';
12
+ import { store as interfaceStore } from '@wordpress/interface';
13
+
14
+ /**
15
+ * Internal dependencies
16
+ */
17
+ import PluginSidebar from '../plugin-sidebar';
18
+ import { collabSidebarName } from './constants';
19
+ import { Comments } from './comments';
20
+ import { AddComment } from './add-comment';
21
+ import { store as editorStore } from '../../store';
22
+ import AddCommentButton from './comment-button';
23
+ import AddCommentToolbarButton from './comment-button-toolbar';
24
+
25
+ const threadsEmptyArray = [];
26
+
27
+ const isBlockCommentExperimentEnabled =
28
+ window?.__experimentalEnableBlockComment;
29
+ const modifyBlockCommentAttributes = ( settings ) => {
30
+ if ( ! settings.attributes.blockCommentId ) {
31
+ settings.attributes = {
32
+ ...settings.attributes,
33
+ blockCommentId: {
34
+ type: 'number',
35
+ },
36
+ };
37
+ }
38
+
39
+ return settings;
40
+ };
41
+
42
+ // Apply the filter to all core blocks
43
+ addFilter(
44
+ 'blocks.registerBlockType',
45
+ 'block-comment/modify-core-block-attributes',
46
+ modifyBlockCommentAttributes
47
+ );
48
+
49
+ /**
50
+ * Renders the Collab sidebar.
51
+ */
52
+ export default function CollabSidebar() {
53
+ const { createNotice } = useDispatch( noticesStore );
54
+ const { saveEntityRecord, deleteEntityRecord } = useDispatch( coreStore );
55
+ const { getEntityRecord } = resolveSelect( coreStore );
56
+ const { enableComplementaryArea } = useDispatch( interfaceStore );
57
+ const [ blockCommentID, setBlockCommentID ] = useState( null );
58
+ const [ showCommentBoard, setShowCommentBoard ] = useState( false );
59
+ const { postId } = useSelect( ( select ) => {
60
+ return {
61
+ postId: select( editorStore ).getCurrentPostId(),
62
+ };
63
+ }, [] );
64
+
65
+ const postStatus = useSelect( ( select ) => {
66
+ const post = select( editorStore ).getCurrentPost();
67
+ return { postStatus: post?.status };
68
+ }, [] );
69
+
70
+ const threads = useSelect(
71
+ ( select ) => {
72
+ if ( ! postId ) {
73
+ return threadsEmptyArray;
74
+ }
75
+ const { getEntityRecords } = select( coreStore );
76
+ const data = getEntityRecords( 'root', 'comment', {
77
+ post: postId,
78
+ type: 'block_comment',
79
+ status: 'any',
80
+ per_page: 100,
81
+ } );
82
+ return data || threadsEmptyArray;
83
+ },
84
+ [ postId ]
85
+ );
86
+
87
+ const clientId = useSelect( ( select ) => {
88
+ const { getSelectedBlockClientId } = select( blockEditorStore );
89
+ return getSelectedBlockClientId();
90
+ }, [] );
91
+
92
+ const blockDetails = useSelect(
93
+ ( select ) => {
94
+ return clientId
95
+ ? select( blockEditorStore ).getBlock( clientId )
96
+ : null;
97
+ },
98
+ [ clientId ]
99
+ );
100
+
101
+ // Get the dispatch functions to save the comment and update the block attributes.
102
+ const { updateBlockAttributes } = useDispatch( blockEditorStore );
103
+
104
+ // Process comments to build the tree structure
105
+ const resultComments = useMemo( () => {
106
+ // Create a compare to store the references to all objects by id
107
+ const compare = {};
108
+ const result = [];
109
+
110
+ const filteredComments = threads.filter(
111
+ ( comment ) => comment.status !== 'trash'
112
+ );
113
+
114
+ // Initialize each object with an empty `reply` array
115
+ filteredComments.forEach( ( item ) => {
116
+ compare[ item.id ] = { ...item, reply: [] };
117
+ } );
118
+
119
+ // Iterate over the data to build the tree structure
120
+ filteredComments.forEach( ( item ) => {
121
+ if ( item.parent === 0 ) {
122
+ // If parent is 0, it's a root item, push it to the result array
123
+ result.push( compare[ item.id ] );
124
+ } else if ( compare[ item.parent ] ) {
125
+ // Otherwise, find its parent and push it to the parent's `reply` array
126
+ compare[ item.parent ].reply.push( compare[ item.id ] );
127
+ }
128
+ } );
129
+
130
+ return result;
131
+ }, [ threads ] );
132
+
133
+ const openCollabBoard = () => {
134
+ setShowCommentBoard( true );
135
+ enableComplementaryArea( 'core', 'edit-post/collab-sidebar' );
136
+ };
137
+
138
+ // Function to save the comment.
139
+ const addNewComment = async ( comment, parentCommentId ) => {
140
+ const args = {
141
+ post: postId,
142
+ content: comment,
143
+ comment_type: 'block_comment',
144
+ comment_approved: 0,
145
+ };
146
+
147
+ // Create a new object, conditionally including the parent property
148
+ const updatedArgs = {
149
+ ...args,
150
+ ...( parentCommentId ? { parent: parentCommentId } : {} ),
151
+ };
152
+
153
+ const savedRecord = await saveEntityRecord(
154
+ 'root',
155
+ 'comment',
156
+ updatedArgs
157
+ );
158
+
159
+ if ( savedRecord ) {
160
+ // If it's a main comment, update the block attributes with the comment id.
161
+ if ( ! parentCommentId ) {
162
+ updateBlockAttributes( clientId, {
163
+ blockCommentId: savedRecord?.id,
164
+ } );
165
+ }
166
+
167
+ createNotice(
168
+ 'snackbar',
169
+ parentCommentId
170
+ ? // translators: Reply added successfully
171
+ __( 'Reply added successfully.' )
172
+ : // translators: Comment added successfully
173
+ __( 'Comment added successfully.' ),
174
+ {
175
+ type: 'snackbar',
176
+ isDismissible: true,
177
+ }
178
+ );
179
+ } else {
180
+ onError();
181
+ }
182
+ };
183
+
184
+ const onCommentResolve = async ( commentId ) => {
185
+ const savedRecord = await saveEntityRecord( 'root', 'comment', {
186
+ id: commentId,
187
+ status: 'approved',
188
+ } );
189
+
190
+ if ( savedRecord ) {
191
+ // translators: Comment resolved successfully
192
+ createNotice( 'snackbar', __( 'Comment marked as resolved.' ), {
193
+ type: 'snackbar',
194
+ isDismissible: true,
195
+ } );
196
+ } else {
197
+ onError();
198
+ }
199
+ };
200
+
201
+ const onEditComment = async ( commentId, comment ) => {
202
+ const savedRecord = await saveEntityRecord( 'root', 'comment', {
203
+ id: commentId,
204
+ content: comment,
205
+ } );
206
+
207
+ if ( savedRecord ) {
208
+ createNotice(
209
+ 'snackbar',
210
+ // translators: Comment edited successfully
211
+ __( 'Comment edited successfully.' ),
212
+ {
213
+ type: 'snackbar',
214
+ isDismissible: true,
215
+ }
216
+ );
217
+ } else {
218
+ onError();
219
+ }
220
+ };
221
+
222
+ const onError = () => {
223
+ createNotice(
224
+ 'error',
225
+ // translators: Error message when comment submission fails
226
+ __(
227
+ 'Something went wrong. Please try publishing the post, or you may have already submitted your comment earlier.'
228
+ ),
229
+ {
230
+ isDismissible: true,
231
+ }
232
+ );
233
+ };
234
+
235
+ const onCommentDelete = async ( commentId ) => {
236
+ const childComment = await getEntityRecord(
237
+ 'root',
238
+ 'comment',
239
+ commentId
240
+ );
241
+ await deleteEntityRecord( 'root', 'comment', commentId );
242
+
243
+ if ( childComment && ! childComment.parent ) {
244
+ updateBlockAttributes( clientId, {
245
+ blockCommentId: undefined,
246
+ } );
247
+ }
248
+
249
+ createNotice(
250
+ 'snackbar',
251
+ // translators: Comment deleted successfully
252
+ __( 'Comment deleted successfully.' ),
253
+ {
254
+ type: 'snackbar',
255
+ isDismissible: true,
256
+ }
257
+ );
258
+ };
259
+
260
+ useEffect( () => {
261
+ if ( blockDetails ) {
262
+ setBlockCommentID( blockDetails?.attributes.blockCommentId );
263
+ }
264
+ }, [ postId, clientId ] );
265
+
266
+ // Check if the experimental flag is enabled.
267
+ if (
268
+ ! isBlockCommentExperimentEnabled ||
269
+ postStatus.postStatus === 'publish'
270
+ ) {
271
+ return null; // or maybe return some message indicating no threads are available.
272
+ }
273
+
274
+ return (
275
+ <>
276
+ { ! blockCommentID && (
277
+ <AddCommentButton onClick={ openCollabBoard } />
278
+ ) }
279
+
280
+ { blockCommentID > 0 && (
281
+ <AddCommentToolbarButton onClick={ openCollabBoard } />
282
+ ) }
283
+ <PluginSidebar
284
+ identifier={ collabSidebarName }
285
+ // translators: Comments sidebar title
286
+ title={ __( 'Comments' ) }
287
+ icon={ commentIcon }
288
+ >
289
+ <div className="editor-collab-sidebar-panel">
290
+ <AddComment
291
+ threads={ resultComments }
292
+ onSubmit={ addNewComment }
293
+ showCommentBoard={ showCommentBoard }
294
+ setShowCommentBoard={ setShowCommentBoard }
295
+ />
296
+ <Comments
297
+ threads={ resultComments }
298
+ onEditComment={ onEditComment }
299
+ onAddReply={ addNewComment }
300
+ onCommentDelete={ onCommentDelete }
301
+ onCommentResolve={ onCommentResolve }
302
+ />
303
+ </div>
304
+ </PluginSidebar>
305
+ </>
306
+ );
307
+ }