@wordpress/editor 14.42.0 → 14.43.1-next.v.202604091042.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 (276) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +8 -0
  3. package/build/components/autocompleters/index.cjs +3 -0
  4. package/build/components/autocompleters/index.cjs.map +2 -2
  5. package/build/components/autocompleters/link.cjs +71 -0
  6. package/build/components/autocompleters/link.cjs.map +7 -0
  7. package/build/components/collaborators-overlay/cursor-dom-utils.cjs +1 -1
  8. package/build/components/collaborators-overlay/cursor-dom-utils.cjs.map +2 -2
  9. package/build/components/collaborators-overlay/cursor-registry.cjs +86 -0
  10. package/build/components/collaborators-overlay/cursor-registry.cjs.map +7 -0
  11. package/build/components/collaborators-overlay/index.cjs +7 -2
  12. package/build/components/collaborators-overlay/index.cjs.map +2 -2
  13. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs +1 -1
  14. package/build/components/collaborators-overlay/overlay-iframe-styles.cjs.map +2 -2
  15. package/build/components/collaborators-overlay/overlay.cjs +31 -1
  16. package/build/components/collaborators-overlay/overlay.cjs.map +2 -2
  17. package/build/components/collaborators-overlay/timing-utils.cjs +1 -1
  18. package/build/components/collaborators-overlay/timing-utils.cjs.map +2 -2
  19. package/build/components/collaborators-overlay/use-render-cursors.cjs.map +2 -2
  20. package/build/components/collaborators-presence/index.cjs +14 -4
  21. package/build/components/collaborators-presence/index.cjs.map +2 -2
  22. package/build/components/collaborators-presence/list.cjs +20 -4
  23. package/build/components/collaborators-presence/list.cjs.map +2 -2
  24. package/build/components/error-boundary/index.cjs +1 -1
  25. package/build/components/error-boundary/index.cjs.map +2 -2
  26. package/build/components/post-card-panel/index.cjs +4 -15
  27. package/build/components/post-card-panel/index.cjs.map +2 -2
  28. package/build/components/post-content-information/index.cjs +10 -13
  29. package/build/components/post-content-information/index.cjs.map +2 -2
  30. package/build/components/post-revisions-panel/index.cjs +164 -0
  31. package/build/components/post-revisions-panel/index.cjs.map +7 -0
  32. package/build/components/post-revisions-preview/revisions-slider.cjs +23 -12
  33. package/build/components/post-revisions-preview/revisions-slider.cjs.map +2 -2
  34. package/build/components/post-template/create-new-template-modal.cjs +39 -46
  35. package/build/components/post-template/create-new-template-modal.cjs.map +2 -2
  36. package/build/components/post-template/hooks.cjs +52 -6
  37. package/build/components/post-template/hooks.cjs.map +2 -2
  38. package/build/components/post-template/swap-template-button.cjs +31 -20
  39. package/build/components/post-template/swap-template-button.cjs.map +2 -2
  40. package/build/components/post-title/index.cjs +2 -2
  41. package/build/components/post-title/index.cjs.map +2 -2
  42. package/build/components/preferences-modal/index.cjs +35 -27
  43. package/build/components/preferences-modal/index.cjs.map +2 -2
  44. package/build/components/revision-block-diff/index.cjs +9 -32
  45. package/build/components/revision-block-diff/index.cjs.map +3 -3
  46. package/build/components/revision-diff-panel/index.cjs +68 -0
  47. package/build/components/revision-diff-panel/index.cjs.map +7 -0
  48. package/build/components/revision-fields-diff/index.cjs +96 -0
  49. package/build/components/revision-fields-diff/index.cjs.map +7 -0
  50. package/build/components/sidebar/dataform-post-summary.cjs +8 -53
  51. package/build/components/sidebar/dataform-post-summary.cjs.map +2 -2
  52. package/build/components/sidebar/index.cjs +25 -22
  53. package/build/components/sidebar/index.cjs.map +3 -3
  54. package/build/components/sidebar/post-revision-summary.cjs +74 -0
  55. package/build/components/sidebar/post-revision-summary.cjs.map +7 -0
  56. package/build/components/sidebar/post-summary.cjs +35 -42
  57. package/build/components/sidebar/post-summary.cjs.map +3 -3
  58. package/build/components/style-book/index.cjs +4 -3
  59. package/build/components/style-book/index.cjs.map +2 -2
  60. package/build/components/styles-canvas/revisions.cjs +2 -2
  61. package/build/components/styles-canvas/revisions.cjs.map +1 -1
  62. package/build/components/sync-connection-error-modal/index.cjs +58 -75
  63. package/build/components/sync-connection-error-modal/index.cjs.map +3 -3
  64. package/build/components/sync-connection-error-modal/use-retry-countdown.cjs +32 -9
  65. package/build/components/sync-connection-error-modal/use-retry-countdown.cjs.map +2 -2
  66. package/build/components/template-actions-panel/block-theme-content.cjs +188 -0
  67. package/build/components/template-actions-panel/block-theme-content.cjs.map +7 -0
  68. package/build/components/template-actions-panel/classic-theme-content.cjs +159 -0
  69. package/build/components/template-actions-panel/classic-theme-content.cjs.map +7 -0
  70. package/build/components/template-actions-panel/index.cjs +59 -0
  71. package/build/components/template-actions-panel/index.cjs.map +7 -0
  72. package/build/dataviews/store/private-actions.cjs +2 -0
  73. package/build/dataviews/store/private-actions.cjs.map +2 -2
  74. package/build/hooks/default-autocompleters.cjs +1 -1
  75. package/build/hooks/default-autocompleters.cjs.map +2 -2
  76. package/build/store/private-actions.cjs +15 -1
  77. package/build/store/private-actions.cjs.map +2 -2
  78. package/build/store/private-selectors.cjs +42 -19
  79. package/build/store/private-selectors.cjs.map +2 -2
  80. package/build/store/reducer.cjs +1 -1
  81. package/build/store/reducer.cjs.map +2 -2
  82. package/build-module/components/autocompleters/index.mjs +4 -2
  83. package/build-module/components/autocompleters/index.mjs.map +2 -2
  84. package/build-module/components/autocompleters/link.mjs +40 -0
  85. package/build-module/components/autocompleters/link.mjs.map +7 -0
  86. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs +1 -1
  87. package/build-module/components/collaborators-overlay/cursor-dom-utils.mjs.map +2 -2
  88. package/build-module/components/collaborators-overlay/cursor-registry.mjs +61 -0
  89. package/build-module/components/collaborators-overlay/cursor-registry.mjs.map +7 -0
  90. package/build-module/components/collaborators-overlay/index.mjs +7 -2
  91. package/build-module/components/collaborators-overlay/index.mjs.map +2 -2
  92. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs +1 -1
  93. package/build-module/components/collaborators-overlay/overlay-iframe-styles.mjs.map +2 -2
  94. package/build-module/components/collaborators-overlay/overlay.mjs +32 -2
  95. package/build-module/components/collaborators-overlay/overlay.mjs.map +2 -2
  96. package/build-module/components/collaborators-overlay/timing-utils.mjs +1 -1
  97. package/build-module/components/collaborators-overlay/timing-utils.mjs.map +2 -2
  98. package/build-module/components/collaborators-overlay/use-render-cursors.mjs.map +2 -2
  99. package/build-module/components/collaborators-presence/index.mjs +14 -4
  100. package/build-module/components/collaborators-presence/index.mjs.map +2 -2
  101. package/build-module/components/collaborators-presence/list.mjs +20 -4
  102. package/build-module/components/collaborators-presence/list.mjs.map +2 -2
  103. package/build-module/components/error-boundary/index.mjs +1 -1
  104. package/build-module/components/error-boundary/index.mjs.map +2 -2
  105. package/build-module/components/post-card-panel/index.mjs +6 -17
  106. package/build-module/components/post-card-panel/index.mjs.map +2 -2
  107. package/build-module/components/post-content-information/index.mjs +6 -13
  108. package/build-module/components/post-content-information/index.mjs.map +2 -2
  109. package/build-module/components/post-revisions-panel/index.mjs +139 -0
  110. package/build-module/components/post-revisions-panel/index.mjs.map +7 -0
  111. package/build-module/components/post-revisions-preview/revisions-slider.mjs +23 -12
  112. package/build-module/components/post-revisions-preview/revisions-slider.mjs.map +2 -2
  113. package/build-module/components/post-template/create-new-template-modal.mjs +39 -46
  114. package/build-module/components/post-template/create-new-template-modal.mjs.map +2 -2
  115. package/build-module/components/post-template/hooks.mjs +53 -7
  116. package/build-module/components/post-template/hooks.mjs.map +2 -2
  117. package/build-module/components/post-template/swap-template-button.mjs +27 -20
  118. package/build-module/components/post-template/swap-template-button.mjs.map +2 -2
  119. package/build-module/components/post-title/index.mjs +2 -2
  120. package/build-module/components/post-title/index.mjs.map +2 -2
  121. package/build-module/components/preferences-modal/index.mjs +35 -27
  122. package/build-module/components/preferences-modal/index.mjs.map +2 -2
  123. package/build-module/components/revision-block-diff/index.mjs +9 -32
  124. package/build-module/components/revision-block-diff/index.mjs.map +2 -2
  125. package/build-module/components/revision-diff-panel/index.mjs +37 -0
  126. package/build-module/components/revision-diff-panel/index.mjs.map +7 -0
  127. package/build-module/components/revision-fields-diff/index.mjs +65 -0
  128. package/build-module/components/revision-fields-diff/index.mjs.map +7 -0
  129. package/build-module/components/sidebar/dataform-post-summary.mjs +8 -53
  130. package/build-module/components/sidebar/dataform-post-summary.mjs.map +2 -2
  131. package/build-module/components/sidebar/index.mjs +25 -22
  132. package/build-module/components/sidebar/index.mjs.map +2 -2
  133. package/build-module/components/sidebar/post-revision-summary.mjs +43 -0
  134. package/build-module/components/sidebar/post-revision-summary.mjs.map +7 -0
  135. package/build-module/components/sidebar/post-summary.mjs +31 -42
  136. package/build-module/components/sidebar/post-summary.mjs.map +2 -2
  137. package/build-module/components/style-book/index.mjs +4 -3
  138. package/build-module/components/style-book/index.mjs.map +2 -2
  139. package/build-module/components/styles-canvas/revisions.mjs +2 -2
  140. package/build-module/components/styles-canvas/revisions.mjs.map +1 -1
  141. package/build-module/components/sync-connection-error-modal/index.mjs +58 -76
  142. package/build-module/components/sync-connection-error-modal/index.mjs.map +2 -2
  143. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs +33 -10
  144. package/build-module/components/sync-connection-error-modal/use-retry-countdown.mjs.map +2 -2
  145. package/build-module/components/template-actions-panel/block-theme-content.mjs +167 -0
  146. package/build-module/components/template-actions-panel/block-theme-content.mjs.map +7 -0
  147. package/build-module/components/template-actions-panel/classic-theme-content.mjs +138 -0
  148. package/build-module/components/template-actions-panel/classic-theme-content.mjs.map +7 -0
  149. package/build-module/components/template-actions-panel/index.mjs +28 -0
  150. package/build-module/components/template-actions-panel/index.mjs.map +7 -0
  151. package/build-module/dataviews/store/private-actions.mjs +5 -1
  152. package/build-module/dataviews/store/private-actions.mjs.map +2 -2
  153. package/build-module/hooks/default-autocompleters.mjs +2 -2
  154. package/build-module/hooks/default-autocompleters.mjs.map +2 -2
  155. package/build-module/store/private-actions.mjs +15 -1
  156. package/build-module/store/private-actions.mjs.map +2 -2
  157. package/build-module/store/private-selectors.mjs +42 -19
  158. package/build-module/store/private-selectors.mjs.map +2 -2
  159. package/build-module/store/reducer.mjs +1 -1
  160. package/build-module/store/reducer.mjs.map +2 -2
  161. package/build-style/style-rtl.css +124 -44
  162. package/build-style/style.css +124 -44
  163. package/build-types/bindings/post-data.d.ts +3 -3
  164. package/build-types/bindings/term-data.d.ts +14 -14
  165. package/build-types/components/autocompleters/index.d.ts +1 -0
  166. package/build-types/components/autocompleters/link.d.ts +12 -0
  167. package/build-types/components/autocompleters/link.d.ts.map +1 -0
  168. package/build-types/components/collaborators-overlay/cursor-registry.d.ts +36 -0
  169. package/build-types/components/collaborators-overlay/cursor-registry.d.ts.map +1 -0
  170. package/build-types/components/collaborators-overlay/index.d.ts +7 -4
  171. package/build-types/components/collaborators-overlay/index.d.ts.map +1 -1
  172. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts +1 -1
  173. package/build-types/components/collaborators-overlay/overlay-iframe-styles.d.ts.map +1 -1
  174. package/build-types/components/collaborators-overlay/overlay.d.ts +4 -1
  175. package/build-types/components/collaborators-overlay/overlay.d.ts.map +1 -1
  176. package/build-types/components/collaborators-overlay/use-render-cursors.d.ts.map +1 -1
  177. package/build-types/components/collaborators-presence/index.d.ts.map +1 -1
  178. package/build-types/components/collaborators-presence/list.d.ts +4 -1
  179. package/build-types/components/collaborators-presence/list.d.ts.map +1 -1
  180. package/build-types/components/keyboard-shortcut-help-modal/config.d.ts +11 -11
  181. package/build-types/components/post-actions/set-as-homepage.d.ts +1 -1
  182. package/build-types/components/post-actions/set-as-posts-page.d.ts +1 -1
  183. package/build-types/components/post-card-panel/index.d.ts.map +1 -1
  184. package/build-types/components/post-content-information/index.d.ts +4 -1
  185. package/build-types/components/post-content-information/index.d.ts.map +1 -1
  186. package/build-types/components/post-format/index.d.ts +10 -10
  187. package/build-types/components/post-locked-modal/index.d.ts +2 -2
  188. package/build-types/components/post-revisions-panel/index.d.ts +2 -0
  189. package/build-types/components/post-revisions-panel/index.d.ts.map +1 -0
  190. package/build-types/components/post-revisions-preview/revisions-slider.d.ts.map +1 -1
  191. package/build-types/components/post-status/index.d.ts +10 -10
  192. package/build-types/components/post-template/create-new-template-modal.d.ts.map +1 -1
  193. package/build-types/components/post-template/hooks.d.ts +1 -1
  194. package/build-types/components/post-template/hooks.d.ts.map +1 -1
  195. package/build-types/components/post-template/swap-template-button.d.ts +4 -0
  196. package/build-types/components/post-template/swap-template-button.d.ts.map +1 -1
  197. package/build-types/components/post-visibility/utils.d.ts +6 -6
  198. package/build-types/components/revision-block-diff/index.d.ts.map +1 -1
  199. package/build-types/components/revision-diff-panel/index.d.ts +14 -0
  200. package/build-types/components/revision-diff-panel/index.d.ts.map +1 -0
  201. package/build-types/components/revision-fields-diff/index.d.ts +6 -0
  202. package/build-types/components/revision-fields-diff/index.d.ts.map +1 -0
  203. package/build-types/components/sidebar/dataform-post-summary.d.ts.map +1 -1
  204. package/build-types/components/sidebar/index.d.ts.map +1 -1
  205. package/build-types/components/sidebar/post-revision-summary.d.ts +2 -0
  206. package/build-types/components/sidebar/post-revision-summary.d.ts.map +1 -0
  207. package/build-types/components/sidebar/post-summary.d.ts +3 -0
  208. package/build-types/components/sidebar/post-summary.d.ts.map +1 -1
  209. package/build-types/components/style-book/index.d.ts +2 -1
  210. package/build-types/components/style-book/index.d.ts.map +1 -1
  211. package/build-types/components/sync-connection-error-modal/index.d.ts +0 -14
  212. package/build-types/components/sync-connection-error-modal/index.d.ts.map +1 -1
  213. package/build-types/components/sync-connection-error-modal/use-retry-countdown.d.ts.map +1 -1
  214. package/build-types/components/template-actions-panel/block-theme-content.d.ts +2 -0
  215. package/build-types/components/template-actions-panel/block-theme-content.d.ts.map +1 -0
  216. package/build-types/components/template-actions-panel/classic-theme-content.d.ts +2 -0
  217. package/build-types/components/template-actions-panel/classic-theme-content.d.ts.map +1 -0
  218. package/build-types/components/template-actions-panel/index.d.ts +2 -0
  219. package/build-types/components/template-actions-panel/index.d.ts.map +1 -0
  220. package/build-types/dataviews/store/private-actions.d.ts.map +1 -1
  221. package/build-types/store/private-actions.d.ts.map +1 -1
  222. package/build-types/store/private-selectors.d.ts.map +1 -1
  223. package/build-types/store/reducer.d.ts +10 -10
  224. package/build-types/store/reducer.d.ts.map +1 -1
  225. package/build-types/utils/pageTypeBadge.d.ts +1 -1
  226. package/build-types/utils/pageTypeBadge.d.ts.map +1 -1
  227. package/package.json +45 -44
  228. package/src/components/autocompleters/index.js +1 -0
  229. package/src/components/autocompleters/link.js +47 -0
  230. package/src/components/autocompleters/style.scss +6 -0
  231. package/src/components/collaborators-overlay/cursor-dom-utils.ts +1 -1
  232. package/src/components/collaborators-overlay/cursor-registry.ts +96 -0
  233. package/src/components/collaborators-overlay/index.tsx +12 -4
  234. package/src/components/collaborators-overlay/overlay-iframe-styles.ts +1 -1
  235. package/src/components/collaborators-overlay/overlay.tsx +45 -1
  236. package/src/components/collaborators-overlay/timing-utils.ts +1 -1
  237. package/src/components/collaborators-overlay/use-render-cursors.ts +4 -2
  238. package/src/components/collaborators-presence/index.tsx +9 -1
  239. package/src/components/collaborators-presence/list.tsx +25 -1
  240. package/src/components/error-boundary/index.js +1 -1
  241. package/src/components/error-boundary/index.native.js +1 -1
  242. package/src/components/post-card-panel/index.js +7 -21
  243. package/src/components/post-content-information/index.js +5 -16
  244. package/src/components/post-revisions-panel/index.js +151 -0
  245. package/src/components/post-revisions-panel/style.scss +16 -0
  246. package/src/components/post-revisions-preview/revisions-slider.js +23 -19
  247. package/src/components/post-template/create-new-template-modal.js +1 -4
  248. package/src/components/post-template/hooks.js +65 -9
  249. package/src/components/post-template/style.scss +0 -6
  250. package/src/components/post-template/swap-template-button.js +30 -21
  251. package/src/components/post-title/index.js +3 -3
  252. package/src/components/preferences-modal/index.js +37 -25
  253. package/src/components/revision-block-diff/index.js +8 -43
  254. package/src/components/revision-diff-panel/index.js +59 -0
  255. package/src/components/revision-fields-diff/index.js +91 -0
  256. package/src/components/sidebar/dataform-post-summary.js +8 -55
  257. package/src/components/sidebar/index.js +33 -22
  258. package/src/components/sidebar/post-revision-summary.js +50 -0
  259. package/src/components/sidebar/post-summary.js +22 -40
  260. package/src/components/sidebar/style.scss +7 -0
  261. package/src/components/style-book/index.js +4 -2
  262. package/src/components/styles-canvas/revisions.js +2 -2
  263. package/src/components/sync-connection-error-modal/index.tsx +128 -155
  264. package/src/components/sync-connection-error-modal/use-retry-countdown.ts +46 -10
  265. package/src/components/template-actions-panel/block-theme-content.js +196 -0
  266. package/src/components/template-actions-panel/classic-theme-content.js +170 -0
  267. package/src/components/template-actions-panel/index.js +32 -0
  268. package/src/components/template-actions-panel/style.scss +39 -0
  269. package/src/dataviews/store/private-actions.ts +6 -0
  270. package/src/hooks/default-autocompleters.js +2 -2
  271. package/src/hooks/test/default-autocompleters.js +2 -2
  272. package/src/store/private-actions.js +18 -2
  273. package/src/store/private-selectors.js +43 -22
  274. package/src/store/reducer.js +9 -8
  275. package/src/style.scss +3 -1
  276. /package/src/components/{revision-block-diff → revision-diff-panel}/style.scss +0 -0
