@wordpress/components 28.8.4 → 28.9.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 (329) hide show
  1. package/CHANGELOG.md +26 -3
  2. package/build/autocomplete/index.js +4 -1
  3. package/build/autocomplete/index.js.map +1 -1
  4. package/build/border-box-control/border-box-control/component.js +3 -14
  5. package/build/border-box-control/border-box-control/component.js.map +1 -1
  6. package/build/border-control/border-control/component.js +1 -2
  7. package/build/border-control/border-control/component.js.map +1 -1
  8. package/build/border-control/border-control-dropdown/component.js +2 -14
  9. package/build/border-control/border-control-dropdown/component.js.map +1 -1
  10. package/build/border-control/styles.js +13 -13
  11. package/build/border-control/styles.js.map +1 -1
  12. package/build/border-control/types.js.map +1 -1
  13. package/build/box-control/index.js +4 -4
  14. package/build/box-control/index.js.map +1 -1
  15. package/build/box-control/types.js.map +1 -1
  16. package/build/composite/group-label.js +7 -1
  17. package/build/composite/group-label.js.map +1 -1
  18. package/build/composite/group.js +7 -1
  19. package/build/composite/group.js.map +1 -1
  20. package/build/composite/hover.js +8 -2
  21. package/build/composite/hover.js.map +1 -1
  22. package/build/composite/index.js +5 -1
  23. package/build/composite/index.js.map +1 -1
  24. package/build/composite/item.js +16 -1
  25. package/build/composite/item.js.map +1 -1
  26. package/build/composite/row.js +7 -1
  27. package/build/composite/row.js.map +1 -1
  28. package/build/composite/typeahead.js +8 -2
  29. package/build/composite/typeahead.js.map +1 -1
  30. package/build/date-time/date/index.js +4 -2
  31. package/build/date-time/date/index.js.map +1 -1
  32. package/build/index.js +36 -5
  33. package/build/index.js.map +1 -1
  34. package/build/navigator/index.js +128 -32
  35. package/build/navigator/index.js.map +1 -1
  36. package/build/navigator/legacy.js +179 -0
  37. package/build/navigator/legacy.js.map +1 -0
  38. package/build/navigator/{navigator-provider → navigator}/component.js +5 -40
  39. package/build/navigator/navigator/component.js.map +1 -0
  40. package/build/navigator/navigator-back-button/component.js +2 -38
  41. package/build/navigator/navigator-back-button/component.js.map +1 -1
  42. package/build/navigator/navigator-back-button/hook.js +1 -1
  43. package/build/navigator/navigator-back-button/hook.js.map +1 -1
  44. package/build/navigator/navigator-button/component.js +2 -37
  45. package/build/navigator/navigator-button/component.js.map +1 -1
  46. package/build/navigator/navigator-button/hook.js +1 -1
  47. package/build/navigator/navigator-button/hook.js.map +1 -1
  48. package/build/navigator/navigator-screen/component.js +40 -62
  49. package/build/navigator/navigator-screen/component.js.map +1 -1
  50. package/build/navigator/navigator-screen/use-screen-animate-presence.js +114 -0
  51. package/build/navigator/navigator-screen/use-screen-animate-presence.js.map +1 -0
  52. package/build/navigator/navigator-to-parent-button/component.js +3 -7
  53. package/build/navigator/navigator-to-parent-button/component.js.map +1 -1
  54. package/build/navigator/styles.js +78 -35
  55. package/build/navigator/styles.js.map +1 -1
  56. package/build/navigator/types.js.map +1 -1
  57. package/build/navigator/use-navigator.js +4 -1
  58. package/build/navigator/use-navigator.js.map +1 -1
  59. package/build/search-control/index.js +5 -2
  60. package/build/search-control/index.js.map +1 -1
  61. package/build/tabs/styles.js +3 -3
  62. package/build/tabs/styles.js.map +1 -1
  63. package/build/tabs/tablist.js +61 -28
  64. package/build/tabs/tablist.js.map +1 -1
  65. package/build/tabs/use-track-overflow.js +73 -0
  66. package/build/tabs/use-track-overflow.js.map +1 -0
  67. package/build/toggle-group-control/toggle-group-control/as-button-group.js +4 -2
  68. package/build/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  69. package/build/toggle-group-control/toggle-group-control/as-radio-group.js +6 -2
  70. package/build/toggle-group-control/toggle-group-control/as-radio-group.js.map +1 -1
  71. package/build/toggle-group-control/toggle-group-control/component.js +73 -8
  72. package/build/toggle-group-control/toggle-group-control/component.js.map +1 -1
  73. package/build/toggle-group-control/toggle-group-control/styles.js +7 -7
  74. package/build/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  75. package/build/toggle-group-control/toggle-group-control-option-base/component.js +10 -19
  76. package/build/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  77. package/build/toggle-group-control/toggle-group-control-option-base/styles.js +8 -9
  78. package/build/toggle-group-control/toggle-group-control-option-base/styles.js.map +1 -1
  79. package/build/toggle-group-control/types.js.map +1 -1
  80. package/build/utils/element-rect.js +22 -13
  81. package/build/utils/element-rect.js.map +1 -1
  82. package/build/utils/hooks/use-on-value-update.js +3 -7
  83. package/build/utils/hooks/use-on-value-update.js.map +1 -1
  84. package/build-module/autocomplete/index.js +4 -1
  85. package/build-module/autocomplete/index.js.map +1 -1
  86. package/build-module/border-box-control/border-box-control/component.js +3 -14
  87. package/build-module/border-box-control/border-box-control/component.js.map +1 -1
  88. package/build-module/border-control/border-control/component.js +1 -2
  89. package/build-module/border-control/border-control/component.js.map +1 -1
  90. package/build-module/border-control/border-control-dropdown/component.js +2 -14
  91. package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
  92. package/build-module/border-control/styles.js +13 -13
  93. package/build-module/border-control/styles.js.map +1 -1
  94. package/build-module/border-control/types.js.map +1 -1
  95. package/build-module/box-control/index.js +4 -4
  96. package/build-module/box-control/index.js.map +1 -1
  97. package/build-module/box-control/types.js.map +1 -1
  98. package/build-module/composite/group-label.js +7 -1
  99. package/build-module/composite/group-label.js.map +1 -1
  100. package/build-module/composite/group.js +7 -1
  101. package/build-module/composite/group.js.map +1 -1
  102. package/build-module/composite/hover.js +8 -2
  103. package/build-module/composite/hover.js.map +1 -1
  104. package/build-module/composite/index.js +5 -1
  105. package/build-module/composite/index.js.map +1 -1
  106. package/build-module/composite/item.js +16 -1
  107. package/build-module/composite/item.js.map +1 -1
  108. package/build-module/composite/row.js +7 -1
  109. package/build-module/composite/row.js.map +1 -1
  110. package/build-module/composite/typeahead.js +8 -2
  111. package/build-module/composite/typeahead.js.map +1 -1
  112. package/build-module/date-time/date/index.js +4 -2
  113. package/build-module/date-time/date/index.js.map +1 -1
  114. package/build-module/index.js +14 -4
  115. package/build-module/index.js.map +1 -1
  116. package/build-module/navigator/index.js +130 -5
  117. package/build-module/navigator/index.js.map +1 -1
  118. package/build-module/navigator/legacy.js +167 -0
  119. package/build-module/navigator/legacy.js.map +1 -0
  120. package/build-module/navigator/{navigator-provider → navigator}/component.js +4 -39
  121. package/build-module/navigator/navigator/component.js.map +1 -0
  122. package/build-module/navigator/navigator-back-button/component.js +1 -37
  123. package/build-module/navigator/navigator-back-button/component.js.map +1 -1
  124. package/build-module/navigator/navigator-back-button/hook.js +1 -1
  125. package/build-module/navigator/navigator-back-button/hook.js.map +1 -1
  126. package/build-module/navigator/navigator-button/component.js +1 -36
  127. package/build-module/navigator/navigator-button/component.js.map +1 -1
  128. package/build-module/navigator/navigator-button/hook.js +1 -1
  129. package/build-module/navigator/navigator-button/hook.js.map +1 -1
  130. package/build-module/navigator/navigator-screen/component.js +39 -61
  131. package/build-module/navigator/navigator-screen/component.js.map +1 -1
  132. package/build-module/navigator/navigator-screen/use-screen-animate-presence.js +106 -0
  133. package/build-module/navigator/navigator-screen/use-screen-animate-presence.js.map +1 -0
  134. package/build-module/navigator/navigator-to-parent-button/component.js +2 -6
  135. package/build-module/navigator/navigator-to-parent-button/component.js.map +1 -1
  136. package/build-module/navigator/styles.js +77 -33
  137. package/build-module/navigator/styles.js.map +1 -1
  138. package/build-module/navigator/types.js.map +1 -1
  139. package/build-module/navigator/use-navigator.js +4 -1
  140. package/build-module/navigator/use-navigator.js.map +1 -1
  141. package/build-module/search-control/index.js +5 -2
  142. package/build-module/search-control/index.js.map +1 -1
  143. package/build-module/tabs/styles.js +3 -3
  144. package/build-module/tabs/styles.js.map +1 -1
  145. package/build-module/tabs/tablist.js +62 -29
  146. package/build-module/tabs/tablist.js.map +1 -1
  147. package/build-module/tabs/use-track-overflow.js +67 -0
  148. package/build-module/tabs/use-track-overflow.js.map +1 -0
  149. package/build-module/toggle-group-control/toggle-group-control/as-button-group.js +4 -2
  150. package/build-module/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  151. package/build-module/toggle-group-control/toggle-group-control/as-radio-group.js +6 -2
  152. package/build-module/toggle-group-control/toggle-group-control/as-radio-group.js.map +1 -1
  153. package/build-module/toggle-group-control/toggle-group-control/component.js +76 -10
  154. package/build-module/toggle-group-control/toggle-group-control/component.js.map +1 -1
  155. package/build-module/toggle-group-control/toggle-group-control/styles.js +7 -7
  156. package/build-module/toggle-group-control/toggle-group-control/styles.js.map +1 -1
  157. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js +12 -22
  158. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  159. package/build-module/toggle-group-control/toggle-group-control-option-base/styles.js +7 -8
  160. package/build-module/toggle-group-control/toggle-group-control-option-base/styles.js.map +1 -1
  161. package/build-module/toggle-group-control/types.js.map +1 -1
  162. package/build-module/utils/element-rect.js +22 -12
  163. package/build-module/utils/element-rect.js.map +1 -1
  164. package/build-module/utils/hooks/use-on-value-update.js +3 -6
  165. package/build-module/utils/hooks/use-on-value-update.js.map +1 -1
  166. package/build-style/style-rtl.css +26 -13
  167. package/build-style/style.css +26 -13
  168. package/build-types/autocomplete/index.d.ts.map +1 -1
  169. package/build-types/border-box-control/border-box-control/component.d.ts +5 -15
  170. package/build-types/border-box-control/border-box-control/component.d.ts.map +1 -1
  171. package/build-types/border-box-control/border-box-control-split-controls/component.d.ts +2 -1
  172. package/build-types/border-box-control/border-box-control-split-controls/component.d.ts.map +1 -1
  173. package/build-types/border-box-control/stories/index.story.d.ts +3 -2
  174. package/build-types/border-box-control/stories/index.story.d.ts.map +1 -1
  175. package/build-types/border-control/border-control/component.d.ts +3 -2
  176. package/build-types/border-control/border-control/component.d.ts.map +1 -1
  177. package/build-types/border-control/border-control-dropdown/component.d.ts +2 -1
  178. package/build-types/border-control/border-control-dropdown/component.d.ts.map +1 -1
  179. package/build-types/border-control/border-control-style-picker/component.d.ts.map +1 -1
  180. package/build-types/border-control/stories/index.story.d.ts +15 -30
  181. package/build-types/border-control/stories/index.story.d.ts.map +1 -1
  182. package/build-types/border-control/styles.d.ts.map +1 -1
  183. package/build-types/border-control/types.d.ts +12 -7
  184. package/build-types/border-control/types.d.ts.map +1 -1
  185. package/build-types/box-control/index.d.ts +4 -4
  186. package/build-types/box-control/stories/index.story.d.ts.map +1 -1
  187. package/build-types/box-control/types.d.ts +3 -3
  188. package/build-types/composite/group-label.d.ts.map +1 -1
  189. package/build-types/composite/index.d.ts.map +1 -1
  190. package/build-types/composite/item.d.ts.map +1 -1
  191. package/build-types/composite/stories/index.story.d.ts +22 -0
  192. package/build-types/composite/stories/index.story.d.ts.map +1 -1
  193. package/build-types/composite/test/index.d.ts +2 -0
  194. package/build-types/composite/test/index.d.ts.map +1 -0
  195. package/build-types/date-time/date/index.d.ts.map +1 -1
  196. package/build-types/dropdown/stories/index.story.d.ts.map +1 -1
  197. package/build-types/dropdown-menu/stories/index.story.d.ts.map +1 -1
  198. package/build-types/index.d.ts +23 -4
  199. package/build-types/index.d.ts.map +1 -1
  200. package/build-types/navigator/index.d.ts +171 -5
  201. package/build-types/navigator/index.d.ts.map +1 -1
  202. package/build-types/navigator/legacy.d.ts +226 -0
  203. package/build-types/navigator/legacy.d.ts.map +1 -0
  204. package/build-types/navigator/navigator/component.d.ts +3 -0
  205. package/build-types/navigator/navigator/component.d.ts.map +1 -0
  206. package/build-types/navigator/navigator-back-button/component.d.ts +0 -35
  207. package/build-types/navigator/navigator-back-button/component.d.ts.map +1 -1
  208. package/build-types/navigator/navigator-button/component.d.ts +0 -34
  209. package/build-types/navigator/navigator-button/component.d.ts.map +1 -1
  210. package/build-types/navigator/navigator-screen/component.d.ts +0 -35
  211. package/build-types/navigator/navigator-screen/component.d.ts.map +1 -1
  212. package/build-types/navigator/navigator-screen/use-screen-animate-presence.d.ts +16 -0
  213. package/build-types/navigator/navigator-screen/use-screen-animate-presence.d.ts.map +1 -0
  214. package/build-types/navigator/navigator-to-parent-button/component.d.ts +0 -4
  215. package/build-types/navigator/navigator-to-parent-button/component.d.ts.map +1 -1
  216. package/build-types/navigator/stories/index.story.d.ts +5 -5
  217. package/build-types/navigator/stories/index.story.d.ts.map +1 -1
  218. package/build-types/navigator/styles.d.ts +20 -7
  219. package/build-types/navigator/styles.d.ts.map +1 -1
  220. package/build-types/navigator/types.d.ts +19 -1
  221. package/build-types/navigator/types.d.ts.map +1 -1
  222. package/build-types/navigator/use-navigator.d.ts +4 -1
  223. package/build-types/navigator/use-navigator.d.ts.map +1 -1
  224. package/build-types/select-control/stories/index.story.d.ts +7 -0
  225. package/build-types/select-control/stories/index.story.d.ts.map +1 -1
  226. package/build-types/tabs/stories/index.story.d.ts +2 -1
  227. package/build-types/tabs/stories/index.story.d.ts.map +1 -1
  228. package/build-types/tabs/styles.d.ts.map +1 -1
  229. package/build-types/tabs/tablist.d.ts.map +1 -1
  230. package/build-types/tabs/use-track-overflow.d.ts +17 -0
  231. package/build-types/tabs/use-track-overflow.d.ts.map +1 -0
  232. package/build-types/toggle-group-control/toggle-group-control/as-button-group.d.ts +2 -2
  233. package/build-types/toggle-group-control/toggle-group-control/as-button-group.d.ts.map +1 -1
  234. package/build-types/toggle-group-control/toggle-group-control/as-radio-group.d.ts +2 -2
  235. package/build-types/toggle-group-control/toggle-group-control/as-radio-group.d.ts.map +1 -1
  236. package/build-types/toggle-group-control/toggle-group-control/component.d.ts.map +1 -1
  237. package/build-types/toggle-group-control/toggle-group-control/styles.d.ts.map +1 -1
  238. package/build-types/toggle-group-control/toggle-group-control-option-base/component.d.ts.map +1 -1
  239. package/build-types/toggle-group-control/toggle-group-control-option-base/styles.d.ts +0 -1
  240. package/build-types/toggle-group-control/toggle-group-control-option-base/styles.d.ts.map +1 -1
  241. package/build-types/toggle-group-control/types.d.ts +2 -1
  242. package/build-types/toggle-group-control/types.d.ts.map +1 -1
  243. package/build-types/utils/element-rect.d.ts +8 -0
  244. package/build-types/utils/element-rect.d.ts.map +1 -1
  245. package/build-types/utils/hooks/use-on-value-update.d.ts.map +1 -1
  246. package/package.json +19 -19
  247. package/src/autocomplete/index.tsx +4 -1
  248. package/src/border-box-control/border-box-control/README.md +22 -26
  249. package/src/border-box-control/border-box-control/component.tsx +3 -14
  250. package/src/border-box-control/stories/index.story.tsx +2 -1
  251. package/src/border-control/border-control/README.md +26 -36
  252. package/src/border-control/border-control/component.tsx +1 -2
  253. package/src/border-control/border-control-dropdown/component.tsx +1 -15
  254. package/src/border-control/stories/index.story.tsx +4 -10
  255. package/src/border-control/styles.ts +0 -1
  256. package/src/border-control/test/index.js +2 -15
  257. package/src/border-control/types.ts +12 -7
  258. package/src/box-control/README.md +9 -12
  259. package/src/box-control/index.tsx +4 -4
  260. package/src/box-control/stories/index.story.tsx +1 -1
  261. package/src/box-control/types.ts +3 -3
  262. package/src/composite/group-label.tsx +7 -5
  263. package/src/composite/group.tsx +7 -7
  264. package/src/composite/hover.tsx +7 -7
  265. package/src/composite/index.tsx +6 -1
  266. package/src/composite/item.tsx +19 -1
  267. package/src/composite/legacy/test/index.tsx +22 -21
  268. package/src/composite/row.tsx +7 -7
  269. package/src/composite/stories/index.story.tsx +42 -0
  270. package/src/composite/test/index.tsx +123 -0
  271. package/src/composite/typeahead.tsx +7 -7
  272. package/src/date-time/date/index.tsx +2 -0
  273. package/src/dropdown/stories/index.story.tsx +1 -0
  274. package/src/dropdown/style.scss +10 -13
  275. package/src/dropdown-menu/stories/index.story.tsx +3 -0
  276. package/src/index.ts +19 -1
  277. package/src/menu-group/style.scss +4 -1
  278. package/src/menu-items-choice/style.scss +2 -0
  279. package/src/navigator/README.md +176 -0
  280. package/src/navigator/index.tsx +131 -0
  281. package/src/navigator/legacy.ts +169 -0
  282. package/src/navigator/{navigator-provider → navigator}/component.tsx +6 -44
  283. package/src/navigator/navigator-back-button/component.tsx +1 -37
  284. package/src/navigator/navigator-back-button/hook.ts +1 -1
  285. package/src/navigator/navigator-button/component.tsx +1 -36
  286. package/src/navigator/navigator-button/hook.ts +1 -1
  287. package/src/navigator/navigator-screen/component.tsx +48 -76
  288. package/src/navigator/navigator-screen/use-screen-animate-presence.ts +177 -0
  289. package/src/navigator/navigator-to-parent-button/component.tsx +2 -7
  290. package/src/navigator/stories/index.story.tsx +55 -54
  291. package/src/navigator/styles.ts +112 -41
  292. package/src/navigator/test/index.tsx +47 -47
  293. package/src/navigator/types.ts +19 -1
  294. package/src/navigator/use-navigator.ts +4 -1
  295. package/src/search-control/index.tsx +2 -2
  296. package/src/select-control/stories/index.story.tsx +14 -1
  297. package/src/tabs/stories/index.story.tsx +106 -0
  298. package/src/tabs/styles.ts +54 -20
  299. package/src/tabs/tablist.tsx +60 -26
  300. package/src/tabs/use-track-overflow.ts +76 -0
  301. package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +208 -44
  302. package/src/toggle-group-control/toggle-group-control/as-button-group.tsx +18 -10
  303. package/src/toggle-group-control/toggle-group-control/as-radio-group.tsx +19 -9
  304. package/src/toggle-group-control/toggle-group-control/component.tsx +114 -6
  305. package/src/toggle-group-control/toggle-group-control/styles.ts +41 -0
  306. package/src/toggle-group-control/toggle-group-control-option-base/component.tsx +10 -27
  307. package/src/toggle-group-control/toggle-group-control-option-base/styles.ts +0 -11
  308. package/src/toggle-group-control/types.ts +3 -1
  309. package/src/tools-panel/tools-panel/README.md +10 -10
  310. package/src/utils/element-rect.ts +32 -15
  311. package/src/utils/hooks/use-on-value-update.ts +3 -6
  312. package/tsconfig.tsbuildinfo +1 -1
  313. package/build/navigator/navigator-provider/component.js.map +0 -1
  314. package/build/utils/hooks/use-event.js +0 -41
  315. package/build/utils/hooks/use-event.js.map +0 -1
  316. package/build-module/navigator/navigator-provider/component.js.map +0 -1
  317. package/build-module/utils/hooks/use-event.js +0 -35
  318. package/build-module/utils/hooks/use-event.js.map +0 -1
  319. package/build-types/navigator/navigator-provider/component.d.ts +0 -37
  320. package/build-types/navigator/navigator-provider/component.d.ts.map +0 -1
  321. package/build-types/utils/hooks/use-event.d.ts +0 -20
  322. package/build-types/utils/hooks/use-event.d.ts.map +0 -1
  323. package/src/navigator/index.ts +0 -6
  324. package/src/navigator/navigator-back-button/README.md +0 -15
  325. package/src/navigator/navigator-button/README.md +0 -38
  326. package/src/navigator/navigator-provider/README.md +0 -94
  327. package/src/navigator/navigator-screen/README.md +0 -33
  328. package/src/navigator/navigator-to-parent-button/README.md +0 -17
  329. package/src/utils/hooks/use-event.ts +0 -38
