@wordpress/components 25.9.1 → 25.10.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 (308) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/build/alignment-matrix-control/cell.js +8 -5
  3. package/build/alignment-matrix-control/cell.js.map +1 -1
  4. package/build/alignment-matrix-control/index.js +27 -43
  5. package/build/alignment-matrix-control/index.js.map +1 -1
  6. package/build/alignment-matrix-control/utils.js +29 -9
  7. package/build/alignment-matrix-control/utils.js.map +1 -1
  8. package/build/circular-option-picker/circular-option-picker-option.js +20 -39
  9. package/build/circular-option-picker/circular-option-picker-option.js.map +1 -1
  10. package/build/circular-option-picker/circular-option-picker.js +11 -32
  11. package/build/circular-option-picker/circular-option-picker.js.map +1 -1
  12. package/build/circular-option-picker/types.js.map +1 -1
  13. package/build/color-palette/index.js +7 -2
  14. package/build/color-palette/index.js.map +1 -1
  15. package/build/color-picker/component.js +12 -2
  16. package/build/color-picker/component.js.map +1 -1
  17. package/build/color-picker/picker.js +77 -1
  18. package/build/color-picker/picker.js.map +1 -1
  19. package/build/color-picker/styles.js +8 -8
  20. package/build/color-picker/styles.js.map +1 -1
  21. package/build/color-picker/types.js.map +1 -1
  22. package/build/composite/v2.js +43 -0
  23. package/build/composite/v2.js.map +1 -0
  24. package/build/confirm-dialog/component.js +74 -8
  25. package/build/confirm-dialog/component.js.map +1 -1
  26. package/build/confirm-dialog/types.js.map +1 -1
  27. package/build/custom-gradient-picker/gradient-bar/control-points.js +13 -4
  28. package/build/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  29. package/build/font-size-picker/utils.js +1 -1
  30. package/build/font-size-picker/utils.js.map +1 -1
  31. package/build/modal/index.js +45 -16
  32. package/build/modal/index.js.map +1 -1
  33. package/build/palette-edit/index.js +4 -0
  34. package/build/palette-edit/index.js.map +1 -1
  35. package/build/popover/index.js +34 -6
  36. package/build/popover/index.js.map +1 -1
  37. package/build/private-apis.js +9 -1
  38. package/build/private-apis.js.map +1 -1
  39. package/build/progress-bar/styles.js +5 -5
  40. package/build/progress-bar/styles.js.map +1 -1
  41. package/build/sandbox/index.js +1 -1
  42. package/build/sandbox/index.js.map +1 -1
  43. package/build/sandbox/index.native.js +1 -1
  44. package/build/sandbox/index.native.js.map +1 -1
  45. package/build/tabs/context.js +16 -0
  46. package/build/tabs/context.js.map +1 -0
  47. package/build/tabs/index.js +147 -0
  48. package/build/tabs/index.js.map +1 -0
  49. package/build/tabs/styles.js +38 -0
  50. package/build/tabs/styles.js.map +1 -0
  51. package/build/tabs/tab.js +46 -0
  52. package/build/tabs/tab.js.map +1 -0
  53. package/build/tabs/tablist.js +47 -0
  54. package/build/tabs/tablist.js.map +1 -0
  55. package/build/tabs/tabpanel.js +48 -0
  56. package/build/tabs/tabpanel.js.map +1 -0
  57. package/build/tabs/types.js +6 -0
  58. package/build/tabs/types.js.map +1 -0
  59. package/build/text/component.js +7 -6
  60. package/build/text/component.js.map +1 -1
  61. package/build/text/hook.js +6 -15
  62. package/build/text/hook.js.map +1 -1
  63. package/build/text/index.js.map +1 -1
  64. package/build/text/styles.js +7 -7
  65. package/build/text/styles.js.map +1 -1
  66. package/build/text/types.js.map +1 -1
  67. package/build/text/utils.js +17 -33
  68. package/build/text/utils.js.map +1 -1
  69. package/build/toggle-group-control/toggle-group-control-option-base/component.js +1 -0
  70. package/build/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  71. package/build/toolbar/toolbar/index.js +17 -10
  72. package/build/toolbar/toolbar/index.js.map +1 -1
  73. package/build/toolbar/toolbar/types.js.map +1 -1
  74. package/build/tools-panel/tools-panel-item/hook.js +2 -2
  75. package/build/tools-panel/tools-panel-item/hook.js.map +1 -1
  76. package/build/tools-panel/types.js.map +1 -1
  77. package/build/unit-control/utils.js +108 -0
  78. package/build/unit-control/utils.js.map +1 -1
  79. package/build/utils/unit-values.js +1 -1
  80. package/build/utils/unit-values.js.map +1 -1
  81. package/build-module/alignment-matrix-control/cell.js +7 -4
  82. package/build-module/alignment-matrix-control/cell.js.map +1 -1
  83. package/build-module/alignment-matrix-control/index.js +27 -43
  84. package/build-module/alignment-matrix-control/index.js.map +1 -1
  85. package/build-module/alignment-matrix-control/utils.js +29 -8
  86. package/build-module/alignment-matrix-control/utils.js.map +1 -1
  87. package/build-module/circular-option-picker/circular-option-picker-option.js +20 -39
  88. package/build-module/circular-option-picker/circular-option-picker-option.js.map +1 -1
  89. package/build-module/circular-option-picker/circular-option-picker.js +10 -31
  90. package/build-module/circular-option-picker/circular-option-picker.js.map +1 -1
  91. package/build-module/circular-option-picker/types.js.map +1 -1
  92. package/build-module/color-palette/index.js +7 -2
  93. package/build-module/color-palette/index.js.map +1 -1
  94. package/build-module/color-picker/component.js +13 -3
  95. package/build-module/color-picker/component.js.map +1 -1
  96. package/build-module/color-picker/picker.js +78 -2
  97. package/build-module/color-picker/picker.js.map +1 -1
  98. package/build-module/color-picker/styles.js +8 -8
  99. package/build-module/color-picker/styles.js.map +1 -1
  100. package/build-module/color-picker/types.js.map +1 -1
  101. package/build-module/composite/v2.js +15 -0
  102. package/build-module/composite/v2.js.map +1 -0
  103. package/build-module/confirm-dialog/component.js +72 -7
  104. package/build-module/confirm-dialog/component.js.map +1 -1
  105. package/build-module/confirm-dialog/types.js.map +1 -1
  106. package/build-module/custom-gradient-picker/gradient-bar/control-points.js +13 -4
  107. package/build-module/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  108. package/build-module/font-size-picker/utils.js +1 -1
  109. package/build-module/font-size-picker/utils.js.map +1 -1
  110. package/build-module/modal/index.js +47 -18
  111. package/build-module/modal/index.js.map +1 -1
  112. package/build-module/palette-edit/index.js +4 -0
  113. package/build-module/palette-edit/index.js.map +1 -1
  114. package/build-module/popover/index.js +34 -6
  115. package/build-module/popover/index.js.map +1 -1
  116. package/build-module/private-apis.js +9 -1
  117. package/build-module/private-apis.js.map +1 -1
  118. package/build-module/progress-bar/styles.js +5 -5
  119. package/build-module/progress-bar/styles.js.map +1 -1
  120. package/build-module/sandbox/index.js +1 -1
  121. package/build-module/sandbox/index.js.map +1 -1
  122. package/build-module/sandbox/index.native.js +1 -1
  123. package/build-module/sandbox/index.native.js.map +1 -1
  124. package/build-module/tabs/context.js +12 -0
  125. package/build-module/tabs/context.js.map +1 -0
  126. package/build-module/tabs/index.js +142 -0
  127. package/build-module/tabs/index.js.map +1 -0
  128. package/build-module/tabs/styles.js +36 -0
  129. package/build-module/tabs/styles.js.map +1 -0
  130. package/build-module/tabs/tab.js +43 -0
  131. package/build-module/tabs/tab.js.map +1 -0
  132. package/build-module/tabs/tablist.js +41 -0
  133. package/build-module/tabs/tablist.js.map +1 -0
  134. package/build-module/tabs/tabpanel.js +43 -0
  135. package/build-module/tabs/tabpanel.js.map +1 -0
  136. package/build-module/tabs/types.js +2 -0
  137. package/build-module/tabs/types.js.map +1 -0
  138. package/build-module/text/component.js +6 -6
  139. package/build-module/text/component.js.map +1 -1
  140. package/build-module/text/hook.js +11 -19
  141. package/build-module/text/hook.js.map +1 -1
  142. package/build-module/text/index.js.map +1 -1
  143. package/build-module/text/styles.js +7 -7
  144. package/build-module/text/styles.js.map +1 -1
  145. package/build-module/text/types.js.map +1 -1
  146. package/build-module/text/utils.js +17 -10
  147. package/build-module/text/utils.js.map +1 -1
  148. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js +1 -0
  149. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  150. package/build-module/toolbar/toolbar/index.js +18 -11
  151. package/build-module/toolbar/toolbar/index.js.map +1 -1
  152. package/build-module/toolbar/toolbar/types.js.map +1 -1
  153. package/build-module/tools-panel/tools-panel-item/hook.js +2 -2
  154. package/build-module/tools-panel/tools-panel-item/hook.js.map +1 -1
  155. package/build-module/tools-panel/types.js.map +1 -1
  156. package/build-module/unit-control/utils.js +108 -0
  157. package/build-module/unit-control/utils.js.map +1 -1
  158. package/build-module/utils/unit-values.js +1 -1
  159. package/build-module/utils/unit-values.js.map +1 -1
  160. package/build-style/style-rtl.css +16 -4
  161. package/build-style/style.css +16 -4
  162. package/build-types/alignment-matrix-control/cell.d.ts +1 -1
  163. package/build-types/alignment-matrix-control/cell.d.ts.map +1 -1
  164. package/build-types/alignment-matrix-control/index.d.ts.map +1 -1
  165. package/build-types/alignment-matrix-control/stories/index.story.d.ts.map +1 -1
  166. package/build-types/alignment-matrix-control/utils.d.ts +9 -9
  167. package/build-types/alignment-matrix-control/utils.d.ts.map +1 -1
  168. package/build-types/circular-option-picker/circular-option-picker-option.d.ts.map +1 -1
  169. package/build-types/circular-option-picker/circular-option-picker.d.ts.map +1 -1
  170. package/build-types/circular-option-picker/types.d.ts +4 -6
  171. package/build-types/circular-option-picker/types.d.ts.map +1 -1
  172. package/build-types/color-palette/index.d.ts.map +1 -1
  173. package/build-types/color-picker/component.d.ts.map +1 -1
  174. package/build-types/color-picker/picker.d.ts +1 -1
  175. package/build-types/color-picker/picker.d.ts.map +1 -1
  176. package/build-types/color-picker/styles.d.ts.map +1 -1
  177. package/build-types/color-picker/types.d.ts +3 -0
  178. package/build-types/color-picker/types.d.ts.map +1 -1
  179. package/build-types/composite/v2.d.ts +12 -0
  180. package/build-types/composite/v2.d.ts.map +1 -0
  181. package/build-types/confirm-dialog/component.d.ts +70 -29
  182. package/build-types/confirm-dialog/component.d.ts.map +1 -1
  183. package/build-types/confirm-dialog/stories/index.story.d.ts +11 -0
  184. package/build-types/confirm-dialog/stories/index.story.d.ts.map +1 -0
  185. package/build-types/confirm-dialog/test/index.d.ts +2 -0
  186. package/build-types/confirm-dialog/test/index.d.ts.map +1 -0
  187. package/build-types/confirm-dialog/types.d.ts +32 -10
  188. package/build-types/confirm-dialog/types.d.ts.map +1 -1
  189. package/build-types/custom-gradient-picker/gradient-bar/control-points.d.ts.map +1 -1
  190. package/build-types/font-size-picker/utils.d.ts.map +1 -1
  191. package/build-types/heading/stories/index.story.d.ts.map +1 -1
  192. package/build-types/modal/index.d.ts.map +1 -1
  193. package/build-types/palette-edit/index.d.ts.map +1 -1
  194. package/build-types/popover/index.d.ts +1 -1
  195. package/build-types/popover/index.d.ts.map +1 -1
  196. package/build-types/popover/stories/e2e/index.story.d.ts +1 -1
  197. package/build-types/private-apis.d.ts.map +1 -1
  198. package/build-types/progress-bar/styles.d.ts.map +1 -1
  199. package/build-types/sandbox/index.d.ts.map +1 -1
  200. package/build-types/tabs/context.d.ts +8 -0
  201. package/build-types/tabs/context.d.ts.map +1 -0
  202. package/build-types/tabs/index.d.ts +13 -0
  203. package/build-types/tabs/index.d.ts.map +1 -0
  204. package/build-types/tabs/stories/index.story.d.ts +20 -0
  205. package/build-types/tabs/stories/index.story.d.ts.map +1 -0
  206. package/build-types/tabs/styles.d.ts +17 -0
  207. package/build-types/tabs/styles.d.ts.map +1 -0
  208. package/build-types/tabs/tab.d.ts +10 -0
  209. package/build-types/tabs/tab.d.ts.map +1 -0
  210. package/build-types/tabs/tablist.d.ts +7 -0
  211. package/build-types/tabs/tablist.d.ts.map +1 -0
  212. package/build-types/tabs/tabpanel.d.ts +7 -0
  213. package/build-types/tabs/tabpanel.d.ts.map +1 -0
  214. package/build-types/tabs/test/index.d.ts +2 -0
  215. package/build-types/tabs/test/index.d.ts.map +1 -0
  216. package/build-types/tabs/types.d.ts +134 -0
  217. package/build-types/tabs/types.d.ts.map +1 -0
  218. package/build-types/text/component.d.ts +4 -2
  219. package/build-types/text/component.d.ts.map +1 -1
  220. package/build-types/text/hook.d.ts +171 -165
  221. package/build-types/text/hook.d.ts.map +1 -1
  222. package/build-types/text/index.d.ts +2 -2
  223. package/build-types/text/index.d.ts.map +1 -1
  224. package/build-types/text/stories/index.story.d.ts +21 -0
  225. package/build-types/text/stories/index.story.d.ts.map +1 -0
  226. package/build-types/text/styles.d.ts +7 -7
  227. package/build-types/text/styles.d.ts.map +1 -1
  228. package/build-types/text/types.d.ts +1 -1
  229. package/build-types/text/types.d.ts.map +1 -1
  230. package/build-types/text/utils.d.ts +56 -61
  231. package/build-types/text/utils.d.ts.map +1 -1
  232. package/build-types/toggle-group-control/toggle-group-control-option-base/component.d.ts.map +1 -1
  233. package/build-types/toolbar/stories/index.story.d.ts +5 -0
  234. package/build-types/toolbar/stories/index.story.d.ts.map +1 -1
  235. package/build-types/toolbar/toolbar/index.d.ts.map +1 -1
  236. package/build-types/toolbar/toolbar/types.d.ts +10 -0
  237. package/build-types/toolbar/toolbar/types.d.ts.map +1 -1
  238. package/build-types/tools-panel/tools-panel-item/hook.d.ts.map +1 -1
  239. package/build-types/tools-panel/types.d.ts +2 -0
  240. package/build-types/tools-panel/types.d.ts.map +1 -1
  241. package/build-types/unit-control/utils.d.ts.map +1 -1
  242. package/package.json +19 -19
  243. package/src/alignment-matrix-control/cell.tsx +6 -2
  244. package/src/alignment-matrix-control/index.tsx +31 -54
  245. package/src/alignment-matrix-control/stories/index.story.tsx +3 -7
  246. package/src/alignment-matrix-control/test/index.tsx +117 -18
  247. package/src/alignment-matrix-control/utils.tsx +33 -9
  248. package/src/button/style.scss +1 -2
  249. package/src/circular-option-picker/circular-option-picker-option.tsx +24 -38
  250. package/src/circular-option-picker/circular-option-picker.tsx +11 -28
  251. package/src/circular-option-picker/types.ts +6 -5
  252. package/src/color-palette/index.tsx +6 -1
  253. package/src/color-picker/component.tsx +25 -3
  254. package/src/color-picker/picker.tsx +96 -2
  255. package/src/color-picker/styles.ts +0 -1
  256. package/src/color-picker/types.ts +3 -0
  257. package/src/composite/v2.ts +22 -0
  258. package/src/confirm-dialog/README.md +1 -1
  259. package/src/confirm-dialog/component.tsx +79 -13
  260. package/src/confirm-dialog/stories/{index.story.js → index.story.tsx} +26 -24
  261. package/src/confirm-dialog/test/{index.js → index.tsx} +3 -3
  262. package/src/confirm-dialog/types.ts +32 -12
  263. package/src/custom-gradient-picker/gradient-bar/control-points.tsx +32 -25
  264. package/src/font-size-picker/utils.ts +2 -1
  265. package/src/heading/stories/index.story.tsx +2 -4
  266. package/src/modal/index.tsx +58 -22
  267. package/src/modal/test/index.tsx +29 -0
  268. package/src/notice/style.scss +0 -1
  269. package/src/palette-edit/index.tsx +4 -0
  270. package/src/popover/index.tsx +99 -57
  271. package/src/popover/style.scss +9 -0
  272. package/src/private-apis.ts +15 -1
  273. package/src/progress-bar/styles.ts +19 -4
  274. package/src/sandbox/index.native.js +1 -1
  275. package/src/sandbox/index.tsx +3 -1
  276. package/src/tabs/README.md +242 -0
  277. package/src/tabs/context.ts +13 -0
  278. package/src/tabs/index.tsx +167 -0
  279. package/src/tabs/stories/index.story.tsx +352 -0
  280. package/src/tabs/styles.ts +103 -0
  281. package/src/tabs/tab.tsx +39 -0
  282. package/src/tabs/tablist.tsx +40 -0
  283. package/src/tabs/tabpanel.tsx +42 -0
  284. package/src/tabs/test/index.tsx +1124 -0
  285. package/src/tabs/types.ts +142 -0
  286. package/src/text/README.md +2 -2
  287. package/src/text/{component.js → component.tsx} +10 -6
  288. package/src/text/{hook.js → hook.ts} +12 -15
  289. package/src/text/stories/index.story.tsx +80 -0
  290. package/src/text/types.ts +1 -6
  291. package/src/text/{utils.js → utils.ts} +40 -14
  292. package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +8 -0
  293. package/src/toggle-group-control/toggle-group-control-option-base/component.tsx +1 -0
  294. package/src/toolbar/stories/index.story.tsx +15 -0
  295. package/src/toolbar/test/index.tsx +8 -0
  296. package/src/toolbar/toolbar/README.md +9 -0
  297. package/src/toolbar/toolbar/index.tsx +21 -12
  298. package/src/toolbar/toolbar/style.scss +9 -0
  299. package/src/toolbar/toolbar/types.ts +10 -0
  300. package/src/tools-panel/tools-panel/README.md +3 -0
  301. package/src/tools-panel/tools-panel-item/hook.ts +4 -6
  302. package/src/tools-panel/types.ts +2 -0
  303. package/src/unit-control/utils.ts +124 -0
  304. package/src/utils/unit-values.ts +1 -1
  305. package/tsconfig.tsbuildinfo +1 -1
  306. package/src/text/stories/index.story.js +0 -53
  307. /package/src/text/{index.js → index.ts} +0 -0
  308. /package/src/text/{styles.js → styles.ts} +0 -0