@@ -8,7 +8,6 @@ import { serialize } from '@wordpress/blocks';
8
8
  import {
9
9
  store as coreDataStore,
10
10
  privateApis as coreDataPrivateApis,
11
- type ConnectionError,
12
11
  } from '@wordpress/core-data';
13
12
  // @ts-expect-error - No type declarations available for @wordpress/block-editor
14
13
  // prettier-ignore
@@ -16,10 +15,10 @@ import { privateApis, store as blockEditorStore } from '@wordpress/block-editor'
16
15
  import {
17
16
  Button,
18
17
  Modal,
19
- withFilters,
20
18
  __experimentalHStack as HStack,
21
19
  __experimentalVStack as VStack,
22
20
  } from '@wordpress/components';
21
+ import { applyFilters } from '@wordpress/hooks';
23
22
  import { useState, useEffect } from '@wordpress/element';
24
23
  import { __, sprintf, _n } from '@wordpress/i18n';
25
24
 
@@ -35,145 +34,7 @@ const { BlockCanvasCover } = unlock( privateApis );
35
34
  const { retrySyncConnection } = unlock( coreDataPrivateApis );
36
35
 
37
36
  // Debounce time for initial disconnected status to allow connection to establish.
38
- const INITIAL_DISCONNECTED_DEBOUNCE_MS = 5000;
39
-
40
- // Debounce time for showing the disconnect dialog after the intial connection,
41
- // allowing brief network interruptions to resolve.
42
- const DISCONNECTED_DEBOUNCE_MS = 2000;
43
-
44
- export interface SyncConnectionErrorModalProps {
45
- description: string; // Modal description.
46
- error?: ConnectionError; // Error object with a `code` property.
47
- manualRetry?: () => void; // Callback for when the retry button is clicked.
48
- postType?: { slug?: string; labels?: { name?: string } } | null; // Current post type object.
49
- secondsRemainingUntilAutoRetry?: number; // Seconds remaining until the next automatic retry attempt, if applicable.
50
- title: string; // Modal title.
51
- }
52
-
53
- /**
54
- * Default sync connection modal component.
55
- *
56
- * Can be replaced or wrapped via the `editor.SyncConnectionErrorModal` filter.
57
- *
58
- * @param props - SyncConnectionErrorModalProps.
59
- */
60
- function DefaultSyncConnectionErrorModal(
61
- props: SyncConnectionErrorModalProps
62
- ) {
63
- const {
64
- description,
65
- manualRetry,
66
- postType,
67
- secondsRemainingUntilAutoRetry,
68
- title,
69
- } = props;
70
- const copyButtonRef = useCopyToClipboard( () => {
71
- const blocks = select( blockEditorStore ).getBlocks();
72
- return serialize( blocks );
73
- } );
74
-
75
- let retryCountdownText: string = '';
76
- let isRetrying = false;
77
- if (
78
- secondsRemainingUntilAutoRetry &&
79
- secondsRemainingUntilAutoRetry > 0
80
- ) {
81
- retryCountdownText = sprintf(
82
- /* translators: %d: number of seconds until retry */
83
- _n(
84
- 'Retrying connection in %d second\u2026',
85
- 'Retrying connection in %d seconds\u2026',
86
- secondsRemainingUntilAutoRetry
87
- ),
88
- secondsRemainingUntilAutoRetry
89
- );
90
- } else if ( 0 === secondsRemainingUntilAutoRetry ) {
91
- isRetrying = true;
92
- retryCountdownText = __( 'Retrying\u2026' );
93
- }
94
-
95
- let editPostHref = 'edit.php';
96
- if ( postType?.slug ) {
97
- editPostHref = `edit.php?post_type=${ postType.slug }`;
98
- }
99
-
100
- return (
101
- <Modal
102
- overlayClassName="editor-sync-connection-error-modal"
103
- isDismissible={ false }
104
- onRequestClose={ () => {} }
105
- shouldCloseOnClickOutside={ false }
106
- shouldCloseOnEsc={ false }
107
- size="medium"
108
- title={ title }
109
- >
110
- <VStack spacing={ 6 }>
111
- <p>{ description }</p>
112
- { retryCountdownText && (
113
- <p className="editor-sync-connection-error-modal__retry-countdown">
114
- { retryCountdownText }
115
- </p>
116
- ) }
117
- <HStack justify="right">
118
- <Button
119
- __next40pxDefaultSize
120
- href={ editPostHref }
121
- isDestructive
122
- variant="tertiary"
123
- >
124
- { sprintf(
125
- /* translators: %s: Post type name (e.g., "Posts", "Pages"). */
126
- __( 'Back to %s' ),
127
- postType?.labels?.name ?? __( 'Posts' )
128
- ) }
129
- </Button>
130
- <Button
131
- __next40pxDefaultSize
132
- ref={ copyButtonRef }
133
- variant={ manualRetry ? 'secondary' : 'primary' }
134
- >
135
- { __( 'Copy Post Content' ) }
136
- </Button>
137
- { manualRetry && (
138
- <Button
139
- __next40pxDefaultSize
140
- accessibleWhenDisabled
141
- aria-disabled={ isRetrying }
142
- disabled={ isRetrying }
143
- isBusy={ isRetrying }
144
- variant="primary"
145
- onClick={ manualRetry }
146
- >
147
- { __( 'Retry' ) }
148
- </Button>
149
- ) }
150
- </HStack>
151
- </VStack>
152
- </Modal>
153
- );
154
- }
155
-
156
- /**
157
- * Filtered version of the sync connection modal, allowing third-party
158
- * plugins to replace the default modal via:
159
- *
160
- * ```js
161
- * wp.hooks.addFilter(
162
- * 'editor.SyncConnectionErrorModal',
163
- * 'my-plugin/custom-sync-connection-error-modal',
164
- * ( OriginalComponent ) => ( props ) => {
165
- * // Return a custom component or wrap the original.
166
- * return <OriginalComponent { ...props } />;
167
- * }
168
- * );
169
- * ```
170
- */
171
- // @ts-ignore
172
- const FilteredSyncConnectionErrorModal = globalThis.IS_GUTENBERG_PLUGIN
173
- ? withFilters( 'editor.SyncConnectionErrorModal' )(
174
- DefaultSyncConnectionErrorModal
175
- )
176
- : DefaultSyncConnectionErrorModal;
37
+ const INITIAL_DISCONNECTED_DEBOUNCE_MS = 20000;
177
38
 