@@ -15,7 +15,6 @@ import {
15
15
  useId,
16
16
  } from '@wordpress/element';
17
17
  import { useMergeRefs } from '@wordpress/compose';
18
- import { isRTL as isRTLFn } from '@wordpress/i18n';
19
18
  import { escapeAttribute } from '@wordpress/escape-html';
20
19
  import warning from '@wordpress/warning';
21
20
 
@@ -29,6 +28,7 @@ import { View } from '../../view';
29
28
  import { NavigatorContext } from '../context';
30
29
  import * as styles from '../styles';
31
30
  import type { NavigatorScreenProps } from '../types';
31
+ import { useScreenAnimatePresence } from './use-screen-animate-presence';
32
32
 
33
33
  function UnconnectedNavigatorScreen(
34
34
  props: WordPressComponentProps< NavigatorScreenProps, 'div', false >,
@@ -36,21 +36,29 @@ function UnconnectedNavigatorScreen(
36
36
  ) {
37
37
  if ( ! /^\//.test( props.path ) ) {
38
38
  warning(
39
- 'wp.components.NavigatorScreen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.'
39
+ 'wp.components.Navigator.Screen: the `path` should follow a URL-like scheme; it should start with and be separated by the `/` character.'
40
40
  );
41
41
  }
42
42
 
43
43
  const screenId = useId();
44
- const { children, className, path, ...otherProps } = useContextSystem(
45
- props,
46
- 'NavigatorScreen'
47
- );
44
+
45
+ const {
46
+ children,
47
+ className,
48
+ path,
49
+ onAnimationEnd: onAnimationEndProp,
50
+ ...otherProps
51
+ } = useContextSystem( props, 'Navigator.Screen' );
48
52
 
49
53
  const { location, match, addScreen, removeScreen } =
50
54
  useContext( NavigatorContext );
55
+ const { isInitial, isBack, focusTargetSelector, skipFocus } = location;
56
+
51
57
  const isMatch = match === screenId;
52
58
  const wrapperRef = useRef< HTMLDivElement >( null );
59
+ const skipAnimationAndFocusRestoration = !! isInitial && ! isBack;
53
60
 
61
+ // Register / unregister screen with the navigator context.
54
62
  useEffect( () => {
55
63
  const screen = {
56
64
  id: screenId,
@@ -60,31 +68,28 @@ function UnconnectedNavigatorScreen(
60
68
  return () => removeScreen( screen );
61
69
  }, [ screenId, path, addScreen, removeScreen ] );
62
70
 
63
- const isRTL = isRTLFn();
64
- const { isInitial, isBack } = location;
71
+ // Animation.
72
+ const { animationStyles, shouldRenderScreen, screenProps } =
73
+ useScreenAnimatePresence( {
74
+ isMatch,
75
+ isBack,
76
+ onAnimationEnd: onAnimationEndProp,
77
+ skipAnimation: skipAnimationAndFocusRestoration,
78
+ } );
79
+
65
80
  const cx = useCx();
66
81
  const classes = useMemo(
67
- () =>
68
- cx(
69
- styles.navigatorScreen( {
70
- isInitial,
71
- isBack,
72
- isRTL,
73
- } ),
74
- className
75
- ),
76
- [ className, cx, isInitial, isBack, isRTL ]
82
+ () => cx( styles.navigatorScreen, animationStyles, className ),
83
+ [ className, cx, animationStyles ]
77
84
  );
78
85
 
86
+ // Focus restoration
79
87
  const locationRef = useRef( location );
80
-
81
88
  useEffect( () => {
82
89
  locationRef.current = location;
83
90
  }, [ location ] );
84
-
85
- // Focus restoration
86
- const isInitialLocation = location.isInitial && ! location.isBack;
87
91
  useEffect( () => {
92
+ const wrapperEl = wrapperRef.current;
88
93
  // Only attempt to restore focus:
89
94
  // - if the current location is not the initial one (to avoid moving focus on page load)
90
95
  // - when the screen becomes visible
@@ -92,20 +97,20 @@ function UnconnectedNavigatorScreen(
92
97
  // - if focus hasn't already been restored for the current location
93
98
  // - if the `skipFocus` option is not set to `true`. This is useful when we trigger the navigation outside of NavigatorScreen.
94
99
  if (
95
- isInitialLocation ||
100
+ skipAnimationAndFocusRestoration ||
96
101
  ! isMatch ||
97
- ! wrapperRef.current ||
102
+ ! wrapperEl ||
98
103
  locationRef.current.hasRestoredFocus ||
99
- location.skipFocus
104
+ skipFocus
100
105
  ) {
101
106
  return;
102
107
  }
103
108
 
104
- const activeElement = wrapperRef.current.ownerDocument.activeElement;
109
+ const activeElement = wrapperEl.ownerDocument.activeElement;
105
110
 
106
111
  // If an element is already focused within the wrapper do not focus the
107
112
  // element. This prevents inputs or buttons from losing focus unnecessarily.
108
- if ( wrapperRef.current.contains( activeElement ) ) {
113
+ if ( wrapperEl.contains( activeElement ) ) {
109
114
  return;
110
115
  }
111
116
 
@@ -113,75 +118,42 @@ function UnconnectedNavigatorScreen(
113
118
 
114
119
  // When navigating back, if a selector is provided, use it to look for the
115
120
  // target element (assumed to be a node inside the current NavigatorScreen)
116
- if ( location.isBack && location.focusTargetSelector ) {
117
- elementToFocus = wrapperRef.current.querySelector(
118
- location.focusTargetSelector
119
- );
121
+ if ( isBack && focusTargetSelector ) {
122
+ elementToFocus = wrapperEl.querySelector( focusTargetSelector );
120
123
  }
121
124
 
122
125
  // If the previous query didn't run or find any element to focus, fallback
123
126
  // to the first tabbable element in the screen (or the screen itself).
124
127
  if ( ! elementToFocus ) {
125
- const [ firstTabbable ] = focus.tabbable.find( wrapperRef.current );
126
- elementToFocus = firstTabbable ?? wrapperRef.current;
128
+ const [ firstTabbable ] = focus.tabbable.find( wrapperEl );
129
+ elementToFocus = firstTabbable ?? wrapperEl;
127
130
  }
128
131
 
129
132
  locationRef.current.hasRestoredFocus = true;
130
133
  elementToFocus.focus();
131
134
  }, [
132
- isInitialLocation,
135
+ skipAnimationAndFocusRestoration,
133
136
  isMatch,
134
- location.isBack,
135
- location.focusTargetSelector,
136
- location.skipFocus,
137
+ isBack,
138
+ focusTargetSelector,
139
+ skipFocus,
137
140
  ] );
138
141
 
139
142
  const mergedWrapperRef = useMergeRefs( [ forwardedRef, wrapperRef ] );
140
143
 
141
- return isMatch ? (
142
- <View ref={ mergedWrapperRef } className={ classes } { ...otherProps }>
144
+ return shouldRenderScreen ? (
145
+ <View
146
+ ref={ mergedWrapperRef }
147
+ className={ classes }
148
+ { ...screenProps }
149
+ { ...otherProps }
150
+ >
143
151
  { children }
144
152
  </View>
145
153
  ) : null;
146
154
  }
147
155
 
148
- /**
149
- * The `NavigatorScreen` component represents a single view/screen/panel and
150
- * should be used in combination with the `NavigatorProvider`, the
151
- * `NavigatorButton` and the `NavigatorBackButton` components (or the `useNavigator`
152
- * hook).
153
- *
154
- * @example
155
- * ```jsx
156
- * import {
157
- * __experimentalNavigatorProvider as NavigatorProvider,
158
- * __experimentalNavigatorScreen as NavigatorScreen,
159
- * __experimentalNavigatorButton as NavigatorButton,
160
- * __experimentalNavigatorBackButton as NavigatorBackButton,
161
- * } from '@wordpress/components';
162
- *
163
- * const MyNavigation = () => (
164
- * <NavigatorProvider initialPath="/">
165
- * <NavigatorScreen path="/">
166
- * <p>This is the home screen.</p>
167
- * <NavigatorButton path="/child">
168
- * Navigate to child screen.
169
- * </NavigatorButton>
170
- * </NavigatorScreen>
171
- *
172
- * <NavigatorScreen path="/child">
173
- * <p>This is the child screen.</p>
174
- * <NavigatorBackButton>
175
- * Go back
176
- * </NavigatorBackButton>
177
- * </NavigatorScreen>
178
- * </NavigatorProvider>
179
- * );
180
- * ```
181
- */
182
156
  export const NavigatorScreen = contextConnect(
183
157
  UnconnectedNavigatorScreen,
184
- 'NavigatorScreen'
158
+ 'Navigator.Screen'
185
159
  );
186
-
187
- export default NavigatorScreen;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ useState,
6
+ useEffect,
7
+ useLayoutEffect,
8
+ useCallback,
9
+ } from '@wordpress/element';
10
+ import { useReducedMotion } from '@wordpress/compose';
11
+ import { isRTL as isRTLFn } from '@wordpress/i18n';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import * as styles from '../styles';
17
+
18
+ // Possible values:
19
+ // - 'INITIAL': the initial state
20
+ // - 'ANIMATING_IN': start enter animation
21
+ // - 'IN': enter animation has ended
22
+ // - 'ANIMATING_OUT': start exit animation
23
+ // - 'OUT': the exit animation has ended
24
+ type AnimationStatus =
25
+ | 'INITIAL'
26
+ | 'ANIMATING_IN'
27
+ | 'IN'
28
+ | 'ANIMATING_OUT'
29
+ | 'OUT';
30
+
31
+ // Allow an extra 20% of the total animation duration to account for potential
32
+ // event loop delays.
33
+ const ANIMATION_TIMEOUT_MARGIN = 1.2;
34
+
35
+ const isEnterAnimation = (
36
+ animationDirection: 'end' | 'start',
37
+ animationStatus: AnimationStatus,
38
+ animationName: string
39
+ ) =>
40
+ animationStatus === 'ANIMATING_IN' &&
41
+ animationName === styles.ANIMATION_END_NAMES[ animationDirection ].in;
42
+
43
+ const isExitAnimation = (
44
+ animationDirection: 'end' | 'start',
45
+ animationStatus: AnimationStatus,
46
+ animationName: string
47
+ ) =>
48
+ animationStatus === 'ANIMATING_OUT' &&
49
+ animationName === styles.ANIMATION_END_NAMES[ animationDirection ].out;
50
+
51
+ export function useScreenAnimatePresence( {
52
+ isMatch,
53
+ skipAnimation,
54
+ isBack,
55
+ onAnimationEnd,
56
+ }: {
57
+ isMatch: boolean;
58
+ skipAnimation: boolean;
59
+ isBack?: boolean;
60
+ onAnimationEnd?: React.AnimationEventHandler< Element >;
61
+ } ) {
62
+ const isRTL = isRTLFn();
63
+ const prefersReducedMotion = useReducedMotion();
64
+
65
+ const [ animationStatus, setAnimationStatus ] =
66
+ useState< AnimationStatus >( 'INITIAL' );
67
+
68
+ // Start enter and exit animations when the screen is selected or deselected.
69
+ // The animation status is set to `IN` or `OUT` immediately if the animation
70
+ // should be skipped.
71
+ const becameSelected =
72
+ animationStatus !== 'ANIMATING_IN' &&
73
+ animationStatus !== 'IN' &&
74
+ isMatch;
75
+ const becameUnselected =
76
+ animationStatus !== 'ANIMATING_OUT' &&
77
+ animationStatus !== 'OUT' &&
78
+ ! isMatch;
79
+ useLayoutEffect( () => {
80
+ if ( becameSelected ) {
81
+ setAnimationStatus(
82
+ skipAnimation || prefersReducedMotion ? 'IN' : 'ANIMATING_IN'
83
+ );
84
+ } else if ( becameUnselected ) {
85
+ setAnimationStatus(
86
+ skipAnimation || prefersReducedMotion ? 'OUT' : 'ANIMATING_OUT'
87
+ );
88
+ }
89
+ }, [
90
+ becameSelected,
91
+ becameUnselected,
92
+ skipAnimation,
93
+ prefersReducedMotion,
94
+ ] );
95
+
96
+ // Animation attributes (derived state).
97
+ const animationDirection =
98
+ ( isRTL && isBack ) || ( ! isRTL && ! isBack ) ? 'end' : 'start';
99
+ const isAnimatingIn = animationStatus === 'ANIMATING_IN';
100
+ const isAnimatingOut = animationStatus === 'ANIMATING_OUT';
101
+ let animationType: 'in' | 'out' | undefined;
102
+ if ( isAnimatingIn ) {
103
+ animationType = 'in';
104
+ } else if ( isAnimatingOut ) {
105
+ animationType = 'out';
106
+ }
107
+
108
+ const onScreenAnimationEnd = useCallback(
109
+ ( e: React.AnimationEvent< HTMLElement > ) => {
110
+ onAnimationEnd?.( e );
111
+
112
+ if (
113
+ isExitAnimation(
114
+ animationDirection,
115
+ animationStatus,
116
+ e.animationName
117
+ )
118
+ ) {
119
+ // When the exit animation ends on an unselected screen, set the
120
+ // status to 'OUT' to remove the screen contents from the DOM.
121
+ setAnimationStatus( 'OUT' );
122
+ } else if (
123
+ isEnterAnimation(
124
+ animationDirection,
125
+ animationStatus,
126
+ e.animationName
127
+ )
128
+ ) {
129
+ // When the enter animation ends on a selected screen, set the
130
+ // status to 'IN' to ensure the screen is rendered in the DOM.
131
+ setAnimationStatus( 'IN' );
132
+ }
133
+ },
134
+ [ onAnimationEnd, animationStatus, animationDirection ]
135
+ );
136
+
137
+ // Fallback timeout to ensure that the logic is applied even if the
138
+ // `animationend` event is not triggered.
139
+ useEffect( () => {
140
+ let animationTimeout: number | undefined;
141
+
142
+ if ( isAnimatingOut ) {
143
+ animationTimeout = window.setTimeout( () => {
144
+ setAnimationStatus( 'OUT' );
145
+ animationTimeout = undefined;
146
+ }, styles.TOTAL_ANIMATION_DURATION.OUT * ANIMATION_TIMEOUT_MARGIN );
147
+ } else if ( isAnimatingIn ) {
148
+ animationTimeout = window.setTimeout( () => {
149
+ setAnimationStatus( 'IN' );
150
+ animationTimeout = undefined;
151
+ }, styles.TOTAL_ANIMATION_DURATION.IN * ANIMATION_TIMEOUT_MARGIN );
152
+ }
153
+
154
+ return () => {
155
+ if ( animationTimeout ) {
156
+ window.clearTimeout( animationTimeout );
157
+ animationTimeout = undefined;
158
+ }
159
+ };
160
+ }, [ isAnimatingOut, isAnimatingIn ] );
161
+
162
+ return {
163
+ animationStyles: styles.navigatorScreenAnimation,
164
+ // Render the screen's contents in the DOM not only when the screen is
165
+ // selected, but also while it is animating out.
166
+ shouldRenderScreen:
167
+ isMatch ||
168
+ animationStatus === 'IN' ||
169
+ animationStatus === 'ANIMATING_OUT',
170
+ screenProps: {
171
+ onAnimationEnd: onScreenAnimationEnd,
172
+ 'data-animation-direction': animationDirection,
173
+ 'data-animation-type': animationType,
174
+ 'data-skip-animation': skipAnimation || undefined,
175
+ },
176
+ } as const;
177
+ }
@@ -17,21 +17,16 @@ function UnconnectedNavigatorToParentButton(
17
17
  ) {
18
18
  deprecated( 'wp.components.NavigatorToParentButton', {
19
19
  since: '6.7',
20
- alternative: 'wp.components.NavigatorBackButton',
20
+ alternative: 'wp.components.Navigator.BackButton',
21
21
  } );
22
22
 
23
23
  return <NavigatorBackButton ref={ forwardedRef } { ...props } />;
24
24
  }
25
25
 
26
26
  /**
27
- * _Note: this component is deprecated. Please use the `NavigatorBackButton`
28
- * component instead._
29
- *
30
27
  * @deprecated
31
28
  */
32
29
  export const NavigatorToParentButton = contextConnect(
33
30
  UnconnectedNavigatorToParentButton,
34
- 'NavigatorToParentButton'
31
+ 'Navigator.ToParentButton'
35
32
  );
36
-
37
- export default NavigatorToParentButton;
@@ -8,20 +8,20 @@ import type { Meta, StoryObj } from '@storybook/react';
8
8
  */
9
9
  import Button from '../../button';
10
10
  import { VStack } from '../../v-stack';
11
- import {
12
- NavigatorProvider,
13
- NavigatorScreen,
14
- NavigatorButton,
15
- NavigatorBackButton,
16
- useNavigator,
17
- } from '..';
18
11
  import { HStack } from '../../h-stack';
19
-
20
- const meta: Meta< typeof NavigatorProvider > = {
21
- component: NavigatorProvider,
22
- // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
23
- subcomponents: { NavigatorScreen, NavigatorButton, NavigatorBackButton },
24
- title: 'Components (Experimental)/Navigator',
12
+ import { Navigator, useNavigator } from '../';
13
+
14
+ const meta: Meta< typeof Navigator > = {
15
+ component: Navigator,
16
+ subcomponents: {
17
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
18
+ Screen: Navigator.Screen,
19
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
20
+ Button: Navigator.Button,
21
+ // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
22
+ BackButton: Navigator.BackButton,
23
+ },
24
+ title: 'Components/Navigator',
25
25
  argTypes: {
26
26
  as: { control: { type: null } },
27
27
  children: { control: { type: null } },
@@ -36,14 +36,14 @@ const meta: Meta< typeof NavigatorProvider > = {
36
36
  return (
37
37
  <>
38
38
  <style>{ `
39
- /* These attributes are a private implementation detail of the
40
- Navigator component. Do not use outside of its source code. */
41
- [data-wp-component="NavigatorProvider"] {
42
- height: calc(100vh - 2rem);
43
- max-height: 250px;
44
-
39
+ /* The data-wp-component attribute is a private implementation
40
+ * detail of the Navigator component. Do not use outside of
41
+ * its source code.
42
+ */
43
+ [data-wp-component="Navigator"] {
44
+ height: 250px;
45
45
  }
46
- [data-wp-component="NavigatorScreen"]:not([data-sticky]) {
46
+ [data-wp-component="Navigator.Screen"] {
47
47
  padding: 8px;
48
48
  }
49
49
  ` }</style>
@@ -55,55 +55,55 @@ const meta: Meta< typeof NavigatorProvider > = {
55
55
  };
56
56
  export default meta;
57
57
 
58
- export const Default: StoryObj< typeof NavigatorProvider > = {
58
+ export const Default: StoryObj< typeof Navigator > = {
59
59
  args: {
60
60
  initialPath: '/',
61
61
  children: (
62
62
  <>
63
- <NavigatorScreen path="/">
63
+ <Navigator.Screen path="/">
64
64
  <h2>This is the home screen.</h2>
65
65
 
66
66
  <VStack alignment="left">
67
- <NavigatorButton variant="primary" path="/child">
67
+ <Navigator.Button variant="primary" path="/child">
68
68
  Go to child screen.
69
- </NavigatorButton>
69
+ </Navigator.Button>
70
70
 
71
- <NavigatorButton variant="primary" path="/product/1">
71
+ <Navigator.Button variant="primary" path="/product/1">
72
72
  Go to dynamic path screen with id 1.
73
- </NavigatorButton>
73
+ </Navigator.Button>
74
74
 
75
- <NavigatorButton variant="primary" path="/product/2">
75
+ <Navigator.Button variant="primary" path="/product/2">
76
76
  Go to dynamic path screen with id 2.
77
- </NavigatorButton>
77
+ </Navigator.Button>
78
78
  </VStack>
79
- </NavigatorScreen>
79
+ </Navigator.Screen>
80
80
 
81
- <NavigatorScreen path="/child">
81
+ <Navigator.Screen path="/child">
82
82
  <h2>This is the child screen.</h2>
83
83
  <HStack spacing={ 2 } alignment="left">
84
- <NavigatorBackButton variant="secondary">
84
+ <Navigator.BackButton variant="secondary">
85
85
  Go back
86
- </NavigatorBackButton>
86
+ </Navigator.BackButton>
87
87
 
88
- <NavigatorButton
88
+ <Navigator.Button
89
89
  variant="primary"
90
90
  path="/child/grandchild"
91
91
  >
92
92
  Go to grand child screen.
93
- </NavigatorButton>
93
+ </Navigator.Button>
94
94
  </HStack>
95
- </NavigatorScreen>
95
+ </Navigator.Screen>
96
96
 
97
- <NavigatorScreen path="/child/grandchild">
97
+ <Navigator.Screen path="/child/grandchild">
98
98
  <h2>This is the grand child screen.</h2>
99
- <NavigatorBackButton variant="secondary">
99
+ <Navigator.BackButton variant="secondary">
100
100
  Go back
101
- </NavigatorBackButton>
102
- </NavigatorScreen>
101
+ </Navigator.BackButton>
102
+ </Navigator.Screen>
103
103
 
104
- <NavigatorScreen path="/product/:id">
104
+ <Navigator.Screen path="/product/:id">
105
105
  <DynamicScreen />
106
- </NavigatorScreen>
106
+ </Navigator.Screen>
107
107
  </>
108
108
  ),
109
109
  },
@@ -119,14 +119,14 @@ function DynamicScreen() {
119
119
  This screen can parse params dynamically. The current id is:{ ' ' }
120
120
  { params.id }
121
121
  </p>
122
- <NavigatorBackButton variant="secondary">
122
+ <Navigator.BackButton variant="secondary">
123
123
  Go back
124
- </NavigatorBackButton>
124
+ </Navigator.BackButton>
125
125
  </>
126
126
  );
127
127
  }
128
128
 
129
- export const WithNestedInitialPath: StoryObj< typeof NavigatorProvider > = {
129
+ export const WithNestedInitialPath: StoryObj< typeof Navigator > = {
130
130
  ...Default,
131
131
  args: {
132
132
  ...Default.args,
@@ -138,7 +138,7 @@ const NavigatorButtonWithSkipFocus = ( {
138
138
  path,
139
139
  onClick,
140
140
  ...props
141
- }: React.ComponentProps< typeof NavigatorButton > ) => {
141
+ }: React.ComponentProps< typeof Navigator.Button > ) => {
142
142
  const { goTo } = useNavigator();
143
143
 
144
144
  return (
@@ -156,7 +156,7 @@ const NavigatorButtonWithSkipFocus = ( {
156
156
  );
157
157
  };
158
158
 
159
- export const SkipFocus: StoryObj< typeof NavigatorProvider > = {
159
+ export const SkipFocus: StoryObj< typeof Navigator > = {
160
160
  args: {
161
161
  initialPath: '/',
162
162
  children: (
@@ -167,21 +167,22 @@ export const SkipFocus: StoryObj< typeof NavigatorProvider > = {
167
167
  outline: '1px solid black',
168
168
  outlineOffset: '-1px',
169
169
  marginBlockEnd: '1rem',
170
+ display: 'contents',
170
171
  } }
171
172
  >
172
- <NavigatorScreen path="/">
173
+ <Navigator.Screen path="/">
173
174
  <h2>Home screen</h2>
174
- <NavigatorButton variant="primary" path="/child">
175
+ <Navigator.Button variant="primary" path="/child">
175
176
  Go to child screen.
176
- </NavigatorButton>
177
- </NavigatorScreen>
177
+ </Navigator.Button>
178
+ </Navigator.Screen>
178
179
 
179
- <NavigatorScreen path="/child">
180
+ <Navigator.Screen path="/child">
180
181
  <h2>Child screen</h2>
181
- <NavigatorBackButton variant="secondary">
182
+ <Navigator.BackButton variant="secondary">
182
183
  Go back to home screen
183
- </NavigatorBackButton>
184
- </NavigatorScreen>
184
+ </Navigator.BackButton>
185
+ </Navigator.Screen>
185
186
  </div>
186
187
 
187
188
  <NavigatorButtonWithSkipFocus path="/child">