@@ -2,6 +2,7 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { __ } from '@wordpress/i18n';
5
+
5
6
  /**
6
7
  * Internal dependencies
7
8
  */
@@ -31,16 +32,24 @@ export const ALIGNMENT_LABEL: Record< AlignmentMatrixControlValue, string > = {
31
32
  export const ALIGNMENTS = GRID.flat();
32
33
 
33
34
  /**
34
- * Parses and transforms an incoming value to better match the alignment values
35
+ * Normalizes and transforms an incoming value to better match the alignment values
35
36
  *
36
37
  * @param value An alignment value to parse.
37
38
  *
38
39
  * @return The parsed value.
39
40
  */
40
- export function transformValue( value: AlignmentMatrixControlValue ) {
41
- const nextValue = value === 'center' ? 'center center' : value;
41
+ function normalize( value?: string | null ) {
42
+ const normalized = value === 'center' ? 'center center' : value;
43
+
44
+ // Strictly speaking, this could be `string | null | undefined`,
45
+ // but will be validated shortly, so we're typecasting to an
46
+ // `AlignmentMatrixControlValue` to keep TypeScript happy.
47
+ const transformed = normalized?.replace(
48
+ '-',
49
+ ' '
50
+ ) as AlignmentMatrixControlValue;
42
51
 
43
- return nextValue.replace( '-', ' ' ) as AlignmentMatrixControlValue;
52
+ return ALIGNMENTS.includes( transformed ) ? transformed : undefined;
44
53
  }
45
54
 
46
55
  /**
@@ -53,11 +62,25 @@ export function transformValue( value: AlignmentMatrixControlValue ) {
53
62
  */
54
63
  export function getItemId(
55
64
  prefixId: string,
56
- value: AlignmentMatrixControlValue
65
+ value?: AlignmentMatrixControlValue
57
66
  ) {
58
- const valueId = transformValue( value ).replace( ' ', '-' );
67
+ const normalized = normalize( value );
68
+ if ( ! normalized ) return;
69
+
70
+ const id = normalized.replace( ' ', '-' );
71
+ return `${ prefixId }-${ id }`;
72
+ }
59
73
 
60
- return `${ prefixId }-${ valueId }`;
74
+ /**
75
+ * Extracts an item value from its ID
76
+ *
77
+ * @param prefixId An ID prefix to remove
78
+ * @param id An item ID
79
+ * @return The item value
80
+ */
81
+ export function getItemValue( prefixId: string, id?: string | null ) {
82
+ const value = id?.replace( prefixId + '-', '' );
83
+ return normalize( value );
61
84
  }
62
85
 
63
86
  /**
@@ -70,8 +93,9 @@ export function getItemId(
70
93
  export function getAlignmentIndex(
71
94
  alignment: AlignmentMatrixControlValue = 'center'
72
95
  ) {
73
- const item = transformValue( alignment );
74
- const index = ALIGNMENTS.indexOf( item );
96
+ const normalized = normalize( alignment );
97
+ if ( ! normalized ) return undefined;
75
98
 
99
+ const index = ALIGNMENTS.indexOf( normalized );
76
100
  return index > -1 ? index : undefined;
77
101
  }
@@ -308,8 +308,7 @@
308
308
  }
309
309
 
310
310
  // Toggled style.
311
- &[aria-pressed="true"],
312
- &[aria-pressed="mixed"] {
311
+ &.is-pressed {
313
312
  color: $components-color-foreground-inverted;
314
313
  background: $components-color-foreground;
315
314
 
@@ -8,7 +8,7 @@ import type { ForwardedRef } from 'react';
8
8
  * WordPress dependencies
9
9
  */
10
10
  import { useInstanceId } from '@wordpress/compose';
11
- import { forwardRef, useContext, useEffect } from '@wordpress/element';
11
+ import { forwardRef, useContext } from '@wordpress/element';
12
12
  import { Icon, check } from '@wordpress/icons';
13
13
 
14
14
  /**
@@ -16,15 +16,9 @@ import { Icon, check } from '@wordpress/icons';
16
16
  */
17
17
  import { CircularOptionPickerContext } from './circular-option-picker-context';
18
18
  import Button from '../button';
19
- import { CompositeItem } from '../composite';
19
+ import { CompositeItem } from '../composite/v2';
20
20
  import Tooltip from '../tooltip';
21
- import type {
22
- OptionProps,
23
- CircularOptionPickerCompositeState,
24
- CircularOptionPickerContextProps,
25
- } from './types';
26
-
27
- const hasSelectedOption = new Map();
21
+ import type { OptionProps, CircularOptionPickerCompositeStore } from './types';
28
22
 
29
23
  function UnforwardedOptionAsButton(
30
24
  props: {
@@ -40,7 +34,7 @@ function UnforwardedOptionAsButton(
40
34
  { ...additionalProps }
41
35
  aria-pressed={ isPressed }
42
36
  ref={ forwardedRef }
43
- ></Button>
37
+ />
44
38
  );
45
39
  }
46
40
 
@@ -51,38 +45,29 @@ function UnforwardedOptionAsOption(
51
45
  id: string;
52
46
  className?: string;
53
47
  isSelected?: boolean;
54
- context: CircularOptionPickerContextProps;
48
+ compositeStore: CircularOptionPickerCompositeStore;
55
49
  },
56
50
  forwardedRef: ForwardedRef< any >
57
51
  ) {
58
- const { id, isSelected, context, ...additionalProps } = props;
59
- const { isComposite, ..._compositeState } = context;
60
- const compositeState =
61
- _compositeState as CircularOptionPickerCompositeState;
62
- const { baseId, currentId, setCurrentId } = compositeState;
52
+ const { id, isSelected, compositeStore, ...additionalProps } = props;
53
+ const activeId = compositeStore.useState( 'activeId' );
63
54
 
64
- useEffect( () => {
65
- // If we call `setCurrentId` here, it doesn't update for other
66
- // Option renders in the same pass. So we have to store our own
67
- // map to make sure that we only set the first selected option.
68
- // We still need to check `currentId` because the control will
69
- // update this as the user moves around, and that state should
70
- // be maintained as the group gains and loses focus.
71
- if ( isSelected && ! currentId && ! hasSelectedOption.get( baseId ) ) {
72
- hasSelectedOption.set( baseId, true );
73
- setCurrentId( id );
74
- }
75
- }, [ baseId, currentId, id, isSelected, setCurrentId ] );
55
+ if ( isSelected && ! activeId ) {
56
+ compositeStore.setActiveId( id );
57
+ }
76
58
 
77
59
  return (
78
60
  <CompositeItem
79
- { ...additionalProps }
80
- { ...compositeState }
81
- as={ Button }
61
+ render={
62
+ <Button
63
+ { ...additionalProps }
64
+ role="option"
65
+ aria-selected={ !! isSelected }
66
+ ref={ forwardedRef }
67
+ />
68
+ }
69
+ store={ compositeStore }
82
70
  id={ id }
83
- role="option"
84
- aria-selected={ !! isSelected }
85
- ref={ forwardedRef }
86
71
  />
87
72
  );
88
73
  }
@@ -96,8 +81,9 @@ export function Option( {
96
81
  tooltipText,
97
82
  ...additionalProps
98
83
  }: OptionProps ) {
99
- const compositeContext = useContext( CircularOptionPickerContext );
100
- const { isComposite, baseId } = compositeContext;
84
+ const { baseId, compositeStore } = useContext(
85
+ CircularOptionPickerContext
86
+ );
101
87
  const id = useInstanceId(
102
88
  Option,
103
89
  baseId || 'components-circular-option-picker__option'
@@ -109,10 +95,10 @@ export function Option( {
109
95
  ...additionalProps,
110
96
  };
111
97
 
112
- const optionControl = isComposite ? (
98
+ const optionControl = compositeStore ? (
113
99
  <OptionAsOption
114
100
  { ...commonProps }
115
- context={ compositeContext }
101
+ compositeStore={ compositeStore }
116
102
  isSelected={ isSelected }
117
103
  />
118
104
  ) : (
@@ -7,14 +7,13 @@ import classnames from 'classnames';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { useInstanceId } from '@wordpress/compose';
10
- import { useEffect } from '@wordpress/element';
11
10
  import { isRTL } from '@wordpress/i18n';
12
11
 
13
12
  /**
14
13
  * Internal dependencies
15
14
  */
16
15
  import { CircularOptionPickerContext } from './circular-option-picker-context';
17
- import { Composite, useCompositeState } from '../composite';
16
+ import { Composite, useCompositeStore } from '../composite/v2';
18
17
  import type {
19
18
  CircularOptionPickerProps,
20
19
  ListboxCircularOptionPickerProps,
@@ -85,30 +84,15 @@ function ListboxCircularOptionPicker(
85
84
  children,
86
85
  ...additionalProps
87
86
  } = props;
88
- const rtl = isRTL();
89
87
 
90
- const compositeState = useCompositeState( { baseId, loop, rtl } );
91
- const { setBaseId, setLoop, setRTL } = compositeState;
92
-
93
- // These are necessary as `useCompositeState` is sealed after
94
- // the first render, so although unlikely to happen, if a state
95
- // property should change, we need to process it accordingly.
96
-
97
- useEffect( () => {
98
- setBaseId( baseId );
99
- }, [ setBaseId, baseId ] );
100
-
101
- useEffect( () => {
102
- setLoop( loop );
103
- }, [ setLoop, loop ] );
104
-
105
- useEffect( () => {
106
- setRTL( rtl );
107
- }, [ setRTL, rtl ] );
88
+ const compositeStore = useCompositeStore( {
89
+ focusLoop: loop,
90
+ rtl: isRTL(),
91
+ } );
108
92
 
109
93
  const compositeContext = {
110
- isComposite: true,
111
- ...compositeState,
94
+ baseId,
95
+ compositeStore,
112
96
  };
113
97
 
114
98
  return (
@@ -116,7 +100,8 @@ function ListboxCircularOptionPicker(
116
100
  <CircularOptionPickerContext.Provider value={ compositeContext }>
117
101
  <Composite
118
102
  { ...additionalProps }
119
- { ...compositeState }
103
+ id={ baseId }
104
+ store={ compositeStore }
120
105
  role={ 'listbox' }
121
106
  >
122
107
  { options }
@@ -134,10 +119,8 @@ function ButtonsCircularOptionPicker(
134
119
  const { actions, options, children, baseId, ...additionalProps } = props;
135
120
 
136
121
  return (
137
- <div { ...additionalProps }>
138
- <CircularOptionPickerContext.Provider
139
- value={ { isComposite: false, baseId } }
140
- >
122
+ <div { ...additionalProps } id={ baseId }>
123
+ <CircularOptionPickerContext.Provider value={ { baseId } }>
141
124
  { options }
142
125
  { children }
143
126
  { actions }
@@ -14,7 +14,7 @@ import type { Icon } from '@wordpress/icons';
14
14
  import type { ButtonAsButtonProps } from '../button/types';
15
15
  import type { DropdownProps } from '../dropdown/types';
16
16
  import type { WordPressComponentProps } from '../context';
17
- import type { CompositeState } from '../composite';
17
+ import type { CompositeStore } from '../composite/v2';
18
18
 
19
19
  type CommonCircularOptionPickerProps = {
20
20
  /**
@@ -123,7 +123,8 @@ export type OptionProps = Omit<
123
123
  >;
124
124
  };
125
125
 
126
- export type CircularOptionPickerCompositeState = CompositeState;
127
- export type CircularOptionPickerContextProps =
128
- | { isComposite?: false; baseId?: string }
129
- | ( { isComposite: true } & CircularOptionPickerCompositeState );
126
+ export type CircularOptionPickerCompositeStore = CompositeStore;
127
+ export type CircularOptionPickerContextProps = {
128
+ baseId?: string;
129
+ compositeStore?: CircularOptionPickerCompositeStore;
130
+ };
@@ -147,6 +147,10 @@ export function CustomColorPickerDropdown( {
147
147
  const popoverProps = useMemo< DropdownProps[ 'popoverProps' ] >(
148
148
  () => ( {
149
149
  shift: true,
150
+ // Disabling resize as it would otherwise cause the popover to show
151
+ // scrollbars while dragging the color picker's handle close to the
152
+ // popover edge.
153
+ resize: false,
150
154
  ...( isRenderedInSidebar
151
155
  ? {
152
156
  // When in the sidebar: open to the left (stacking),
@@ -299,6 +303,7 @@ function UnforwardedColorPalette(
299
303
  style={ {
300
304
  background: value,
301
305
  } }
306
+ type="button"
302
307
  />
303
308
  <VStack
304
309
  className="components-color-palette__custom-color-text-wrapper"
@@ -307,7 +312,7 @@ function UnforwardedColorPalette(
307
312
  <Truncate className="components-color-palette__custom-color-name">
308
313
  { value
309
314
  ? buttonLabelName
310
- : 'No color selected' }
315
+ : __( 'No color selected' ) }
311
316
  </Truncate>
312
317
  { /*
313
318
  This `Truncate` is always rendered, even if
@@ -10,7 +10,7 @@ import namesPlugin from 'colord/plugins/names';
10
10
  * WordPress dependencies
11
11
  */
12
12
  import { useCallback, useState, useMemo } from '@wordpress/element';
13
- import { useDebounce } from '@wordpress/compose';
13
+ import { useDebounce, useMergeRefs } from '@wordpress/compose';
14
14
  import { __ } from '@wordpress/i18n';
15
15
 
16
16
  /**
@@ -49,8 +49,24 @@ const UnconnectedColorPicker = (
49
49
  onChange,
50
50
  defaultValue = '#fff',
51
51
  copyFormat,
52
+
53
+ // Context
54
+ onPickerDragStart,
55
+ onPickerDragEnd,
52
56
  ...divProps
53
- } = useContextSystem( props, 'ColorPicker' );
57
+ } = useContextSystem<
58
+ ColorPickerProps & {
59
+ onPickerDragStart?: ( event: MouseEvent ) => void;
60
+ onPickerDragEnd?: ( event: MouseEvent ) => void;
61
+ }
62
+ >( props, 'ColorPicker' );
63
+
64
+ const [ containerEl, setContainerEl ] = useState< HTMLElement | null >(
65
+ null
66
+ );
67
+ const containerRef = ( node: HTMLElement | null ) => {
68
+ setContainerEl( node );
69
+ };
54
70
 
55
71
  // Use a safe default value for the color and remove the possibility of `undefined`.
56
72
  const [ color, setColor ] = useControlledValue( {
@@ -77,11 +93,17 @@ const UnconnectedColorPicker = (
77
93
  );
78
94
 
79
95
  return (
80
- <ColorfulWrapper ref={ forwardedRef } { ...divProps }>
96
+ <ColorfulWrapper
97
+ ref={ useMergeRefs( [ containerRef, forwardedRef ] ) }
98
+ { ...divProps }
99
+ >
81
100
  <Picker
101
+ containerEl={ containerEl }
82
102
  onChange={ handleChange }
83
103
  color={ safeColordColor }
84
104
  enableAlpha={ enableAlpha }
105
+ onDragStart={ onPickerDragStart }
106
+ onDragEnd={ onPickerDragEnd }
85
107
  />
86
108
  <AuxiliaryColorArtefactWrapper>
87
109
  <AuxiliaryColorArtefactHStackHeader justify="space-between">
@@ -7,18 +7,112 @@ import { colord } from 'colord';
7
7
  /**
8
8
  * WordPress dependencies
9
9
  */
10
- import { useMemo } from '@wordpress/element';
10
+ import { useMemo, useEffect, useRef } from '@wordpress/element';
11
11
  /**
12
12
  * Internal dependencies
13
13
  */
14
14
  import type { PickerProps } from './types';
15
15
 
16
- export const Picker = ( { color, enableAlpha, onChange }: PickerProps ) => {
16
+ /**
17
+ * Track the start and the end of drag pointer events related to controlling
18
+ * the picker's saturation / hue / alpha, and fire the corresponding callbacks.
19
+ * This is particularly useful to implement synergies like the one with the
20
+ * `Popover` component, where a pointer events "trap" is rendered while
21
+ * the user is dragging the pointer to avoid potential interference with iframe
22
+ * elements.
23
+ *
24
+ * @param props
25
+ * @param props.containerEl
26
+ * @param props.onDragStart
27
+ * @param props.onDragEnd
28
+ */
29
+ const useOnPickerDrag = ( {
30
+ containerEl,
31
+ onDragStart,
32
+ onDragEnd,
33
+ }: Pick< PickerProps, 'containerEl' | 'onDragStart' | 'onDragEnd' > ) => {
34
+ const isDragging = useRef( false );
35
+ const leftWhileDragging = useRef( false );
36
+ useEffect( () => {
37
+ if ( ! containerEl || ( ! onDragStart && ! onDragEnd ) ) {
38
+ return;
39
+ }
40
+ const interactiveElements = [
41
+ containerEl.querySelector( '.react-colorful__saturation' ),
42
+ containerEl.querySelector( '.react-colorful__hue' ),
43
+ containerEl.querySelector( '.react-colorful__alpha' ),
44
+ ].filter( ( el ) => !! el ) as Element[];
45
+
46
+ if ( interactiveElements.length === 0 ) {
47
+ return;
48
+ }
49
+
50
+ const doc = containerEl.ownerDocument;
51
+
52
+ const onPointerUp: EventListener = ( event ) => {
53
+ isDragging.current = false;
54
+ leftWhileDragging.current = false;
55
+ onDragEnd?.( event as MouseEvent );
56
+ };
57
+
58
+ const onPointerDown: EventListener = ( event ) => {
59
+ isDragging.current = true;
60
+ onDragStart?.( event as MouseEvent );
61
+ };
62
+
63
+ const onPointerLeave: EventListener = () => {
64
+ leftWhileDragging.current = isDragging.current;
65
+ };
66
+
67
+ // Try to detect if the user released the pointer while away from the
68
+ // current window. If the check is successfull, the dragEnd callback will
69
+ // called as soon as the pointer re-enters the window (better late than never)
70
+ const onPointerEnter: EventListener = ( event ) => {
71
+ const noPointerButtonsArePressed =
72
+ ( event as PointerEvent ).buttons === 0;
73
+
74
+ if ( leftWhileDragging.current && noPointerButtonsArePressed ) {
75
+ onPointerUp( event );
76
+ }
77
+ };
78
+
79
+ // The pointerdown event is added on the interactive elements,
80
+ // while the remaining events are added on the document object since
81
+ // the pointer wouldn't necessarily be hovering the initial interactive
82
+ // element at that point.
83
+ interactiveElements.forEach( ( el ) =>
84
+ el.addEventListener( 'pointerdown', onPointerDown )
85
+ );
86
+ doc.addEventListener( 'pointerup', onPointerUp );
87
+ doc.addEventListener( 'pointerenter', onPointerEnter );
88
+ doc.addEventListener( 'pointerleave', onPointerLeave );
89
+
90
+ return () => {
91
+ interactiveElements.forEach( ( el ) =>
92
+ el.removeEventListener( 'pointerdown', onPointerDown )
93
+ );
94
+ doc.removeEventListener( 'pointerup', onPointerUp );
95
+ doc.removeEventListener( 'pointerenter', onPointerEnter );
96
+ doc.removeEventListener( 'pointerleave', onPointerUp );
97
+ };
98
+ }, [ onDragStart, onDragEnd, containerEl ] );
99
+ };
100
+
101
+ export const Picker = ( {
102
+ color,
103
+ enableAlpha,
104
+ onChange,
105
+ onDragStart,
106
+ onDragEnd,
107
+ containerEl,
108
+ }: PickerProps ) => {
17
109
  const Component = enableAlpha
18
110
  ? RgbaStringColorPicker
19
111
  : RgbStringColorPicker;
20
112
  const rgbColor = useMemo( () => color.toRgbString(), [ color ] );
21
113
 
114
+ useOnPickerDrag( { containerEl, onDragStart, onDragEnd } );
115
+
22
116
  return (
23
117
  <Component
24
118
  color={ rgbColor }
@@ -81,7 +81,6 @@ export const ColorfulWrapper = styled.div`
81
81
  align-items: center;
82
82
  width: 216px;
83
83
  height: auto;
84
- overflow: hidden;
85
84
  }
86
85
 
87
86
  .react-colorful__saturation {
@@ -59,6 +59,9 @@ export interface PickerProps {
59
59
  color: Colord;
60
60
  enableAlpha: boolean;
61
61
  onChange: ( nextColor: Colord ) => void;
62
+ containerEl: HTMLElement | null;
63
+ onDragStart?: ( event: MouseEvent ) => void;
64
+ onDragEnd?: ( event: MouseEvent ) => void;
62
65
  }
63
66
 
64
67
  export interface ColorInputProps {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Composite is a component that may contain navigable items represented by
3
+ * CompositeItem. It's inspired by the WAI-ARIA Composite Role and implements
4
+ * all the keyboard navigation mechanisms to ensure that there's only one
5
+ * tab stop for the whole Composite element. This means that it can behave as
6
+ * a roving tabindex or aria-activedescendant container.
7
+ *
8
+ * @see https://ariakit.org/components/composite
9
+ */
10
+
11
+ /* eslint-disable-next-line no-restricted-imports */
12
+ export {
13
+ Composite,
14
+ CompositeGroup,
15
+ CompositeGroupLabel,
16
+ CompositeItem,
17
+ CompositeRow,
18
+ useCompositeStore,
19
+ } from '@ariakit/react';
20
+
21
+ /* eslint-disable-next-line no-restricted-imports */
22
+ export type { CompositeStore } from '@ariakit/react';
@@ -137,4 +137,4 @@ The optional custom text to display as the confirmation button's label
137
137
  - Required: No
138
138
  - Default: "Cancel"
139
139
 
140
- The optional custom text to display as the cancelation button's label
140
+ The optional custom text to display as the cancellation button's label
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import type { ForwardedRef, KeyboardEvent } from 'react';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -13,7 +8,7 @@ import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
13
8
  * Internal dependencies
14
9
  */
15
10
  import Modal from '../modal';
16
- import type { OwnProps, DialogInputEvent } from './types';
11
+ import type { ConfirmDialogProps, DialogInputEvent } from './types';
17
12
  import type { WordPressComponentProps } from '../context';
18
13
  import { useContextSystem, contextConnect } from '../context';
19
14
  import { Flex } from '../flex';
@@ -23,10 +18,10 @@ import { VStack } from '../v-stack';
23
18
  import * as styles from './styles';
24
19
  import { useCx } from '../utils/hooks/use-cx';
25
20
 
26
- function ConfirmDialog(
27
- props: WordPressComponentProps< OwnProps, 'div', false >,
28
- forwardedRef: ForwardedRef< any >
29
- ) {
21
+ const UnconnectedConfirmDialog = (
22
+ props: WordPressComponentProps< ConfirmDialogProps, 'div', false >,
23
+ forwardedRef: React.ForwardedRef< any >
24
+ ) => {
30
25
  const {
31
26
  isOpen: isOpenProp,
32
27
  onConfirm,
@@ -67,7 +62,7 @@ function ConfirmDialog(
67
62
  );
68
63
 
69
64
  const handleEnter = useCallback(
70
- ( event: KeyboardEvent< HTMLDivElement > ) => {
65
+ ( event: React.KeyboardEvent< HTMLDivElement > ) => {
71
66
  // Avoid triggering the 'confirm' action when a button is focused,
72
67
  // as this can cause a double submission.
73
68
  const isConfirmOrCancelButton =
@@ -120,6 +115,77 @@ function ConfirmDialog(
120
115
  ) }
121
116
  </>
122
117
  );
123
- }
118
+ };
124
119
 
125
- export default contextConnect( ConfirmDialog, 'ConfirmDialog' );
120
+ /**
121
+ * `ConfirmDialog` is built of top of [`Modal`](/packages/components/src/modal/README.md)
122
+ * and displays a confirmation dialog, with _confirm_ and _cancel_ buttons.
123
+ * The dialog is confirmed by clicking the _confirm_ button or by pressing the `Enter` key.
124
+ * It is cancelled (closed) by clicking the _cancel_ button, by pressing the `ESC` key, or by
125
+ * clicking outside the dialog focus (i.e, the overlay).
126
+ *
127
+ * `ConfirmDialog` has two main implicit modes: controlled and uncontrolled.
128
+ *
129
+ * UnControlled:
130
+ *
131
+ * Allows the component to be used standalone, just by declaring it as part of another React's component render method:
132
+ * - It will be automatically open (displayed) upon mounting;
133
+ * - It will be automatically closed when clicking the _cancel_ button, by pressing the `ESC` key, or by clicking outside the dialog focus (i.e, the overlay);
134
+ * - `onCancel` is not mandatory but can be passed. Even if passed, the dialog will still be able to close itself.
135
+ *
136
+ * Activating this mode is as simple as omitting the `isOpen` prop. The only mandatory prop, in this case, is the `onConfirm` callback. The message is passed as the `children`. You can pass any JSX you'd like, which allows to further format the message or include sub-component if you'd like:
137
+ *
138
+ * ```jsx
139
+ * import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components';
140
+ *
141
+ * function Example() {
142
+ * return (
143
+ * <ConfirmDialog onConfirm={ () => console.debug( ' Confirmed! ' ) }>
144
+ * Are you sure? <strong>This action cannot be undone!</strong>
145
+ * </ConfirmDialog>
146
+ * );
147
+ * }
148
+ * ```
149
+ *
150
+ *
151
+ * Controlled mode:
152
+ * Let the parent component control when the dialog is open/closed. It's activated when a
153
+ * boolean value is passed to `isOpen`:
154
+ * - It will not be automatically closed. You need to let it know when to open/close by updating the value of the `isOpen` prop;
155
+ * - Both `onConfirm` and the `onCancel` callbacks are mandatory props in this mode;
156
+ * - You'll want to update the state that controls `isOpen` by updating it from the `onCancel` and `onConfirm` callbacks.
157
+ *
158
+ *```jsx
159
+ * import { __experimentalConfirmDialog as ConfirmDialog } from '@wordpress/components';
160
+ * import { useState } from '@wordpress/element';
161
+ *
162
+ * function Example() {
163
+ * const [ isOpen, setIsOpen ] = useState( true );
164
+ *
165
+ * const handleConfirm = () => {
166
+ * console.debug( 'Confirmed!' );
167
+ * setIsOpen( false );
168
+ * };
169
+ *
170
+ * const handleCancel = () => {
171
+ * console.debug( 'Cancelled!' );
172
+ * setIsOpen( false );
173
+ * };
174
+ *
175
+ * return (
176
+ * <ConfirmDialog
177
+ * isOpen={ isOpen }
178
+ * onConfirm={ handleConfirm }
179
+ * onCancel={ handleCancel }
180
+ * >
181
+ * Are you sure? <strong>This action cannot be undone!</strong>
182
+ * </ConfirmDialog>
183
+ * );
184
+ * }
185
+ * ```
186
+ */
187
+ export const ConfirmDialog = contextConnect(
188
+ UnconnectedConfirmDialog,
189
+ 'ConfirmDialog'
190
+ );
191
+ export default ConfirmDialog;