178
39
  /**
179
40
  * Sync connection modal that displays when any entity reports a disconnection.
@@ -207,7 +68,10 @@ export function SyncConnectionErrorModal() {
207
68
  const { onManualRetry, secondsRemaining } =
208
69
  useRetryCountdown( connectionStatus );
209
70
 
210
- const isConnected = 'connected' === connectionStatus?.status;
71
+ const copyButtonRef = useCopyToClipboard( () => {
72
+ const blocks = select( blockEditorStore ).getBlocks();
73
+ return serialize( blocks );
74
+ } );
211
75
 
212
76
  // Set hasInitialized after a debounce to give extra time on initial load.
213
77
  useEffect( () => {
@@ -218,18 +82,30 @@ export function SyncConnectionErrorModal() {
218
82
  return () => clearTimeout( timeout );
219
83
  }, [] );
220
84
 
85
+ // Show the modal when disconnected and either retries are exhausted or
86
+ // no retry is available (unrecoverable error). Hide on reconnect.
87
+ // The 'connecting' state is ignored so the modal preserves its current
88
+ // visibility during active retry attempts.
89
+ const canRetry =
90
+ connectionStatus &&
91
+ 'disconnected' === connectionStatus.status &&
92
+ ( connectionStatus.canManuallyRetry ||
93
+ connectionStatus.willAutoRetryInMs );
94
+
221
95
  useEffect( () => {
222
- if ( isConnected ) {
96
+ if ( 'connected' === connectionStatus?.status ) {
223
97
  setShowModal( false );
224
98
  return;
225
99
  }
226
100
 
227
- const timeout = setTimeout( () => {
101
+ if (
102
+ connectionStatus?.status &&
103
+ 'connecting' !== connectionStatus.status &&
104
+ ( ! canRetry || connectionStatus.backgroundRetriesFailed )
105
+ ) {
228
106
  setShowModal( true );
229
- }, DISCONNECTED_DEBOUNCE_MS );
230
-
231
- return () => clearTimeout( timeout );
232
- }, [ isConnected ] );
107
+ }
108
+ }, [ connectionStatus, canRetry ] );
233
109
 
234
110
  if ( ! isCollaborationEnabled || ! hasInitialized || ! showModal ) {
235
111
  return null;
@@ -239,6 +115,36 @@ export function SyncConnectionErrorModal() {
239
115
  connectionStatus && 'error' in connectionStatus
240
116
  ? connectionStatus?.error
241
117
  : undefined;
118
+
119
+ // For unrecoverable errors (no retry available), allow plugins to handle
120
+ // the error themselves. If a plugin returns a value other than false, it
121
+ // signals that it has taken over error display and the default modal is
122
+ // suppressed.
123
+ //
124
+ // @example
125
+ // ```js
126
+ // wp.hooks.addFilter(
127
+ // 'editor.isSyncConnectionErrorHandled',
128
+ // 'my-plugin/handle-sync-error',
129
+ // ( isHandled, errorCode ) => {
130
+ // if ( errorCode === 'connection-limit-exceeded' ) {
131
+ // return true; // Plugin handles this error via its own UI.
132
+ // }
133
+ // return isHandled;
134
+ // }
135
+ // );
136
+ // ```
137
+ if (
138
+ ! canRetry &&
139
+ applyFilters(
140
+ 'editor.isSyncConnectionErrorHandled',
141
+ false,
142
+ error?.code
143
+ ) !== false
144
+ ) {
145
+ return null;
146
+ }
147
+
242
148
  const manualRetry =
243
149
  connectionStatus &&
244
150
  'canManuallyRetry' in connectionStatus &&
@@ -248,18 +154,85 @@ export function SyncConnectionErrorModal() {
248
154
  retrySyncConnection();
249
155
  }
250
156
  : undefined;
157
+
251
158
  const messages = getSyncErrorMessages( error );
252
159
 
160
+ let retryCountdownText: string = '';
161
+ let isRetrying = false;
162
+ if ( secondsRemaining && secondsRemaining > 0 ) {
163
+ retryCountdownText = sprintf(
164
+ /* translators: %d: number of seconds until retry */
165
+ _n(
166
+ 'Retrying connection in %d second\u2026',
167
+ 'Retrying connection in %d seconds\u2026',
168
+ secondsRemaining
169
+ ),
170
+ secondsRemaining
171
+ );
172
+ } else if ( 0 === secondsRemaining ) {
173
+ isRetrying = true;
174
+ retryCountdownText = __( 'Retrying\u2026' );
175
+ }
176
+
177
+ let editPostHref = 'edit.php';
178
+ if ( postType?.slug ) {
179
+ editPostHref = `edit.php?post_type=${ postType.slug }`;
180
+ }
181
+
253
182
  return (
254
183
  <BlockCanvasCover.Fill>
255
- <FilteredSyncConnectionErrorModal
256
- description={ messages.description }
257
- error={ error }
258
- manualRetry={ manualRetry }
259
- postType={ postType }
260
- secondsRemainingUntilAutoRetry={ secondsRemaining }
184
+ <Modal
185
+ overlayClassName="editor-sync-connection-error-modal"
186
+ isDismissible={ false }
187
+ onRequestClose={ () => {} }
188
+ shouldCloseOnClickOutside={ false }
189
+ shouldCloseOnEsc={ false }
190
+ size="medium"
261
191
  title={ messages.title }
262
- />
192
+ >
193
+ <VStack spacing={ 6 }>
194
+ <p>{ messages.description }</p>
195
+ { retryCountdownText && (
196
+ <p className="editor-sync-connection-error-modal__retry-countdown">
197
+ { retryCountdownText }
198
+ </p>
199
+ ) }
200
+ <HStack justify="right">
201
+ <Button
202
+ __next40pxDefaultSize
203
+ href={ editPostHref }
204
+ isDestructive
205
+ variant="tertiary"
206
+ >
207
+ { sprintf(
208
+ /* translators: %s: Post type name (e.g., "Posts", "Pages"). */
209
+ __( 'Back to %s' ),
210
+ postType?.labels?.name ?? __( 'Posts' )
211
+ ) }
212
+ </Button>
213
+ <Button
214
+ __next40pxDefaultSize
215
+ ref={ copyButtonRef }
216
+ variant={ manualRetry ? 'secondary' : 'primary' }
217
+ >
218
+ { __( 'Copy Post Content' ) }
219
+ </Button>
220
+ { manualRetry && (
221
+ <Button
222
+ __next40pxDefaultSize
223
+ accessibleWhenDisabled
224
+ aria-disabled={ isRetrying }
225
+ disabled={ isRetrying }
226
+ isBusy={ isRetrying }
227
+ variant="primary"
228
+ onClick={ manualRetry }
229
+ >
230
+ { __( 'Retry' ) }
231
+ </Button>
232
+ ) }
233
+ </HStack>
234
+ </VStack>
235
+ </Modal>
263
236
  </BlockCanvasCover.Fill>
264
237
  );
265
238
  }
@@ -2,7 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import type { ConnectionStatus } from '@wordpress/core-data';
5
- import { useState, useEffect } from '@wordpress/element';
5
+ import { useState, useEffect, useRef } from '@wordpress/element';
6
6
 
7
7
  interface UseRetryCountdownResult {
8
8
  onManualRetry: () => void;
@@ -13,6 +13,7 @@ export function useRetryCountdown(
13
13
  connectionStatus?: ConnectionStatus | null
14
14
  ): UseRetryCountdownResult {
15
15
  const [ secondsRemaining, setSecondsRemaining ] = useState< number >();
16
+ const hasRetriedRef = useRef( false );
16
17
 
17
18
  useEffect( () => {
18
19
  if ( ! connectionStatus ) {
@@ -22,6 +23,7 @@ export function useRetryCountdown(
22
23
  // Only clear countdown when explicitly connected.
23
24
  if ( 'connected' === connectionStatus.status ) {
24
25
  setSecondsRemaining( undefined );
26
+ hasRetriedRef.current = false;
25
27
  return;
26
28
  }
27
29
 
@@ -37,21 +39,55 @@ export function useRetryCountdown(
37
39
 
38
40
  const { willAutoRetryInMs: retryInMs } = connectionStatus;
39
41
  const retryAt = Date.now() + retryInMs;
40
- setSecondsRemaining( Math.ceil( retryInMs / 1000 ) );
41
42
 
42
- const intervalId = setInterval( () => {
43
- const remaining = Math.ceil( ( retryAt - Date.now() ) / 1000 );
44
- setSecondsRemaining( Math.max( 0, remaining ) );
45
- if ( remaining <= 0 ) {
46
- clearInterval( intervalId );
43
+ // After a retry attempt (manual or automatic), show "Retrying..."
44
+ // for 500ms before starting the next countdown. Skip the delay on
45
+ // the very first disconnect so the countdown starts immediately.
46
+ const hasRetried = hasRetriedRef.current;
47
+ hasRetriedRef.current = true;
48
+
49
+ if ( hasRetried ) {
50
+ setSecondsRemaining( 0 );
51
+ }
52
+
53
+ let countdownIntervalId: ReturnType< typeof setInterval > | null = null;
54
+
55
+ const startCountdown = () => {
56
+ setSecondsRemaining( Math.ceil( ( retryAt - Date.now() ) / 1000 ) );
57
+
58
+ countdownIntervalId = setInterval( () => {
59
+ const remaining = Math.ceil( ( retryAt - Date.now() ) / 1000 );
60
+ setSecondsRemaining( Math.max( 0, remaining ) );
61
+
62
+ if ( remaining <= 0 && countdownIntervalId ) {
63
+ clearInterval( countdownIntervalId );
64
+ }
65
+ }, 1000 );
66
+ };
67
+
68
+ const retryingDelayId = hasRetried
69
+ ? setTimeout( startCountdown, 500 )
70
+ : null;
71
+
72
+ if ( ! retryingDelayId ) {
73
+ startCountdown();
74
+ }
75
+
76
+ return () => {
77
+ if ( retryingDelayId ) {
78
+ clearTimeout( retryingDelayId );
47
79
  }
48
- }, 1000 );
49
80
 
50
- return () => clearInterval( intervalId );
81
+ if ( countdownIntervalId ) {
82
+ clearInterval( countdownIntervalId );
83
+ }
84
+ };
51
85
  }, [ connectionStatus ] );
52
86
 
53
87
  return {
54
- onManualRetry: () => setSecondsRemaining( 0 ),
88
+ onManualRetry: () => {
89
+ setSecondsRemaining( 0 );
90
+ },
55
91
  secondsRemaining,
56
92
  };
57
93
  }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect, useDispatch } from '@wordpress/data';
5
+ import {
6
+ useEntityRecord,
7
+ useEntityBlockEditor,
8
+ store as coreStore,
9
+ } from '@wordpress/core-data';
10
+ import { BlockPreview } from '@wordpress/block-editor';
11
+ import {
12
+ PanelBody,
13
+ Button,
14
+ Tooltip,
15
+ __experimentalHStack as HStack,
16
+ __experimentalVStack as VStack,
17
+ } from '@wordpress/components';
18
+ import { useState } from '@wordpress/element';
19
+ import { __, sprintf } from '@wordpress/i18n';
20
+ import { decodeEntities } from '@wordpress/html-entities';
21
+ import { store as noticesStore } from '@wordpress/notices';
22
+ import { store as preferencesStore } from '@wordpress/preferences';
23
+
24
+ /**
25
+ * Internal dependencies
26
+ */
27
+ import { store as editorStore } from '../../store';
28
+ import CreateNewTemplateModal from '../post-template/create-new-template-modal';
29
+ import { SwapTemplateModal } from '../post-template/swap-template-button';
30
+ import { useAvailableTemplates } from '../post-template/hooks';
31
+
32
+ export default function TemplateActionsPanelContent() {
33
+ const templateId = useSelect(
34
+ ( select ) => select( editorStore ).getCurrentTemplateId(),
35
+ []
36
+ );
37
+ const [ isCreateModalOpen, setIsCreateModalOpen ] = useState( false );
38
+ const [ isSwapModalOpen, setIsSwapModalOpen ] = useState( false );
39
+
40
+ const availableTemplates = useAvailableTemplates();
41
+ const hasSwapTargets = !! availableTemplates?.length;
42
+
43
+ const {
44
+ onNavigateToEntityRecord,
45
+ canCreateTemplate,
46
+ hasGoBack,
47
+ getEditorSettings,
48
+ } = useSelect( ( select ) => {
49
+ const { getEditorSettings: _getEditorSettings } = select( editorStore );
50
+ const editorSettings = _getEditorSettings();
51
+ return {
52
+ onNavigateToEntityRecord: editorSettings.onNavigateToEntityRecord,
53
+ canCreateTemplate: !! select( coreStore ).canUser( 'create', {
54
+ kind: 'postType',
55
+ name: 'wp_template',
56
+ } ),
57
+ hasGoBack: editorSettings.hasOwnProperty(
58
+ 'onNavigateToPreviousEntityRecord'
59
+ ),
60
+ getEditorSettings: _getEditorSettings,
61
+ };
62
+ }, [] );
63
+
64
+ const { get: getPreference } = useSelect( preferencesStore );
65
+ const { createSuccessNotice } = useDispatch( noticesStore );
66
+
67
+ const { editedRecord: template, hasResolved } = useEntityRecord(
68
+ 'postType',
69
+ 'wp_template',
70
+ templateId
71
+ );
72
+
73
+ const [ blocks ] = useEntityBlockEditor( 'postType', 'wp_template', {
74
+ id: templateId,
75
+ } );
76
+
77
+ if ( ! hasResolved ) {
78
+ return null;
79
+ }
80
+
81
+ // The site editor does not have a `onNavigateToPreviousEntityRecord` setting as it uses its own routing
82
+ // and assigns its own backlink to focusMode pages.
83
+ const notificationAction = hasGoBack
84
+ ? [
85
+ {
86
+ label: __( 'Go back' ),
87
+ onClick: () =>
88
+ getEditorSettings().onNavigateToPreviousEntityRecord(),
89
+ },
90
+ ]
91
+ : undefined;
92
+
93
+ const mayShowTemplateEditNotice = () => {
94
+ if ( ! getPreference( 'core/edit-site', 'welcomeGuideTemplate' ) ) {
95
+ createSuccessNotice(
96
+ __(
97
+ 'Editing template. Changes made here affect all posts and pages that use the template.'
98
+ ),
99
+ { type: 'snackbar', actions: notificationAction }
100
+ );
101
+ }
102
+ };
103
+
104
+ const templateName = decodeEntities( template.title );
105
+
106
+ const previewContent = !! blocks?.length && (
107
+ <BlockPreview.Async>
108
+ <BlockPreview blocks={ blocks } />
109
+ </BlockPreview.Async>
110
+ );
111
+
112
+ const renderPreview = () => {
113
+ if ( ! previewContent ) {
114
+ return null;
115
+ }
116
+
117
+ if ( hasSwapTargets ) {
118
+ const tooltipText = __( 'Change template' );
119
+ return (
120
+ <Tooltip text={ tooltipText }>
121
+ <div
122
+ className="editor-template-actions-panel__preview"
123
+ role="button"
124
+ tabIndex={ 0 }
125
+ aria-label={ tooltipText }
126
+ onClick={ () => setIsSwapModalOpen( true ) }
127
+ onKeyPress={ () => setIsSwapModalOpen( true ) }
128
+ >
129
+ { previewContent }
130
+ </div>
131
+ </Tooltip>
132
+ );
133
+ }
134
+
135
+ return (
136
+ <div className="editor-template-actions-panel__preview">
137
+ { previewContent }
138
+ </div>
139
+ );
140
+ };
141
+
142
+ return (
143
+ <>
144
+ <PanelBody
145
+ title={ sprintf(
146
+ /* translators: %s: template name */
147
+ __( 'Template: %s' ),
148
+ templateName
149
+ ) }
150
+ initialOpen={ false }
151
+ >
152
+ <VStack>
153
+ { renderPreview() }
154
+ <HStack>
155
+ { onNavigateToEntityRecord && (
156
+ <Button
157
+ className="editor-template-actions-panel__action"
158
+ __next40pxDefaultSize
159
+ variant="secondary"
160
+ onClick={ () => {
161
+ onNavigateToEntityRecord( {
162
+ postId: template.id,
163
+ postType: 'wp_template',
164
+ } );
165
+ mayShowTemplateEditNotice();
166
+ } }
167
+ >
168
+ { __( 'Edit' ) }
169
+ </Button>
170
+ ) }
171
+ { canCreateTemplate && (
172
+ <Button
173
+ className="editor-template-actions-panel__action"
174
+ __next40pxDefaultSize
175
+ variant="secondary"
176
+ onClick={ () => setIsCreateModalOpen( true ) }
177
+ >
178
+ { __( 'Create new' ) }
179
+ </Button>
180
+ ) }
181
+ </HStack>
182
+ </VStack>
183
+ </PanelBody>
184
+ { isCreateModalOpen && (
185
+ <CreateNewTemplateModal
186
+ onClose={ () => setIsCreateModalOpen( false ) }
187
+ />
188
+ ) }
189
+ { isSwapModalOpen && (
190
+ <SwapTemplateModal
191
+ onRequestClose={ () => setIsSwapModalOpen( false ) }
192
+ />
193
+ ) }
194
+ </>
195
+ );
196
+ }