@mortenbrudvik/waveui 0.7.0 → 0.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 (439) hide show
  1. package/README.md +564 -155
  2. package/dist/components/button/Button.cjs +1 -1
  3. package/dist/components/button/Button.cjs.map +1 -1
  4. package/dist/components/button/Button.mjs +36 -35
  5. package/dist/components/button/Button.mjs.map +1 -1
  6. package/dist/components/button/Button.slots.cjs +1 -1
  7. package/dist/components/button/Button.slots.cjs.map +1 -1
  8. package/dist/components/button/Button.slots.mjs +31 -21
  9. package/dist/components/button/Button.slots.mjs.map +1 -1
  10. package/dist/components/button/CompoundButton.cjs +1 -1
  11. package/dist/components/button/CompoundButton.cjs.map +1 -1
  12. package/dist/components/button/CompoundButton.mjs +31 -29
  13. package/dist/components/button/CompoundButton.mjs.map +1 -1
  14. package/dist/components/button/Link.cjs +1 -1
  15. package/dist/components/button/Link.cjs.map +1 -1
  16. package/dist/components/button/Link.mjs +24 -23
  17. package/dist/components/button/Link.mjs.map +1 -1
  18. package/dist/components/button/MenuButton.cjs +1 -1
  19. package/dist/components/button/MenuButton.cjs.map +1 -1
  20. package/dist/components/button/MenuButton.mjs +29 -28
  21. package/dist/components/button/MenuButton.mjs.map +1 -1
  22. package/dist/components/button/SplitButton.cjs +1 -1
  23. package/dist/components/button/SplitButton.cjs.map +1 -1
  24. package/dist/components/button/SplitButton.mjs +46 -45
  25. package/dist/components/button/SplitButton.mjs.map +1 -1
  26. package/dist/components/button/ToggleButton.cjs +1 -1
  27. package/dist/components/button/ToggleButton.cjs.map +1 -1
  28. package/dist/components/button/ToggleButton.mjs +38 -35
  29. package/dist/components/button/ToggleButton.mjs.map +1 -1
  30. package/dist/components/button/Toolbar.cjs +1 -1
  31. package/dist/components/button/Toolbar.cjs.map +1 -1
  32. package/dist/components/button/Toolbar.mjs +43 -42
  33. package/dist/components/button/Toolbar.mjs.map +1 -1
  34. package/dist/components/button/Toolbar.parts.cjs +1 -1
  35. package/dist/components/button/Toolbar.parts.cjs.map +1 -1
  36. package/dist/components/button/Toolbar.parts.mjs +76 -81
  37. package/dist/components/button/Toolbar.parts.mjs.map +1 -1
  38. package/dist/components/data-display/Avatar.cjs +1 -1
  39. package/dist/components/data-display/Avatar.cjs.map +1 -1
  40. package/dist/components/data-display/Avatar.mjs +72 -67
  41. package/dist/components/data-display/Avatar.mjs.map +1 -1
  42. package/dist/components/data-display/AvatarGroup.cjs +1 -1
  43. package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
  44. package/dist/components/data-display/AvatarGroup.mjs +89 -88
  45. package/dist/components/data-display/AvatarGroup.mjs.map +1 -1
  46. package/dist/components/data-display/Badge.cjs +1 -1
  47. package/dist/components/data-display/Badge.cjs.map +1 -1
  48. package/dist/components/data-display/Badge.mjs +17 -16
  49. package/dist/components/data-display/Badge.mjs.map +1 -1
  50. package/dist/components/data-display/CounterBadge.cjs +1 -1
  51. package/dist/components/data-display/CounterBadge.cjs.map +1 -1
  52. package/dist/components/data-display/CounterBadge.mjs +18 -17
  53. package/dist/components/data-display/CounterBadge.mjs.map +1 -1
  54. package/dist/components/data-display/Divider.cjs +1 -1
  55. package/dist/components/data-display/Divider.cjs.map +1 -1
  56. package/dist/components/data-display/Divider.mjs +31 -30
  57. package/dist/components/data-display/Divider.mjs.map +1 -1
  58. package/dist/components/data-display/Image.cjs +1 -1
  59. package/dist/components/data-display/Image.cjs.map +1 -1
  60. package/dist/components/data-display/Image.mjs +16 -15
  61. package/dist/components/data-display/Image.mjs.map +1 -1
  62. package/dist/components/data-display/InfoButton.cjs +2 -0
  63. package/dist/components/data-display/InfoButton.cjs.map +1 -0
  64. package/dist/components/data-display/InfoButton.mjs +173 -0
  65. package/dist/components/data-display/InfoButton.mjs.map +1 -0
  66. package/dist/components/data-display/InfoLabel.cjs +1 -1
  67. package/dist/components/data-display/InfoLabel.cjs.map +1 -1
  68. package/dist/components/data-display/InfoLabel.mjs +36 -92
  69. package/dist/components/data-display/InfoLabel.mjs.map +1 -1
  70. package/dist/components/data-display/List.cjs +1 -1
  71. package/dist/components/data-display/List.cjs.map +1 -1
  72. package/dist/components/data-display/List.mjs +175 -174
  73. package/dist/components/data-display/List.mjs.map +1 -1
  74. package/dist/components/data-display/Persona.cjs +1 -1
  75. package/dist/components/data-display/Persona.cjs.map +1 -1
  76. package/dist/components/data-display/Persona.mjs +31 -29
  77. package/dist/components/data-display/Persona.mjs.map +1 -1
  78. package/dist/components/data-display/PresenceBadge.cjs +1 -1
  79. package/dist/components/data-display/PresenceBadge.cjs.map +1 -1
  80. package/dist/components/data-display/PresenceBadge.mjs +25 -24
  81. package/dist/components/data-display/PresenceBadge.mjs.map +1 -1
  82. package/dist/components/data-display/Tag.cjs +1 -1
  83. package/dist/components/data-display/Tag.cjs.map +1 -1
  84. package/dist/components/data-display/Tag.mjs +87 -82
  85. package/dist/components/data-display/Tag.mjs.map +1 -1
  86. package/dist/components/feedback/MessageBar.cjs +1 -1
  87. package/dist/components/feedback/MessageBar.cjs.map +1 -1
  88. package/dist/components/feedback/MessageBar.mjs +129 -71
  89. package/dist/components/feedback/MessageBar.mjs.map +1 -1
  90. package/dist/components/feedback/MessageBar.parts.cjs +2 -0
  91. package/dist/components/feedback/MessageBar.parts.cjs.map +1 -0
  92. package/dist/components/feedback/MessageBar.parts.mjs +84 -0
  93. package/dist/components/feedback/MessageBar.parts.mjs.map +1 -0
  94. package/dist/components/feedback/MessageBar.reflow.cjs +2 -0
  95. package/dist/components/feedback/MessageBar.reflow.cjs.map +1 -0
  96. package/dist/components/feedback/MessageBar.reflow.mjs +74 -0
  97. package/dist/components/feedback/MessageBar.reflow.mjs.map +1 -0
  98. package/dist/components/feedback/MessageBar.status.cjs +1 -1
  99. package/dist/components/feedback/MessageBar.status.cjs.map +1 -1
  100. package/dist/components/feedback/MessageBar.status.mjs +2 -1
  101. package/dist/components/feedback/MessageBar.status.mjs.map +1 -1
  102. package/dist/components/feedback/ProgressBar.cjs +1 -1
  103. package/dist/components/feedback/ProgressBar.cjs.map +1 -1
  104. package/dist/components/feedback/ProgressBar.mjs +44 -43
  105. package/dist/components/feedback/ProgressBar.mjs.map +1 -1
  106. package/dist/components/feedback/Skeleton.cjs +1 -1
  107. package/dist/components/feedback/Skeleton.cjs.map +1 -1
  108. package/dist/components/feedback/Skeleton.mjs +26 -24
  109. package/dist/components/feedback/Skeleton.mjs.map +1 -1
  110. package/dist/components/feedback/Spinner.cjs +1 -1
  111. package/dist/components/feedback/Spinner.cjs.map +1 -1
  112. package/dist/components/feedback/Spinner.mjs +32 -31
  113. package/dist/components/feedback/Spinner.mjs.map +1 -1
  114. package/dist/components/feedback/Toast.cjs +1 -1
  115. package/dist/components/feedback/Toast.cjs.map +1 -1
  116. package/dist/components/feedback/Toast.context.cjs +2 -0
  117. package/dist/components/feedback/Toast.context.cjs.map +1 -0
  118. package/dist/components/feedback/Toast.context.mjs +23 -0
  119. package/dist/components/feedback/Toast.context.mjs.map +1 -0
  120. package/dist/components/feedback/Toast.mjs +10 -342
  121. package/dist/components/feedback/Toast.mjs.map +1 -1
  122. package/dist/components/feedback/Toast.parts.cjs +2 -0
  123. package/dist/components/feedback/Toast.parts.cjs.map +1 -0
  124. package/dist/components/feedback/Toast.parts.mjs +98 -0
  125. package/dist/components/feedback/Toast.parts.mjs.map +1 -0
  126. package/dist/components/feedback/Toast.root.cjs +2 -0
  127. package/dist/components/feedback/Toast.root.cjs.map +1 -0
  128. package/dist/components/feedback/Toast.root.mjs +109 -0
  129. package/dist/components/feedback/Toast.root.mjs.map +1 -0
  130. package/dist/components/feedback/Toaster.cjs +2 -0
  131. package/dist/components/feedback/Toaster.cjs.map +1 -0
  132. package/dist/components/feedback/Toaster.mjs +564 -0
  133. package/dist/components/feedback/Toaster.mjs.map +1 -0
  134. package/dist/components/input/AlphaSlider.cjs +2 -0
  135. package/dist/components/input/AlphaSlider.cjs.map +1 -0
  136. package/dist/components/input/AlphaSlider.mjs +115 -0
  137. package/dist/components/input/AlphaSlider.mjs.map +1 -0
  138. package/dist/components/input/Checkbox.cjs +1 -1
  139. package/dist/components/input/Checkbox.cjs.map +1 -1
  140. package/dist/components/input/Checkbox.mjs +87 -70
  141. package/dist/components/input/Checkbox.mjs.map +1 -1
  142. package/dist/components/input/ColorArea.cjs +2 -0
  143. package/dist/components/input/ColorArea.cjs.map +1 -0
  144. package/dist/components/input/ColorArea.mjs +204 -0
  145. package/dist/components/input/ColorArea.mjs.map +1 -0
  146. package/dist/components/input/ColorPicker.cjs +5 -5
  147. package/dist/components/input/ColorPicker.cjs.map +1 -1
  148. package/dist/components/input/ColorPicker.mjs +142 -165
  149. package/dist/components/input/ColorPicker.mjs.map +1 -1
  150. package/dist/components/input/ColorPicker.parts.cjs +2 -0
  151. package/dist/components/input/ColorPicker.parts.cjs.map +1 -0
  152. package/dist/components/input/ColorPicker.parts.mjs +192 -0
  153. package/dist/components/input/ColorPicker.parts.mjs.map +1 -0
  154. package/dist/components/input/ColorSlider.cjs +2 -0
  155. package/dist/components/input/ColorSlider.cjs.map +1 -0
  156. package/dist/components/input/ColorSlider.mjs +138 -0
  157. package/dist/components/input/ColorSlider.mjs.map +1 -0
  158. package/dist/components/input/Combobox.cjs +1 -1
  159. package/dist/components/input/Combobox.cjs.map +1 -1
  160. package/dist/components/input/Combobox.expand.cjs +1 -1
  161. package/dist/components/input/Combobox.expand.cjs.map +1 -1
  162. package/dist/components/input/Combobox.expand.mjs +22 -22
  163. package/dist/components/input/Combobox.expand.mjs.map +1 -1
  164. package/dist/components/input/Combobox.mjs +151 -141
  165. package/dist/components/input/Combobox.mjs.map +1 -1
  166. package/dist/components/input/DatePicker.cjs +1 -1
  167. package/dist/components/input/DatePicker.cjs.map +1 -1
  168. package/dist/components/input/DatePicker.mjs +239 -234
  169. package/dist/components/input/DatePicker.mjs.map +1 -1
  170. package/dist/components/input/Dropdown.cjs +1 -1
  171. package/dist/components/input/Dropdown.cjs.map +1 -1
  172. package/dist/components/input/Dropdown.mjs +109 -100
  173. package/dist/components/input/Dropdown.mjs.map +1 -1
  174. package/dist/components/input/Field.cjs +1 -1
  175. package/dist/components/input/Field.cjs.map +1 -1
  176. package/dist/components/input/Field.mjs +139 -105
  177. package/dist/components/input/Field.mjs.map +1 -1
  178. package/dist/components/input/Input.cjs +1 -1
  179. package/dist/components/input/Input.cjs.map +1 -1
  180. package/dist/components/input/Input.mjs +80 -55
  181. package/dist/components/input/Input.mjs.map +1 -1
  182. package/dist/components/input/Label.cjs +1 -1
  183. package/dist/components/input/Label.cjs.map +1 -1
  184. package/dist/components/input/Label.mjs +19 -12
  185. package/dist/components/input/Label.mjs.map +1 -1
  186. package/dist/components/input/Option.cjs +1 -1
  187. package/dist/components/input/Option.cjs.map +1 -1
  188. package/dist/components/input/Option.mjs +100 -98
  189. package/dist/components/input/Option.mjs.map +1 -1
  190. package/dist/components/input/RadioGroup.cjs +1 -1
  191. package/dist/components/input/RadioGroup.cjs.map +1 -1
  192. package/dist/components/input/RadioGroup.mjs +89 -88
  193. package/dist/components/input/RadioGroup.mjs.map +1 -1
  194. package/dist/components/input/Rating.cjs +1 -1
  195. package/dist/components/input/Rating.cjs.map +1 -1
  196. package/dist/components/input/Rating.item.cjs +2 -0
  197. package/dist/components/input/Rating.item.cjs.map +1 -0
  198. package/dist/components/input/Rating.item.mjs +255 -0
  199. package/dist/components/input/Rating.item.mjs.map +1 -0
  200. package/dist/components/input/Rating.mjs +152 -152
  201. package/dist/components/input/Rating.mjs.map +1 -1
  202. package/dist/components/input/SearchBox.cjs +1 -1
  203. package/dist/components/input/SearchBox.cjs.map +1 -1
  204. package/dist/components/input/SearchBox.mjs +162 -132
  205. package/dist/components/input/SearchBox.mjs.map +1 -1
  206. package/dist/components/input/Select.cjs +1 -1
  207. package/dist/components/input/Select.cjs.map +1 -1
  208. package/dist/components/input/Select.mjs +34 -24
  209. package/dist/components/input/Select.mjs.map +1 -1
  210. package/dist/components/input/Slider.cjs +1 -1
  211. package/dist/components/input/Slider.cjs.map +1 -1
  212. package/dist/components/input/Slider.mjs +78 -54
  213. package/dist/components/input/Slider.mjs.map +1 -1
  214. package/dist/components/input/SpinButton.cjs +1 -1
  215. package/dist/components/input/SpinButton.cjs.map +1 -1
  216. package/dist/components/input/SpinButton.mjs +202 -99
  217. package/dist/components/input/SpinButton.mjs.map +1 -1
  218. package/dist/components/input/SwatchPicker.cjs +1 -1
  219. package/dist/components/input/SwatchPicker.cjs.map +1 -1
  220. package/dist/components/input/SwatchPicker.mjs +38 -37
  221. package/dist/components/input/SwatchPicker.mjs.map +1 -1
  222. package/dist/components/input/Switch.cjs +1 -1
  223. package/dist/components/input/Switch.cjs.map +1 -1
  224. package/dist/components/input/Switch.mjs +75 -58
  225. package/dist/components/input/Switch.mjs.map +1 -1
  226. package/dist/components/input/TagPicker.cjs +1 -1
  227. package/dist/components/input/TagPicker.cjs.map +1 -1
  228. package/dist/components/input/TagPicker.mjs +199 -169
  229. package/dist/components/input/TagPicker.mjs.map +1 -1
  230. package/dist/components/input/Textarea.cjs +1 -1
  231. package/dist/components/input/Textarea.cjs.map +1 -1
  232. package/dist/components/input/Textarea.mjs +31 -23
  233. package/dist/components/input/Textarea.mjs.map +1 -1
  234. package/dist/components/input/TimePicker.cjs +1 -1
  235. package/dist/components/input/TimePicker.cjs.map +1 -1
  236. package/dist/components/input/TimePicker.mjs +185 -177
  237. package/dist/components/input/TimePicker.mjs.map +1 -1
  238. package/dist/components/input/colorName.cjs +2 -0
  239. package/dist/components/input/colorName.cjs.map +1 -0
  240. package/dist/components/input/colorName.mjs +30 -0
  241. package/dist/components/input/colorName.mjs.map +1 -0
  242. package/dist/components/input/colorUtils.cjs +1 -1
  243. package/dist/components/input/colorUtils.cjs.map +1 -1
  244. package/dist/components/input/colorUtils.mjs +154 -56
  245. package/dist/components/input/colorUtils.mjs.map +1 -1
  246. package/dist/components/input/fieldLabel.cjs +2 -0
  247. package/dist/components/input/fieldLabel.cjs.map +1 -0
  248. package/dist/components/input/fieldLabel.mjs +17 -0
  249. package/dist/components/input/fieldLabel.mjs.map +1 -0
  250. package/dist/components/input/inputLook.cjs +2 -0
  251. package/dist/components/input/inputLook.cjs.map +1 -0
  252. package/dist/components/input/inputLook.mjs +28 -0
  253. package/dist/components/input/inputLook.mjs.map +1 -0
  254. package/dist/components/input/pickerStyles.cjs +1 -1
  255. package/dist/components/input/pickerStyles.cjs.map +1 -1
  256. package/dist/components/input/pickerStyles.mjs +42 -4
  257. package/dist/components/input/pickerStyles.mjs.map +1 -1
  258. package/dist/components/layout/Accordion.cjs +1 -1
  259. package/dist/components/layout/Accordion.cjs.map +1 -1
  260. package/dist/components/layout/Accordion.mjs +130 -129
  261. package/dist/components/layout/Accordion.mjs.map +1 -1
  262. package/dist/components/layout/Card.cjs +1 -1
  263. package/dist/components/layout/Card.cjs.map +1 -1
  264. package/dist/components/layout/Card.mjs +78 -77
  265. package/dist/components/layout/Card.mjs.map +1 -1
  266. package/dist/components/layout/Carousel.cjs +1 -1
  267. package/dist/components/layout/Carousel.cjs.map +1 -1
  268. package/dist/components/layout/Carousel.mjs +111 -110
  269. package/dist/components/layout/Carousel.mjs.map +1 -1
  270. package/dist/components/layout/Flex.cjs +1 -1
  271. package/dist/components/layout/Flex.cjs.map +1 -1
  272. package/dist/components/layout/Flex.mjs +22 -21
  273. package/dist/components/layout/Flex.mjs.map +1 -1
  274. package/dist/components/layout/Grid.cjs +1 -1
  275. package/dist/components/layout/Grid.cjs.map +1 -1
  276. package/dist/components/layout/Grid.mjs +19 -18
  277. package/dist/components/layout/Grid.mjs.map +1 -1
  278. package/dist/components/layout/Overflow.cjs +1 -1
  279. package/dist/components/layout/Overflow.cjs.map +1 -1
  280. package/dist/components/layout/Overflow.mjs +92 -91
  281. package/dist/components/layout/Overflow.mjs.map +1 -1
  282. package/dist/components/layout/Stack.cjs +1 -1
  283. package/dist/components/layout/Stack.cjs.map +1 -1
  284. package/dist/components/layout/Stack.mjs +14 -13
  285. package/dist/components/layout/Stack.mjs.map +1 -1
  286. package/dist/components/layout/TabList.cjs +1 -1
  287. package/dist/components/layout/TabList.cjs.map +1 -1
  288. package/dist/components/layout/TabList.mjs +118 -117
  289. package/dist/components/layout/TabList.mjs.map +1 -1
  290. package/dist/components/layout/Tree.cjs +1 -1
  291. package/dist/components/layout/Tree.cjs.map +1 -1
  292. package/dist/components/layout/Tree.context.cjs +2 -0
  293. package/dist/components/layout/Tree.context.cjs.map +1 -0
  294. package/dist/components/layout/Tree.context.mjs +33 -0
  295. package/dist/components/layout/Tree.context.mjs.map +1 -0
  296. package/dist/components/layout/Tree.core.cjs +2 -0
  297. package/dist/components/layout/Tree.core.cjs.map +1 -0
  298. package/dist/components/layout/Tree.core.mjs +416 -0
  299. package/dist/components/layout/Tree.core.mjs.map +1 -0
  300. package/dist/components/layout/Tree.itemPersonaLayout.cjs +2 -0
  301. package/dist/components/layout/Tree.itemPersonaLayout.cjs.map +1 -0
  302. package/dist/components/layout/Tree.itemPersonaLayout.mjs +46 -0
  303. package/dist/components/layout/Tree.itemPersonaLayout.mjs.map +1 -0
  304. package/dist/components/layout/Tree.mjs +7 -215
  305. package/dist/components/layout/Tree.mjs.map +1 -1
  306. package/dist/components/layout/Tree.selection.cjs +2 -0
  307. package/dist/components/layout/Tree.selection.cjs.map +1 -0
  308. package/dist/components/layout/Tree.selection.mjs +73 -0
  309. package/dist/components/layout/Tree.selection.mjs.map +1 -0
  310. package/dist/components/navigation/Breadcrumb.cjs +1 -1
  311. package/dist/components/navigation/Breadcrumb.cjs.map +1 -1
  312. package/dist/components/navigation/Breadcrumb.mjs +76 -75
  313. package/dist/components/navigation/Breadcrumb.mjs.map +1 -1
  314. package/dist/components/navigation/Menu.group.cjs +1 -1
  315. package/dist/components/navigation/Menu.group.cjs.map +1 -1
  316. package/dist/components/navigation/Menu.group.mjs +39 -37
  317. package/dist/components/navigation/Menu.group.mjs.map +1 -1
  318. package/dist/components/navigation/Menu.items.cjs +1 -1
  319. package/dist/components/navigation/Menu.items.cjs.map +1 -1
  320. package/dist/components/navigation/Menu.items.mjs +67 -61
  321. package/dist/components/navigation/Menu.items.mjs.map +1 -1
  322. package/dist/components/navigation/Menu.link.cjs +1 -1
  323. package/dist/components/navigation/Menu.link.cjs.map +1 -1
  324. package/dist/components/navigation/Menu.link.mjs +34 -28
  325. package/dist/components/navigation/Menu.link.mjs.map +1 -1
  326. package/dist/components/navigation/Menu.popover.cjs +1 -1
  327. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  328. package/dist/components/navigation/Menu.popover.mjs +114 -113
  329. package/dist/components/navigation/Menu.popover.mjs.map +1 -1
  330. package/dist/components/navigation/Menu.root.cjs +1 -1
  331. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  332. package/dist/components/navigation/Menu.root.mjs +171 -170
  333. package/dist/components/navigation/Menu.root.mjs.map +1 -1
  334. package/dist/components/navigation/Menu.selectable.cjs +1 -1
  335. package/dist/components/navigation/Menu.selectable.cjs.map +1 -1
  336. package/dist/components/navigation/Menu.selectable.mjs +85 -63
  337. package/dist/components/navigation/Menu.selectable.mjs.map +1 -1
  338. package/dist/components/navigation/Menu.splitGroup.cjs +1 -1
  339. package/dist/components/navigation/Menu.splitGroup.cjs.map +1 -1
  340. package/dist/components/navigation/Menu.splitGroup.mjs +27 -26
  341. package/dist/components/navigation/Menu.splitGroup.mjs.map +1 -1
  342. package/dist/components/navigation/Menu.trigger.cjs +1 -1
  343. package/dist/components/navigation/Menu.trigger.cjs.map +1 -1
  344. package/dist/components/navigation/Menu.trigger.mjs +6 -6
  345. package/dist/components/navigation/Nav.cjs +1 -1
  346. package/dist/components/navigation/Nav.cjs.map +1 -1
  347. package/dist/components/navigation/Nav.mjs +170 -155
  348. package/dist/components/navigation/Nav.mjs.map +1 -1
  349. package/dist/components/navigation/Pagination.cjs +1 -1
  350. package/dist/components/navigation/Pagination.cjs.map +1 -1
  351. package/dist/components/navigation/Pagination.mjs +73 -65
  352. package/dist/components/navigation/Pagination.mjs.map +1 -1
  353. package/dist/components/navigation/Stepper.cjs +1 -1
  354. package/dist/components/navigation/Stepper.cjs.map +1 -1
  355. package/dist/components/navigation/Stepper.mjs +110 -109
  356. package/dist/components/navigation/Stepper.mjs.map +1 -1
  357. package/dist/components/overlays/Dialog.cjs +1 -1
  358. package/dist/components/overlays/Dialog.cjs.map +1 -1
  359. package/dist/components/overlays/Dialog.mjs +109 -108
  360. package/dist/components/overlays/Dialog.mjs.map +1 -1
  361. package/dist/components/overlays/Drawer.cjs +1 -1
  362. package/dist/components/overlays/Drawer.cjs.map +1 -1
  363. package/dist/components/overlays/Drawer.mjs +110 -109
  364. package/dist/components/overlays/Drawer.mjs.map +1 -1
  365. package/dist/components/overlays/Popover.cjs +1 -1
  366. package/dist/components/overlays/Popover.cjs.map +1 -1
  367. package/dist/components/overlays/Popover.mjs +148 -151
  368. package/dist/components/overlays/Popover.mjs.map +1 -1
  369. package/dist/components/overlays/Popover.shared.cjs +1 -1
  370. package/dist/components/overlays/Popover.shared.cjs.map +1 -1
  371. package/dist/components/overlays/Popover.shared.mjs +82 -77
  372. package/dist/components/overlays/Popover.shared.mjs.map +1 -1
  373. package/dist/components/overlays/TeachingPopover.cjs +1 -1
  374. package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
  375. package/dist/components/overlays/TeachingPopover.mjs +41 -37
  376. package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
  377. package/dist/components/overlays/Tooltip.cjs +1 -1
  378. package/dist/components/overlays/Tooltip.cjs.map +1 -1
  379. package/dist/components/overlays/Tooltip.mjs +87 -86
  380. package/dist/components/overlays/Tooltip.mjs.map +1 -1
  381. package/dist/components/portal/Portal.cjs +1 -1
  382. package/dist/components/portal/Portal.cjs.map +1 -1
  383. package/dist/components/portal/Portal.mjs +33 -32
  384. package/dist/components/portal/Portal.mjs.map +1 -1
  385. package/dist/components/provider/WaveProvider.cjs +1 -1
  386. package/dist/components/provider/WaveProvider.cjs.map +1 -1
  387. package/dist/components/provider/WaveProvider.mjs +36 -29
  388. package/dist/components/provider/WaveProvider.mjs.map +1 -1
  389. package/dist/components/table/DataGrid.cjs +1 -1
  390. package/dist/components/table/DataGrid.cjs.map +1 -1
  391. package/dist/components/table/DataGrid.mjs +228 -227
  392. package/dist/components/table/DataGrid.mjs.map +1 -1
  393. package/dist/components/table/Table.cjs +1 -1
  394. package/dist/components/table/Table.cjs.map +1 -1
  395. package/dist/components/table/Table.mjs +60 -59
  396. package/dist/components/table/Table.mjs.map +1 -1
  397. package/dist/components/typography/Text.cjs +1 -1
  398. package/dist/components/typography/Text.cjs.map +1 -1
  399. package/dist/components/typography/Text.mjs +12 -11
  400. package/dist/components/typography/Text.mjs.map +1 -1
  401. package/dist/hooks/useFieldControl.cjs.map +1 -1
  402. package/dist/hooks/useFieldControl.mjs.map +1 -1
  403. package/dist/hooks/useMediaQuery.cjs +2 -0
  404. package/dist/hooks/useMediaQuery.cjs.map +1 -0
  405. package/dist/hooks/useMediaQuery.mjs +20 -0
  406. package/dist/hooks/useMediaQuery.mjs.map +1 -0
  407. package/dist/hooks/usePrefersReducedMotion.cjs +1 -1
  408. package/dist/hooks/usePrefersReducedMotion.cjs.map +1 -1
  409. package/dist/hooks/usePrefersReducedMotion.mjs +4 -18
  410. package/dist/hooks/usePrefersReducedMotion.mjs.map +1 -1
  411. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  412. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  413. package/dist/hooks/useRovingTabIndex.mjs +3 -3
  414. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  415. package/dist/index.cjs +1 -1
  416. package/dist/index.d.cts +2890 -197
  417. package/dist/index.d.ts +2890 -197
  418. package/dist/index.mjs +101 -87
  419. package/dist/lib/classNames.cjs +2 -0
  420. package/dist/lib/classNames.cjs.map +1 -0
  421. package/dist/lib/classNames.mjs +423 -0
  422. package/dist/lib/classNames.mjs.map +1 -0
  423. package/dist/lib/color.cjs +2 -0
  424. package/dist/lib/color.cjs.map +1 -0
  425. package/dist/lib/color.mjs +86 -0
  426. package/dist/lib/color.mjs.map +1 -0
  427. package/dist/lib/globalRegistry.cjs.map +1 -1
  428. package/dist/lib/globalRegistry.mjs.map +1 -1
  429. package/dist/lib/styles.cjs +1 -1
  430. package/dist/lib/styles.cjs.map +1 -1
  431. package/dist/lib/styles.mjs +21 -4
  432. package/dist/lib/styles.mjs.map +1 -1
  433. package/dist/lib/toaster.cjs +2 -0
  434. package/dist/lib/toaster.cjs.map +1 -0
  435. package/dist/lib/toaster.mjs +77 -0
  436. package/dist/lib/toaster.mjs.map +1 -0
  437. package/dist/styles.css +1 -1
  438. package/package.json +1 -1
  439. package/src/styles/tokens.css +13 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Avatar.mjs","names":["React","avatarSizeClasses","IMAGE_CLASS","getInitials","parts","name","normaliseName","readSlotProps","slot","getImageKey","imageSlot","src","element","slotProps","isPlainObject","value","proto","sameKeyPart","a","b","keys","k","sameImageKey","part","i","Avatar","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaDescription","ariaDetails","rest","image","imageKey","failed","setFailed","prevImageKey","setPrevImageKey","visualRef","img","active","checkedSrc","checkedSrcSet","markFailed","rootVisualRef","useMergedRefs","ref","hasConsumerName","roleCarriesName","role","normalisedName","imageAlt","decorative","handleError","ownAlt","hasOwnAlt","renderedAlt","description","imageIsNamed","imageDescription","imageNode","overrides","composeEventHandlers","ownIds","joinIds","renderSlot","visualDescription","iconNode","icon","slotRendersContent","initials","content","colorClass","PersonIcon","imageName","slotAltName","label","a11yProps","badgeNode","badge","hasBadge","visualClassName","cn","size","className"],"sources":["../../../src/components/data-display/Avatar.tsx"],"sourcesContent":["import * as React from 'react';\nimport { joinIds } from '../../lib/aria';\nimport { cn } from '../../lib/cn';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { PersonIcon } from '../../lib/icons';\nimport type { Size, Slot } from '../../lib/types';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\n\n/** Properties for the Avatar component. */\nexport interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * URL of the avatar image. When it fails to load (also before hydration, for server-rendered\n * markup), the avatar falls back to its icon or initials; a new `src` is tried again.\n */\n src?: string;\n /**\n * Full name of the person. It names the avatar for assistive technology (the image `alt`; in\n * initials and icon mode, or with a consumer `role`, the avatar's `aria-label`, with\n * `role=\"img\"` unless `role` is given) and generates the initials shown when there is no image.\n */\n name?: string;\n /** Size of the avatar.\n * @default 'medium'\n */\n size?: Size;\n /**\n * Icon shown instead of the initials when there is no image. Rendered in an `aria-hidden`\n * `<span>` (a slot object can override its attributes). A falsy value (`null`, `false`, `''`,\n * `0`, `NaN`, e.g. `icon={count && <Icon />}`) counts as no icon, as in 0.4, and so does an\n * array, `Set` or generator whose items render nothing. Without `icon` and without a usable\n * `name`, a generic person glyph is shown.\n */\n icon?: Slot<'span'>;\n /**\n * Custom image. Accepts a URL string (treated like `src`), an element (rendered as the image\n * itself, e.g. `<img>` or a framework image component) or an object of `<img>` attributes\n * (`{ src, alt, … }`). Takes precedence over `src`; its `alt` defaults to `name` (an `alt` that\n * is `undefined` counts as not given). When its alt names the avatar, the avatar's\n * `aria-describedby` ids are joined with the slot's own, while the slot's own `aria-description`\n * and `aria-details` win over the avatar's. When the image fails to load, an avatar without a\n * `name` keeps the name of the slot's own `alt`. After a load failure, a new image is tried\n * again: another `src` or `srcSet` of any type (an equal inline object is the same image), or\n * for an element slot another component type or `key`.\n */\n image?: Slot<'img'>;\n /**\n * Badge shown at the bottom end corner, e.g. `<PresenceBadge status=\"busy\" />`. With a badge\n * the avatar is wrapped in an outer `<span>` that receives `ref`, `className` and the other\n * props; `role`, `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-description` and\n * `aria-details` stay on the avatar visual, so the badge keeps its own accessible name. A falsy\n * value (`null`, `false`, `''`, `0`, `NaN`, e.g. `badge={count && <CounterBadge count={count} />}`)\n * counts as no badge, as in 0.4, and so does an array, `Set` or generator whose items render\n * nothing: the avatar then renders without the wrapper.\n */\n badge?: Slot<'span'>;\n /**\n * Hides the avatar visual from assistive technology (`aria-hidden`, empty image `alt`, no\n * `role=\"img\"`). Use it when the name is already shown next to the avatar (Persona). A badge\n * stays exposed.\n * @default false\n */\n decorative?: boolean;\n /** Ref to the root `<span>`: the avatar itself, or the badge wrapper when `badge` is set. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * Width, height and text size classes of the avatar per `size`. Module-only (not in the barrel):\n * AvatarGroup sizes its overflow button with the same classes, so the two always match.\n */\nexport const avatarSizeClasses: Readonly<Record<Size, string>> = {\n 'extra-small': 'w-6 h-6 text-[10px]', // 24px\n small: 'w-8 h-8 text-xs', // 32px\n medium: 'w-10 h-10 text-sm', // 40px\n large: 'w-12 h-12 text-base', // 48px\n 'extra-large': 'w-14 h-14 text-lg', // 56px\n};\n\nconst IMAGE_CLASS = 'w-full h-full object-cover';\n\n/** First letter of the first and last word, upper-cased; `''` for a blank name. */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n const first = Array.from(parts[0]!)[0] ?? '';\n const last = parts.length > 1 ? (Array.from(parts[parts.length - 1]!)[0] ?? '') : '';\n return (first + last).toUpperCase();\n}\n\n/** A name with its whitespace collapsed, or `undefined` when it is blank. */\nfunction normaliseName(name: string | undefined): string | undefined {\n return name?.trim().replace(/\\s+/g, ' ') || undefined;\n}\n\ntype ImageErrorHandler = React.ReactEventHandler<HTMLImageElement>;\n\n/** Description props that go to the element carrying the avatar's name (data-display#19). */\ntype DescriptionProps = Pick<\n React.HTMLAttributes<HTMLElement>,\n 'aria-describedby' | 'aria-description' | 'aria-details'\n>;\n\n/** Props the avatar sets on an element or object image slot, over the slot's own. */\ninterface SlotOverrides {\n onError: ImageErrorHandler;\n alt?: string;\n 'aria-describedby'?: string;\n}\n\n/** The props of an element or object image slot that the avatar reads. */\ninterface ImageSlotProps {\n src?: unknown;\n srcSet?: unknown;\n alt?: unknown;\n onError?: unknown;\n 'aria-describedby'?: unknown;\n}\n\n/** The props of an element or object image slot (to read its `src`, `alt` and handlers). */\nfunction readSlotProps(slot: unknown): ImageSlotProps | undefined {\n if (React.isValidElement(slot)) return slot.props as ImageSlotProps;\n if (typeof slot === 'object' && slot !== null && !(Symbol.iterator in slot)) {\n return slot as ImageSlotProps;\n }\n return undefined;\n}\n\n/**\n * What identifies the image, so a load failure is kept for it and a new image is tried again\n * (data-display#4): the `src` and `srcSet`, of any type (a framework image component may take a\n * static-import object), and for an element slot also its type and `key`.\n */\ntype ImageKey = readonly [type: unknown, key: unknown, src: unknown, srcSet: unknown];\n\nfunction getImageKey(\n imageSlot: unknown,\n slotProps: ImageSlotProps | undefined,\n src: string | undefined,\n): ImageKey {\n if (imageSlot === undefined) return [undefined, undefined, src || undefined, undefined];\n if (typeof imageSlot === 'string') return [undefined, undefined, imageSlot, undefined];\n const element = React.isValidElement(imageSlot) ? imageSlot : undefined;\n return [element?.type, element?.key, slotProps?.src, slotProps?.srcSet];\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/** Same value, or plain objects with the same entries (an inline `src={{ … }}` on each render). */\nfunction sameKeyPart(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]))\n );\n}\n\nfunction sameImageKey(a: ImageKey, b: ImageKey): boolean {\n return a.every((part, i) => sameKeyPart(part, b[i]));\n}\n\n/**\n * A circular picture of a person: their image, an icon, or the initials of their `name`.\n *\n * - **Image**: `image` (URL string, element or `{ src, alt }` object) or `src`. If the image fails\n * to load, the avatar falls back to `icon`, the initials or a person glyph, with their\n * background; a new image is tried again. A server-rendered image that failed before hydration\n * is detected after mount (settled without a natural size, confirmed by `decode()`).\n * - **Accessible name**: an image uses `name` (whitespace collapsed) as its `alt`, or `''` when the\n * name is blank. Without an image (none given, or it failed to load) the avatar is `role=\"img\"`\n * with `aria-label={name}` (without a name: the image slot's own `alt`) and the initials/icon\n * are hidden; without either (and without `aria-label`/`aria-labelledby`) it is decorative\n * (`aria-hidden`). A consumer `aria-label`, `aria-labelledby` or `role` (other than\n * `presentation`/`none`) puts the name on the avatar visual, and an image inside it gets\n * `alt=\"\"`. `decorative` hides it in every mode. `aria-describedby`, `aria-description` and\n * `aria-details` go to the element that carries the name: the `<img>` when a non-blank `alt`\n * names it, otherwise the avatar visual (never an `alt=\"\"` image).\n * - **Badge**: `badge` renders at the bottom end corner (logical, RTL-safe) as a sibling of the\n * avatar visual, so a `PresenceBadge` keeps its own name. With a badge, `ref`, `className` and\n * the rest props go to the outer wrapper.\n *\n * @example\n * <Avatar name=\"Jane Doe\" src=\"/jane.jpg\" badge={<PresenceBadge status=\"available\" />} />\n */\nexport const Avatar = ({\n src,\n name,\n size = 'medium',\n icon,\n image,\n badge,\n decorative = false,\n className,\n ref,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n ...rest\n}: AvatarProps) => {\n const imageSlot =\n image === null || image === undefined || typeof image === 'boolean' || image === ''\n ? undefined\n : image;\n const slotProps = readSlotProps(imageSlot);\n const imageKey = getImageKey(imageSlot, slotProps, src);\n\n // Load failure of the current image; a new image is tried again (C-HOOKS: adjust state during\n // render on prop change).\n const [failed, setFailed] = React.useState(false);\n const [prevImageKey, setPrevImageKey] = React.useState(imageKey);\n if (!sameImageKey(prevImageKey, imageKey)) {\n setPrevImageKey(imageKey);\n setFailed(false);\n }\n\n // An image that failed before hydration (a fast 404 of server-rendered markup) never reaches\n // `onError`: React attaches the listener while hydrating and does not replay the event. After\n // mount, a settled image without a natural size is broken, unless it is an SVG without one, so\n // `decode()` (which rejects for a broken image) confirms it where the browser supports it. Later\n // images, rendered by the client, report their errors through `onError`.\n const visualRef = React.useRef<HTMLSpanElement>(null);\n React.useLayoutEffect(() => {\n const img = visualRef.current?.querySelector('img');\n if (!img || !img.complete || img.naturalWidth !== 0) return;\n let active = true;\n const checkedSrc = img.getAttribute('src');\n const checkedSrcSet = img.getAttribute('srcset');\n const markFailed = () => {\n // Deferred (C-HOOKS); skipped if the image was replaced or got another source meanwhile.\n if (\n active &&\n img.isConnected &&\n img.getAttribute('src') === checkedSrc &&\n img.getAttribute('srcset') === checkedSrcSet\n ) {\n setFailed(true);\n }\n };\n if (typeof img.decode === 'function') img.decode().then(undefined, markFailed);\n else queueMicrotask(markFailed);\n return () => {\n active = false;\n };\n }, []);\n const rootVisualRef = useMergedRefs(ref, visualRef);\n\n const hasConsumerName = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n // A consumer role goes to the visual span together with the name (data-display#19), so the\n // image inside it is presentational (`alt=\"\"`), as with a consumer `aria-label`: the children\n // of `role=\"img\"` are not exposed. A presentational role (`presentation`, `none`) cannot carry\n // a name, so the image keeps it.\n const roleCarriesName = role !== undefined && role !== 'presentation' && role !== 'none';\n const normalisedName = normaliseName(name);\n const imageAlt = decorative || hasConsumerName || roleCarriesName ? '' : (normalisedName ?? '');\n const handleError: ImageErrorHandler = () => setFailed(true);\n\n // The alt text the image renders with: an element or object slot's own `alt` wins over ours. An\n // `alt` that is `undefined` (an optional `alt: user.photoAlt`, a wrapper forwarding `alt={alt}`)\n // or `null` is not given, so ours applies (the slot's own value would otherwise override it).\n const ownAlt = slotProps?.alt;\n const hasOwnAlt = ownAlt !== undefined && ownAlt !== null;\n const renderedAlt: unknown = hasOwnAlt ? ownAlt : imageAlt;\n\n // The description goes to the element that carries the name: the image itself when a non-blank\n // `alt` names it (the visual span then has no role, or a presentational one), otherwise the\n // visual span. An image with an empty `alt` is presentational, and a description on it would\n // conflict with that role.\n const description: DescriptionProps = {\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n };\n const imageIsNamed =\n !decorative &&\n !hasConsumerName &&\n !roleCarriesName &&\n typeof renderedAlt === 'string' &&\n renderedAlt.trim() !== '';\n const imageDescription: DescriptionProps = imageIsNamed ? description : {};\n\n let imageNode: React.ReactNode = null;\n if (!failed) {\n if (typeof imageSlot === 'string') {\n imageNode = (\n <img\n src={imageSlot}\n alt={imageAlt}\n {...imageDescription}\n className={IMAGE_CLASS}\n onError={handleError}\n />\n );\n } else if (imageSlot !== undefined) {\n // The slot's own props win over renderSlot's defaults, so the composed `onError`, the\n // default `alt` (when the slot gives none) and the joined `aria-describedby` (the avatar's\n // ids, then the slot's) are set on the slot itself. A slot's own `aria-description` and\n // `aria-details` win over the avatar's.\n const overrides: SlotOverrides = {\n onError: composeEventHandlers(\n slotProps?.onError as ImageErrorHandler | undefined,\n handleError,\n ),\n };\n if (!hasOwnAlt) overrides.alt = imageAlt;\n if (imageIsNamed) {\n const ownIds = slotProps?.['aria-describedby'];\n overrides['aria-describedby'] = joinIds(\n ariaDescribedBy,\n typeof ownIds === 'string' ? ownIds : undefined,\n );\n }\n const slot = React.isValidElement(imageSlot)\n ? React.cloneElement(imageSlot as React.ReactElement<SlotOverrides>, overrides)\n : slotProps\n ? { ...(imageSlot as object), ...overrides }\n : imageSlot;\n imageNode = renderSlot(slot as Slot<'img'>, 'img', IMAGE_CLASS, {\n alt: imageAlt,\n ...imageDescription,\n });\n } else if (src) {\n imageNode = (\n <img\n src={src}\n alt={imageAlt}\n {...imageDescription}\n className={IMAGE_CLASS}\n onError={handleError}\n />\n );\n }\n }\n const visualDescription = imageNode !== null && imageIsNamed ? {} : description;\n\n // A falsy icon (`icon={count && <Icon />}` with count 0) is no icon, as in 0.4, and so is a\n // collection whose items render nothing (`icon={items.map(…)}` mapping to nothing). The check\n // does not consume a generator: renderSlot still renders its items.\n const iconNode =\n imageNode === null && icon && slotRendersContent(icon)\n ? renderSlot(icon, 'span', 'inline-flex', { 'aria-hidden': true })\n : null;\n const initials = imageNode === null && iconNode === null && name ? getInitials(name) : '';\n\n let content: React.ReactNode;\n let colorClass: string | undefined;\n if (imageNode !== null) {\n content = imageNode;\n } else if (iconNode !== null) {\n content = iconNode;\n colorClass = 'bg-muted text-muted-foreground';\n } else if (initials) {\n content = <span aria-hidden=\"true\">{initials}</span>;\n colorClass = 'bg-primary text-primary-foreground';\n } else {\n content = <PersonIcon size=\"60%\" />;\n colorClass = 'bg-muted text-muted-foreground';\n }\n\n // Accessible name of the avatar visual (data-display#19, #27). With a consumer role and an\n // image it is the name the image would carry: an element or object slot's own alt, else `name`.\n // Without an image (none given, or it failed to load) it is `name`, else the slot's own alt: an\n // avatar named only by its image alt keeps that name when the image fails.\n const imageName =\n imageNode !== null && roleCarriesName && typeof renderedAlt === 'string'\n ? normaliseName(renderedAlt)\n : undefined;\n const slotAltName = typeof ownAlt === 'string' ? normaliseName(ownAlt) : undefined;\n const label =\n ariaLabel ??\n (imageNode === null\n ? (normalisedName ?? slotAltName)\n : roleCarriesName\n ? (imageName ?? normalisedName)\n : undefined);\n let a11yProps: React.HTMLAttributes<HTMLSpanElement>;\n if (decorative) {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n } else if (label !== undefined || ariaLabelledBy !== undefined) {\n a11yProps = {\n role: role ?? 'img',\n 'aria-label': label,\n 'aria-labelledby': ariaLabelledBy,\n ...visualDescription,\n };\n } else if (imageNode !== null || role !== undefined) {\n a11yProps = { role, ...visualDescription };\n } else {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n }\n\n // A falsy or empty badge (`badge={count && <CounterBadge … />}` with count 0) is no badge, as in\n // 0.4 and like `icon`: no stray \"0\" in the corner, and no wrapper taking the root props.\n const badgeNode =\n badge && slotRendersContent(badge)\n ? renderSlot(badge, 'span', 'absolute bottom-0 end-0 inline-flex')\n : null;\n const hasBadge = badgeNode !== null;\n\n const visualClassName = cn(\n 'inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-bold',\n avatarSizeClasses[size],\n colorClass,\n );\n\n if (!hasBadge) {\n return (\n <span ref={rootVisualRef} {...a11yProps} {...rest} className={cn(visualClassName, className)}>\n {content}\n </span>\n );\n }\n\n return (\n <span ref={ref} {...rest} className={cn('relative inline-flex shrink-0', className)}>\n <span ref={visualRef} {...a11yProps} className={visualClassName}>\n {content}\n </span>\n {badgeNode}\n </span>\n );\n};\n\nAvatar.displayName = 'Avatar';\n"],"mappings":";;;;;;;;;;AAuEA,IAAaC,IAAoD;CAC/D,eAAe;CACf,OAAO;CACP,QAAQ;CACR,OAAO;CACP,eAAe;AACjB,GAEMC,IAAc;AAGpB,SAASC,GAAY,GAAsB;CACzC,IAAMC,IAAQC,EAAK,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAIrD,OAHID,EAAM,WAAW,IAAU,OACjB,MAAM,KAAKA,EAAM,EAAG,CAAC,CAAC,MAAM,OAC7BA,EAAM,SAAS,IAAK,MAAM,KAAKA,EAAMA,EAAM,SAAS,EAAG,CAAC,CAAC,MAAM,KAAM,IAAA,CAC5D,YAAY;AACpC;AAGA,SAASE,EAAc,GAA8C;CACnE,OAAOD,GAAM,KAAK,CAAC,CAAC,QAAQ,QAAQ,GAAG,KAAK,KAAA;AAC9C;AA2BA,SAASE,EAAc,GAA2C;CAChE,IAAIP,EAAM,eAAeQ,CAAI,GAAG,OAAOA,EAAK;CAC5C,IAAI,OAAOA,KAAS,YAAYA,KAAiB,EAAE,OAAO,YAAYA,IACpE,OAAOA;AAGX;AASA,SAASC,GACP,GACA,GACA,GACU;CACV,IAAIC,MAAc,KAAA,GAAW,OAAO;EAAC,KAAA;EAAW,KAAA;EAAWC,KAAO,KAAA;EAAW,KAAA;CAAS;CACtF,IAAI,OAAOD,KAAc,UAAU,OAAO;EAAC,KAAA;EAAW,KAAA;EAAWA;EAAW,KAAA;CAAS;CACrF,IAAME,IAAUZ,EAAM,eAAeU,CAAS,IAAIA,IAAY,KAAA;CAC9D,OAAO;EAACE,GAAS;EAAMA,GAAS;EAAKC,GAAW;EAAKA,GAAW;CAAM;AACxE;AAEA,SAASC,EAAc,GAAkD;CACvE,IAAI,OAAOC,KAAU,aAAYA,GAAgB,OAAO;CACxD,IAAMC,IAAiB,OAAO,eAAeD,CAAK;CAClD,OAAOC,MAAU,OAAO,aAAaA,MAAU;AACjD;AAGA,SAASC,EAAY,GAAY,GAAqB;CACpD,IAAI,OAAO,GAAGC,GAAGC,CAAC,GAAG,OAAO;CAC5B,IAAI,CAACL,EAAcI,CAAC,KAAK,CAACJ,EAAcK,CAAC,GAAG,OAAO;CACnD,IAAMC,IAAO,OAAO,KAAKF,CAAC;CAC1B,OACEE,EAAK,WAAW,OAAO,KAAKD,CAAC,CAAC,CAAC,UAC/BC,EAAK,OAAO,MAAM,OAAO,UAAU,eAAe,KAAKD,GAAGE,CAAC,KAAK,OAAO,GAAGH,EAAEG,IAAIF,EAAEE,EAAE,CAAC;AAEzF;AAEA,SAASC,GAAa,GAAa,GAAsB;CACvD,OAAOJ,EAAE,OAAO,GAAM,MAAMD,EAAYM,GAAMJ,EAAEK,EAAE,CAAC;AACrD;AAyBA,IAAaC,KAAU,EACrB,QACA,SACA,UAAO,UACP,SACA,UACA,UACA,gBAAa,IACb,cACA,QACA,SACA,cAAcC,GACd,mBAAmBC,GACnB,oBAAoBC,GACpB,oBAAoBC,IACpB,gBAAgBC,IAChB,GAAGC,QACc;CACjB,IAAMrB,IACJsB,KAAU,QAA+B,OAAOA,KAAU,aAAaA,MAAU,KAC7E,KAAA,IACAA,GACAnB,IAAYN,EAAcG,CAAS,GACnCuB,IAAWxB,GAAYC,GAAWG,GAAWF,CAAG,GAIhD,CAACuB,IAAQC,KAAanC,EAAM,SAAS,EAAK,GAC1C,CAACoC,GAAcC,KAAmBrC,EAAM,SAASiC,CAAQ;CAC/D,AAAKX,GAAac,GAAcH,CAAQ,MACtCI,EAAgBJ,CAAQ,GACxBE,EAAU,EAAK;CAQjB,IAAMG,IAAYtC,EAAM,OAAwB,IAAI;CACpD,EAAM,sBAAsB;EAC1B,IAAMuC,IAAMD,EAAU,SAAS,cAAc,KAAK;EAClD,IAAI,CAACC,KAAO,CAACA,EAAI,YAAYA,EAAI,iBAAiB,GAAG;EACrD,IAAIC,IAAS,IACPC,IAAaF,EAAI,aAAa,KAAK,GACnCG,IAAgBH,EAAI,aAAa,QAAQ,GACzCI,UAAmB;GAEvB,AACEH,KACAD,EAAI,eACJA,EAAI,aAAa,KAAK,MAAME,KAC5BF,EAAI,aAAa,QAAQ,MAAMG,KAE/BP,EAAU,EAAI;EAElB;EAGA,OAFI,OAAOI,EAAI,UAAW,aAAYA,EAAI,OAAO,CAAC,CAAC,KAAK,KAAA,GAAWI,CAAU,IACxE,eAAeA,CAAU,SACjB;GACX,IAAS;EACX;CACF,GAAG,CAAC,CAAC;CACL,IAAMC,IAAgBC,EAAcC,GAAKR,CAAS,GAE5CS,IAAkBrB,MAAc,KAAA,KAAaC,MAAmB,KAAA,GAKhEqB,IAAkBC,MAAS,KAAA,KAAaA,MAAS,kBAAkBA,MAAS,QAC5EC,IAAiB5C,EAAcD,CAAI,GACnC8C,IAAWC,KAAcL,KAAmBC,IAAkB,KAAME,KAAkB,IACtFG,UAAuClB,EAAU,EAAI,GAKrDmB,IAASzC,GAAW,KACpB0C,IAAYD,KAAmC,MAC/CE,IAAuBD,IAAYD,IAASH,GAM5CM,IAAgC;EACpC,oBAAoB7B;EACpB,oBAAoBC;EACpB,gBAAgBC;CAClB,GACM4B,IACJ,CAACN,KACD,CAACL,KACD,CAACC,KACD,OAAOQ,KAAgB,YACvBA,EAAY,KAAK,MAAM,IACnBG,IAAqCD,IAAeD,IAAc,CAAC,GAErEG,IAA6B;CACjC,IAAI,CAAC1B,IAAQ;EACX,IAAI,OAAOxB,KAAc,UACvB,IACE,kBAAC,OAAD;GACE,KAAKA;GACL,KAAKyC;GACL,GAAIQ;GACJ,WAAWzD;GACX,SAASmD;EACV,CAAA;OAEE,IAAI3C,MAAc,KAAA,GAAW;GAKlC,IAAMmD,IAA2B,EAC/B,SAASC,EACPjD,GAAW,SACXwC,CACF,EACF;GAEA,IADKE,MAAW,EAAU,MAAMJ,IAC5BO,GAAc;IAChB,IAAMK,IAASlD,IAAY;IAC3B,EAAU,sBAAsBmD,GAC9BpC,GACA,OAAOmC,KAAW,WAAWA,IAAS,KAAA,CACxC;GACF;GACA,IAAMvD,IAAOR,EAAM,eAAeU,CAAS,IACvCV,EAAM,aAAaU,GAAgDmD,CAAS,IAC5EhD,IACE;IAAE,GAAIH;IAAsB,GAAGmD;GAAU,IACzCnD;GACN,IAAYuD,EAAWzD,GAAqB,OAAON,GAAa;IAC9D,KAAKiD;IACL,GAAGQ;GACL,CAAC;EACH,OAAO,AAAIhD,MACT,IACE,kBAAC,OAAD;GACO;GACL,KAAKwC;GACL,GAAIQ;GACJ,WAAWzD;GACX,SAASmD;EACV,CAAA;CAGP;CACA,IAAMa,IAAoBN,MAAc,QAAQF,IAAe,CAAC,IAAID,GAK9DU,IACJP,MAAc,QAAQQ,KAAQC,EAAmBD,CAAI,IACjDH,EAAWG,GAAM,QAAQ,eAAe,EAAE,eAAe,GAAK,CAAC,IAC/D,MACAE,IAAWV,MAAc,QAAQO,MAAa,QAAQ9D,IAAOF,GAAYE,CAAI,IAAI,IAEnFkE,GACAC;CACJ,AAAIZ,MAAc,OAEPO,MAAa,OAGbG,KACT,IAAU,kBAAC,QAAD;EAAM,eAAY;EAAQ,UAAAA;CAAe,CAAA,GACnD,IAAa,yCAEb,IAAU,kBAACG,IAAD,EAAY,MAAK,MAAO,CAAA,GAClC,IAAa,qCAPb,IAAUN,GACV,IAAa,oCAHb,IAAUP;CAgBZ,IAAMc,KACJd,MAAc,QAAQZ,KAAmB,OAAOQ,KAAgB,WAC5DlD,EAAckD,CAAW,IACzB,KAAA,GACAmB,KAAc,OAAOrB,KAAW,WAAWhD,EAAcgD,CAAM,IAAI,KAAA,GACnEsB,IACJlD,MACCkC,MAAc,OACVV,KAAkByB,KACnB3B,IACG0B,MAAaxB,IACd,KAAA,IACJ2B;CACJ,AAYEA,IAZEzB,IACU;EAAE,eAAe;EAAM,GAAGc;CAAkB,IAC/CU,MAAU,KAAA,KAAajD,MAAmB,KAAA,IACvC;EACV,MAAMsB,KAAQ;EACd,cAAc2B;EACd,mBAAmBjD;EACnB,GAAGuC;CACL,IACSN,MAAc,QAAQX,MAAS,KAAA,IAC5B;EAAE;EAAM,GAAGiB;CAAkB,IAE7B;EAAE,eAAe;EAAM,GAAGA;CAAkB;CAK1D,IAAMY,IACJC,KAASV,EAAmBU,CAAK,IAC7Bd,EAAWc,GAAO,QAAQ,qCAAqC,IAC/D,MACAC,KAAWF,MAAc,MAEzBG,IAAkBC,EACtB,2FACAjF,EAAkBkF,IAClBX,CACF;CAUA,OARKQ,KASH,mBAAC,QAAD;EAAW;EAAK,GAAIjD;EAAM,WAAWmD,EAAG,iCAAiCE,CAAS;EAAlF,UAAA,CACE,kBAAC,QAAD;GAAM,KAAK9C;GAAW,GAAIuC;GAAW,WAAWI;GAC7C,UAAAV;EACG,CAAA,GACLO,CACG;MAZJ,kBAAC,QAAD;EAAM,KAAKlC;EAAe,GAAIiC;EAAW,GAAI9C;EAAM,WAAWmD,EAAGD,GAAiBG,CAAS;EACxF,UAAAb;CACG,CAAA;AAYZ;AAEA,EAAO,cAAc"}
1
+ {"version":3,"file":"Avatar.mjs","names":["React","avatarSizeClasses","IMAGE_CLASS","getInitials","parts","name","normaliseName","readSlotProps","slot","getImageKey","imageSlot","src","element","slotProps","isPlainObject","value","proto","sameKeyPart","a","b","keys","k","sameImageKey","part","i","Avatar","ariaLabel","ariaLabelledBy","ariaDescribedBy","ariaDescription","ariaDetails","rest","image","imageKey","failed","setFailed","prevImageKey","setPrevImageKey","visualRef","img","active","checkedSrc","checkedSrcSet","markFailed","rootVisualRef","useMergedRefs","ref","hasConsumerName","roleCarriesName","role","normalisedName","imageAlt","decorative","handleError","ownAlt","hasOwnAlt","renderedAlt","description","imageIsNamed","imageDescription","imageNode","cn","avatarClassNames","overrides","composeEventHandlers","ownIds","joinIds","renderSlot","visualDescription","iconNode","icon","slotRendersContent","initials","content","colorClass","PersonIcon","imageName","slotAltName","label","a11yProps","badgeNode","badge","hasBadge","visualClassName","size","className"],"sources":["../../../src/components/data-display/Avatar.tsx"],"sourcesContent":["import * as React from 'react';\nimport { joinIds } from '../../lib/aria';\nimport { cn } from '../../lib/cn';\nimport { avatarClassNames } from '../../lib/classNames';\nimport { composeEventHandlers } from '../../lib/composeEventHandlers';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { PersonIcon } from '../../lib/icons';\nimport type { Size, Slot } from '../../lib/types';\nimport { renderSlot, slotRendersContent } from '../../lib/slot';\n\n/** Properties for the Avatar component. */\nexport interface AvatarProps extends React.HTMLAttributes<HTMLSpanElement> {\n /**\n * URL of the avatar image. When it fails to load (also before hydration, for server-rendered\n * markup), the avatar falls back to its icon or initials; a new `src` is tried again.\n */\n src?: string;\n /**\n * Full name of the person. It names the avatar for assistive technology (the image `alt`; in\n * initials and icon mode, or with a consumer `role`, the avatar's `aria-label`, with\n * `role=\"img\"` unless `role` is given) and generates the initials shown when there is no image.\n */\n name?: string;\n /** Size of the avatar.\n * @default 'medium'\n */\n size?: Size;\n /**\n * Icon shown instead of the initials when there is no image. Rendered in an `aria-hidden`\n * `<span>` (a slot object can override its attributes). A falsy value (`null`, `false`, `''`,\n * `0`, `NaN`, e.g. `icon={count && <Icon />}`) counts as no icon, as in 0.4, and so does an\n * array, `Set` or generator whose items render nothing. Without `icon` and without a usable\n * `name`, a generic person glyph is shown.\n */\n icon?: Slot<'span'>;\n /**\n * Custom image. Accepts a URL string (treated like `src`), an element (rendered as the image\n * itself, e.g. `<img>` or a framework image component) or an object of `<img>` attributes\n * (`{ src, alt, … }`). Takes precedence over `src`; its `alt` defaults to `name` (an `alt` that\n * is `undefined` counts as not given). When its alt names the avatar, the avatar's\n * `aria-describedby` ids are joined with the slot's own, while the slot's own `aria-description`\n * and `aria-details` win over the avatar's. When the image fails to load, an avatar without a\n * `name` keeps the name of the slot's own `alt`. After a load failure, a new image is tried\n * again: another `src` or `srcSet` of any type (an equal inline object is the same image), or\n * for an element slot another component type or `key`.\n */\n image?: Slot<'img'>;\n /**\n * Badge shown at the bottom end corner, e.g. `<PresenceBadge status=\"busy\" />`. With a badge\n * the avatar is wrapped in an outer `<span>` that receives `ref`, `className` and the other\n * props; `role`, `aria-label`, `aria-labelledby`, `aria-describedby`, `aria-description` and\n * `aria-details` stay on the avatar visual, so the badge keeps its own accessible name. A falsy\n * value (`null`, `false`, `''`, `0`, `NaN`, e.g. `badge={count && <CounterBadge count={count} />}`)\n * counts as no badge, as in 0.4, and so does an array, `Set` or generator whose items render\n * nothing: the avatar then renders without the wrapper.\n */\n badge?: Slot<'span'>;\n /**\n * Hides the avatar visual from assistive technology (`aria-hidden`, empty image `alt`, no\n * `role=\"img\"`). Use it when the name is already shown next to the avatar (Persona). A badge\n * stays exposed.\n * @default false\n */\n decorative?: boolean;\n /** Ref to the root `<span>`: the avatar itself, or the badge wrapper when `badge` is set. */\n ref?: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * Width, height and text size classes of the avatar per `size`. Module-only (not in the barrel):\n * AvatarGroup sizes its overflow button with the same classes, so the two always match.\n */\nexport const avatarSizeClasses: Readonly<Record<Size, string>> = {\n 'extra-small': 'w-6 h-6 text-[10px]', // 24px\n small: 'w-8 h-8 text-xs', // 32px\n medium: 'w-10 h-10 text-sm', // 40px\n large: 'w-12 h-12 text-base', // 48px\n 'extra-large': 'w-14 h-14 text-lg', // 56px\n};\n\nconst IMAGE_CLASS = 'w-full h-full object-cover';\n\n/** First letter of the first and last word, upper-cased; `''` for a blank name. */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n const first = Array.from(parts[0]!)[0] ?? '';\n const last = parts.length > 1 ? (Array.from(parts[parts.length - 1]!)[0] ?? '') : '';\n return (first + last).toUpperCase();\n}\n\n/** A name with its whitespace collapsed, or `undefined` when it is blank. */\nfunction normaliseName(name: string | undefined): string | undefined {\n return name?.trim().replace(/\\s+/g, ' ') || undefined;\n}\n\ntype ImageErrorHandler = React.ReactEventHandler<HTMLImageElement>;\n\n/** Description props that go to the element carrying the avatar's name (data-display#19). */\ntype DescriptionProps = Pick<\n React.HTMLAttributes<HTMLElement>,\n 'aria-describedby' | 'aria-description' | 'aria-details'\n>;\n\n/** Props the avatar sets on an element or object image slot, over the slot's own. */\ninterface SlotOverrides {\n onError: ImageErrorHandler;\n alt?: string;\n 'aria-describedby'?: string;\n}\n\n/** The props of an element or object image slot that the avatar reads. */\ninterface ImageSlotProps {\n src?: unknown;\n srcSet?: unknown;\n alt?: unknown;\n onError?: unknown;\n 'aria-describedby'?: unknown;\n}\n\n/** The props of an element or object image slot (to read its `src`, `alt` and handlers). */\nfunction readSlotProps(slot: unknown): ImageSlotProps | undefined {\n if (React.isValidElement(slot)) return slot.props as ImageSlotProps;\n if (typeof slot === 'object' && slot !== null && !(Symbol.iterator in slot)) {\n return slot as ImageSlotProps;\n }\n return undefined;\n}\n\n/**\n * What identifies the image, so a load failure is kept for it and a new image is tried again\n * (data-display#4): the `src` and `srcSet`, of any type (a framework image component may take a\n * static-import object), and for an element slot also its type and `key`.\n */\ntype ImageKey = readonly [type: unknown, key: unknown, src: unknown, srcSet: unknown];\n\nfunction getImageKey(\n imageSlot: unknown,\n slotProps: ImageSlotProps | undefined,\n src: string | undefined,\n): ImageKey {\n if (imageSlot === undefined) return [undefined, undefined, src || undefined, undefined];\n if (typeof imageSlot === 'string') return [undefined, undefined, imageSlot, undefined];\n const element = React.isValidElement(imageSlot) ? imageSlot : undefined;\n return [element?.type, element?.key, slotProps?.src, slotProps?.srcSet];\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n const proto: unknown = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n\n/** Same value, or plain objects with the same entries (an inline `src={{ … }}` on each render). */\nfunction sameKeyPart(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && Object.is(a[k], b[k]))\n );\n}\n\nfunction sameImageKey(a: ImageKey, b: ImageKey): boolean {\n return a.every((part, i) => sameKeyPart(part, b[i]));\n}\n\n/**\n * A circular picture of a person: their image, an icon, or the initials of their `name`.\n *\n * - **Image**: `image` (URL string, element or `{ src, alt }` object) or `src`. If the image fails\n * to load, the avatar falls back to `icon`, the initials or a person glyph, with their\n * background; a new image is tried again. A server-rendered image that failed before hydration\n * is detected after mount (settled without a natural size, confirmed by `decode()`).\n * - **Accessible name**: an image uses `name` (whitespace collapsed) as its `alt`, or `''` when the\n * name is blank. Without an image (none given, or it failed to load) the avatar is `role=\"img\"`\n * with `aria-label={name}` (without a name: the image slot's own `alt`) and the initials/icon\n * are hidden; without either (and without `aria-label`/`aria-labelledby`) it is decorative\n * (`aria-hidden`). A consumer `aria-label`, `aria-labelledby` or `role` (other than\n * `presentation`/`none`) puts the name on the avatar visual, and an image inside it gets\n * `alt=\"\"`. `decorative` hides it in every mode. `aria-describedby`, `aria-description` and\n * `aria-details` go to the element that carries the name: the `<img>` when a non-blank `alt`\n * names it, otherwise the avatar visual (never an `alt=\"\"` image).\n * - **Badge**: `badge` renders at the bottom end corner (logical, RTL-safe) as a sibling of the\n * avatar visual, so a `PresenceBadge` keeps its own name. With a badge, `ref`, `className` and\n * the rest props go to the outer wrapper.\n *\n * @example\n * <Avatar name=\"Jane Doe\" src=\"/jane.jpg\" badge={<PresenceBadge status=\"available\" />} />\n */\nexport const Avatar = ({\n src,\n name,\n size = 'medium',\n icon,\n image,\n badge,\n decorative = false,\n className,\n ref,\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n ...rest\n}: AvatarProps) => {\n const imageSlot =\n image === null || image === undefined || typeof image === 'boolean' || image === ''\n ? undefined\n : image;\n const slotProps = readSlotProps(imageSlot);\n const imageKey = getImageKey(imageSlot, slotProps, src);\n\n // Load failure of the current image; a new image is tried again (C-HOOKS: adjust state during\n // render on prop change).\n const [failed, setFailed] = React.useState(false);\n const [prevImageKey, setPrevImageKey] = React.useState(imageKey);\n if (!sameImageKey(prevImageKey, imageKey)) {\n setPrevImageKey(imageKey);\n setFailed(false);\n }\n\n // An image that failed before hydration (a fast 404 of server-rendered markup) never reaches\n // `onError`: React attaches the listener while hydrating and does not replay the event. After\n // mount, a settled image without a natural size is broken, unless it is an SVG without one, so\n // `decode()` (which rejects for a broken image) confirms it where the browser supports it. Later\n // images, rendered by the client, report their errors through `onError`.\n const visualRef = React.useRef<HTMLSpanElement>(null);\n React.useLayoutEffect(() => {\n const img = visualRef.current?.querySelector('img');\n if (!img || !img.complete || img.naturalWidth !== 0) return;\n let active = true;\n const checkedSrc = img.getAttribute('src');\n const checkedSrcSet = img.getAttribute('srcset');\n const markFailed = () => {\n // Deferred (C-HOOKS); skipped if the image was replaced or got another source meanwhile.\n if (\n active &&\n img.isConnected &&\n img.getAttribute('src') === checkedSrc &&\n img.getAttribute('srcset') === checkedSrcSet\n ) {\n setFailed(true);\n }\n };\n if (typeof img.decode === 'function') img.decode().then(undefined, markFailed);\n else queueMicrotask(markFailed);\n return () => {\n active = false;\n };\n }, []);\n const rootVisualRef = useMergedRefs(ref, visualRef);\n\n const hasConsumerName = ariaLabel !== undefined || ariaLabelledBy !== undefined;\n // A consumer role goes to the visual span together with the name (data-display#19), so the\n // image inside it is presentational (`alt=\"\"`), as with a consumer `aria-label`: the children\n // of `role=\"img\"` are not exposed. A presentational role (`presentation`, `none`) cannot carry\n // a name, so the image keeps it.\n const roleCarriesName = role !== undefined && role !== 'presentation' && role !== 'none';\n const normalisedName = normaliseName(name);\n const imageAlt = decorative || hasConsumerName || roleCarriesName ? '' : (normalisedName ?? '');\n const handleError: ImageErrorHandler = () => setFailed(true);\n\n // The alt text the image renders with: an element or object slot's own `alt` wins over ours. An\n // `alt` that is `undefined` (an optional `alt: user.photoAlt`, a wrapper forwarding `alt={alt}`)\n // or `null` is not given, so ours applies (the slot's own value would otherwise override it).\n const ownAlt = slotProps?.alt;\n const hasOwnAlt = ownAlt !== undefined && ownAlt !== null;\n const renderedAlt: unknown = hasOwnAlt ? ownAlt : imageAlt;\n\n // The description goes to the element that carries the name: the image itself when a non-blank\n // `alt` names it (the visual span then has no role, or a presentational one), otherwise the\n // visual span. An image with an empty `alt` is presentational, and a description on it would\n // conflict with that role.\n const description: DescriptionProps = {\n 'aria-describedby': ariaDescribedBy,\n 'aria-description': ariaDescription,\n 'aria-details': ariaDetails,\n };\n const imageIsNamed =\n !decorative &&\n !hasConsumerName &&\n !roleCarriesName &&\n typeof renderedAlt === 'string' &&\n renderedAlt.trim() !== '';\n const imageDescription: DescriptionProps = imageIsNamed ? description : {};\n\n let imageNode: React.ReactNode = null;\n if (!failed) {\n if (typeof imageSlot === 'string') {\n imageNode = (\n <img\n src={imageSlot}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n } else if (imageSlot !== undefined) {\n // The slot's own props win over renderSlot's defaults, so the composed `onError`, the\n // default `alt` (when the slot gives none) and the joined `aria-describedby` (the avatar's\n // ids, then the slot's) are set on the slot itself. A slot's own `aria-description` and\n // `aria-details` win over the avatar's.\n const overrides: SlotOverrides = {\n onError: composeEventHandlers(\n slotProps?.onError as ImageErrorHandler | undefined,\n handleError,\n ),\n };\n if (!hasOwnAlt) overrides.alt = imageAlt;\n if (imageIsNamed) {\n const ownIds = slotProps?.['aria-describedby'];\n overrides['aria-describedby'] = joinIds(\n ariaDescribedBy,\n typeof ownIds === 'string' ? ownIds : undefined,\n );\n }\n const slot = React.isValidElement(imageSlot)\n ? React.cloneElement(imageSlot as React.ReactElement<SlotOverrides>, overrides)\n : slotProps\n ? { ...(imageSlot as object), ...overrides }\n : imageSlot;\n imageNode = renderSlot(slot as Slot<'img'>, 'img', cn(avatarClassNames.image, IMAGE_CLASS), {\n alt: imageAlt,\n ...imageDescription,\n });\n } else if (src) {\n imageNode = (\n <img\n src={src}\n alt={imageAlt}\n {...imageDescription}\n className={cn(avatarClassNames.image, IMAGE_CLASS)}\n onError={handleError}\n />\n );\n }\n }\n const visualDescription = imageNode !== null && imageIsNamed ? {} : description;\n\n // A falsy icon (`icon={count && <Icon />}` with count 0) is no icon, as in 0.4, and so is a\n // collection whose items render nothing (`icon={items.map(…)}` mapping to nothing). The check\n // does not consume a generator: renderSlot still renders its items.\n const iconNode =\n imageNode === null && icon && slotRendersContent(icon)\n ? renderSlot(icon, 'span', cn(avatarClassNames.icon, 'inline-flex'), { 'aria-hidden': true })\n : null;\n const initials = imageNode === null && iconNode === null && name ? getInitials(name) : '';\n\n let content: React.ReactNode;\n let colorClass: string | undefined;\n if (imageNode !== null) {\n content = imageNode;\n } else if (iconNode !== null) {\n content = iconNode;\n colorClass = 'bg-muted text-muted-foreground';\n } else if (initials) {\n content = (\n <span aria-hidden=\"true\" className={avatarClassNames.initials}>\n {initials}\n </span>\n );\n colorClass = 'bg-primary text-primary-foreground';\n } else {\n content = <PersonIcon size=\"60%\" className={avatarClassNames.icon} />;\n colorClass = 'bg-muted text-muted-foreground';\n }\n\n // Accessible name of the avatar visual (data-display#19, #27). With a consumer role and an\n // image it is the name the image would carry: an element or object slot's own alt, else `name`.\n // Without an image (none given, or it failed to load) it is `name`, else the slot's own alt: an\n // avatar named only by its image alt keeps that name when the image fails.\n const imageName =\n imageNode !== null && roleCarriesName && typeof renderedAlt === 'string'\n ? normaliseName(renderedAlt)\n : undefined;\n const slotAltName = typeof ownAlt === 'string' ? normaliseName(ownAlt) : undefined;\n const label =\n ariaLabel ??\n (imageNode === null\n ? (normalisedName ?? slotAltName)\n : roleCarriesName\n ? (imageName ?? normalisedName)\n : undefined);\n let a11yProps: React.HTMLAttributes<HTMLSpanElement>;\n if (decorative) {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n } else if (label !== undefined || ariaLabelledBy !== undefined) {\n a11yProps = {\n role: role ?? 'img',\n 'aria-label': label,\n 'aria-labelledby': ariaLabelledBy,\n ...visualDescription,\n };\n } else if (imageNode !== null || role !== undefined) {\n a11yProps = { role, ...visualDescription };\n } else {\n a11yProps = { 'aria-hidden': true, ...visualDescription };\n }\n\n // A falsy or empty badge (`badge={count && <CounterBadge … />}` with count 0) is no badge, as in\n // 0.4 and like `icon`: no stray \"0\" in the corner, and no wrapper taking the root props.\n const badgeNode =\n badge && slotRendersContent(badge)\n ? renderSlot(badge, 'span', cn(avatarClassNames.badge, 'absolute bottom-0 end-0 inline-flex'))\n : null;\n const hasBadge = badgeNode !== null;\n\n const visualClassName = cn(\n avatarClassNames.visual,\n 'inline-flex items-center justify-center rounded-full overflow-hidden shrink-0 font-bold',\n avatarSizeClasses[size],\n colorClass,\n );\n\n if (!hasBadge) {\n return (\n <span\n ref={rootVisualRef}\n {...a11yProps}\n {...rest}\n className={cn(avatarClassNames.root, visualClassName, className)}\n >\n {content}\n </span>\n );\n }\n\n return (\n <span\n ref={ref}\n {...rest}\n className={cn(avatarClassNames.root, 'relative inline-flex shrink-0', className)}\n >\n <span ref={visualRef} {...a11yProps} className={visualClassName}>\n {content}\n </span>\n {badgeNode}\n </span>\n );\n};\n\nAvatar.displayName = 'Avatar';\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/children.cjs"),r=require("../../lib/dev.cjs"),i=require("../../hooks/useId.cjs"),a=require("../../lib/focus.cjs"),o=require("../../hooks/useMergedRefs.cjs"),s=require("../../lib/layers.cjs"),c=require("../../hooks/useDismiss.cjs"),l=require("../portal/Portal.cjs");require("../../lib/styles.cjs");const u=require("../../hooks/usePopupPosition.cjs"),d=require("../../hooks/useRestoreFocus.cjs"),f=require("./Avatar.cjs");let p=require("react");p=e.__toESM(p,1);let m=require("react/jsx-runtime");var h=`Unnamed member`,g=e=>typeof e==`string`&&e.trim()!==``;function _(e){if(typeof e==`string`||typeof e==`number`)return String(e).trim()||void 0;if(!n.isElementOfType(e,f.Avatar))return;let{decorative:t,name:r,"aria-label":i,"aria-labelledby":a}=e.props;if(t||!g(a)){for(let e of[i,r])if(g(e))return e.trim()}}function v(e){if(typeof e==`string`||typeof e==`number`)return!0;if(!n.isElementOfType(e,f.Avatar))return!1;let{decorative:t,image:r,"aria-labelledby":i}=e.props;if(t)return!0;if(g(i))return!1;let a=p.isValidElement(r)?r.props:r;return!g(typeof a==`object`&&a&&`alt`in a?a.alt:void 0)}var y=({max:e,size:g=`medium`,overflowLabel:y,unnamedMemberLabel:b=h,className:x,children:S,ref:C,"aria-label":w,"aria-labelledby":T,...E})=>{let D=n.flattenChildren(S),O=e===void 0||Number.isNaN(e)?1/0:Math.max(0,Math.floor(e)),k=D.slice(0,O),A=D.slice(k.length),j=A.length,M=w===void 0&&T===void 0;p.useEffect(()=>{M&&r.warnOnce(`AvatarGroup:unnamed`,"AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.")},[M]);let N=i.useId(`avatar-group-overflow`),[P,F]=p.useState(!1);P&&j===0&&F(!1);let I=P&&j>0,L=typeof y==`function`?y(j):y??`${j} more`,R=p.useContext(c.DismissLayerContext),z=p.useRef(null),B=p.useRef(null),[V,H]=p.useState(null),{setReference:U,setFloating:W,floatingProps:G}=u.usePopupPosition({open:I,side:`bottom`,align:`start`}),{layerId:K}=c.useDismiss({open:I,onDismiss:()=>F(!1),refs:[z,B],anchorRef:z,focusOutside:!0});d.useRestoreFocus({enabled:I,container:V,triggerRef:z,onlyIfFocusInside:!0});let q=o.useMergedRefs(z,U),J=o.useMergedRefs(B,W,H);p.useEffect(()=>{I&&V&&V.focus({preventScroll:!0})},[I,V]);let Y=e=>{let t=z.current;if(e.key!==`Tab`||e.defaultPrevented||!t)return;let n=e.currentTarget,r=e.target;if(!r||typeof r.nodeType!=`number`||!n.contains(r))return;let i=a.getTabbableElements(n),o=i.findIndex(e=>e.contains(r))+(e.shiftKey?-1:1);if(o>=0&&o<i.length){e.preventDefault(),i[o].focus();return}let c=t;if(!e.shiftKey){let e=a.getTabbableElements(t.ownerDocument.body).filter(e=>!n.contains(e)),r=e.indexOf(t);c=r===-1?t:e[r+1],c&&R!==null&&!s.isInsideLayerTree(R,c)&&(c=void 0)}F(!1),c&&(e.preventDefault(),c.focus())},X=I&&A.some(({node:e})=>_(e)===void 0&&v(e));return p.useEffect(()=>{X&&r.warnOnce(`AvatarGroup:unnamed-member`,`AvatarGroup: a hidden member has no accessible name and is listed as "${b}". Give each member a \`name\` or \`aria-label\`.`)},[X,b]),(0,m.jsxs)(`div`,{ref:C,role:`group`,"aria-label":w,"aria-labelledby":T,...E,className:t.cn(`inline-flex items-center`,x),children:[k.map(({key:e,node:n},r)=>(0,m.jsx)(`span`,{className:t.cn(`inline-flex rounded-full ring-2 ring-background`,r>0&&`-ms-2`),children:n},e)),j>0&&(0,m.jsxs)(`button`,{ref:q,type:`button`,"aria-label":L,"aria-haspopup":`dialog`,"aria-expanded":I,"aria-controls":I?N:void 0,className:t.cn(`inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold`,`bg-muted text-muted-foreground ring-2 ring-background`,`not-disabled:not-aria-disabled:hover:bg-subtle-pressed`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`,k.length>0&&`-ms-2`,f.avatarSizeClasses[g]),onClick:()=>F(e=>!e),children:[`+`,j]}),I&&(0,m.jsx)(l.Portal,{layerId:K,children:(0,m.jsx)(`div`,{ref:J,id:N,role:`dialog`,"aria-label":L,tabIndex:-1,"data-state":`open`,...G,className:t.cn(`min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`),onKeyDown:Y,children:(0,m.jsx)(`ul`,{className:`m-0 flex list-none flex-col gap-1 p-0`,children:A.map(({key:e,node:t})=>(0,m.jsx)(`li`,{className:`px-1 py-0.5`,children:_(t)??(v(t)?b:t)},e))})})})]})};y.displayName=`AvatarGroup`,exports.AvatarGroup=y;
1
+ "use client";const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../lib/cn.cjs"),n=require("../../lib/children.cjs"),r=require("../../lib/dev.cjs"),i=require("../../lib/classNames.cjs"),a=require("../../hooks/useId.cjs"),o=require("../../lib/focus.cjs"),s=require("../../hooks/useMergedRefs.cjs"),c=require("../../lib/layers.cjs"),l=require("../../hooks/useDismiss.cjs"),u=require("../portal/Portal.cjs");require("../../lib/styles.cjs");const d=require("../../hooks/usePopupPosition.cjs"),f=require("../../hooks/useRestoreFocus.cjs"),p=require("./Avatar.cjs");let m=require("react");m=e.__toESM(m,1);let h=require("react/jsx-runtime");var g=`Unnamed member`,_=e=>typeof e==`string`&&e.trim()!==``;function v(e){if(typeof e==`string`||typeof e==`number`)return String(e).trim()||void 0;if(!n.isElementOfType(e,p.Avatar))return;let{decorative:t,name:r,"aria-label":i,"aria-labelledby":a}=e.props;if(t||!_(a)){for(let e of[i,r])if(_(e))return e.trim()}}function y(e){if(typeof e==`string`||typeof e==`number`)return!0;if(!n.isElementOfType(e,p.Avatar))return!1;let{decorative:t,image:r,"aria-labelledby":i}=e.props;if(t)return!0;if(_(i))return!1;let a=m.isValidElement(r)?r.props:r;return!_(typeof a==`object`&&a&&`alt`in a?a.alt:void 0)}var b=({max:e,size:_=`medium`,overflowLabel:b,unnamedMemberLabel:x=g,className:S,children:C,ref:w,"aria-label":T,"aria-labelledby":E,...D})=>{let O=n.flattenChildren(C),k=e===void 0||Number.isNaN(e)?1/0:Math.max(0,Math.floor(e)),A=O.slice(0,k),j=O.slice(A.length),M=j.length,N=T===void 0&&E===void 0;m.useEffect(()=>{N&&r.warnOnce(`AvatarGroup:unnamed`,"AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.")},[N]);let P=a.useId(`avatar-group-overflow`),[F,I]=m.useState(!1);F&&M===0&&I(!1);let L=F&&M>0,R=typeof b==`function`?b(M):b??`${M} more`,z=m.useContext(l.DismissLayerContext),B=m.useRef(null),V=m.useRef(null),[H,U]=m.useState(null),{setReference:W,setFloating:G,floatingProps:K}=d.usePopupPosition({open:L,side:`bottom`,align:`start`}),{layerId:q}=l.useDismiss({open:L,onDismiss:()=>I(!1),refs:[B,V],anchorRef:B,focusOutside:!0});f.useRestoreFocus({enabled:L,container:H,triggerRef:B,onlyIfFocusInside:!0});let J=s.useMergedRefs(B,W),Y=s.useMergedRefs(V,G,U);m.useEffect(()=>{L&&H&&H.focus({preventScroll:!0})},[L,H]);let X=e=>{let t=B.current;if(e.key!==`Tab`||e.defaultPrevented||!t)return;let n=e.currentTarget,r=e.target;if(!r||typeof r.nodeType!=`number`||!n.contains(r))return;let i=o.getTabbableElements(n),a=i.findIndex(e=>e.contains(r))+(e.shiftKey?-1:1);if(a>=0&&a<i.length){e.preventDefault(),i[a].focus();return}let s=t;if(!e.shiftKey){let e=o.getTabbableElements(t.ownerDocument.body).filter(e=>!n.contains(e)),r=e.indexOf(t);s=r===-1?t:e[r+1],s&&z!==null&&!c.isInsideLayerTree(z,s)&&(s=void 0)}I(!1),s&&(e.preventDefault(),s.focus())},Z=L&&j.some(({node:e})=>v(e)===void 0&&y(e));return m.useEffect(()=>{Z&&r.warnOnce(`AvatarGroup:unnamed-member`,`AvatarGroup: a hidden member has no accessible name and is listed as "${x}". Give each member a \`name\` or \`aria-label\`.`)},[Z,x]),(0,h.jsxs)(`div`,{ref:w,role:`group`,"aria-label":T,"aria-labelledby":E,...D,className:t.cn(i.avatarGroupClassNames.root,`inline-flex items-center`,S),children:[A.map(({key:e,node:n},r)=>(0,h.jsx)(`span`,{className:t.cn(i.avatarGroupClassNames.item,`inline-flex rounded-full ring-2 ring-background`,r>0&&`-ms-2`),children:n},e)),M>0&&(0,h.jsxs)(`button`,{ref:J,type:`button`,"aria-label":R,"aria-haspopup":`dialog`,"aria-expanded":L,"aria-controls":L?P:void 0,className:t.cn(i.avatarGroupClassNames.overflowButton,`inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold`,`bg-muted text-muted-foreground ring-2 ring-background`,`not-disabled:not-aria-disabled:hover:bg-subtle-pressed`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`,A.length>0&&`-ms-2`,p.avatarSizeClasses[_]),onClick:()=>I(e=>!e),children:[`+`,M]}),L&&(0,h.jsx)(u.Portal,{layerId:q,children:(0,h.jsx)(`div`,{ref:Y,id:P,role:`dialog`,"aria-label":R,tabIndex:-1,"data-state":`open`,...K,className:t.cn(i.avatarGroupClassNames.surface,`min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16`,`focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring`),onKeyDown:X,children:(0,h.jsx)(`ul`,{className:t.cn(i.avatarGroupClassNames.list,`m-0 flex list-none flex-col gap-1 p-0`),children:j.map(({key:e,node:n})=>(0,h.jsx)(`li`,{className:t.cn(i.avatarGroupClassNames.listItem,`px-1 py-0.5`),children:v(n)??(y(n)?x:n)},e))})})})]})};b.displayName=`AvatarGroup`,exports.AvatarGroup=b;
2
2
  //# sourceMappingURL=AvatarGroup.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroup.cjs","names":["isElementOfType","Avatar","React","flattenChildren","useId","DismissLayerContext","usePopupPosition","useDismiss","useMergedRefs","getTabbableElements","isInsideLayerTree","cn","avatarSizeClasses","Portal","require_runtime","require_cn","require_children","require_dev","require_useId","require_focus","require_useMergedRefs","require_layers","require_useDismiss","require_Portal","require_usePopupPosition","require_useRestoreFocus","require_Avatar","react","react_jsx_runtime","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","getMemberName","member","label","labelledBy","decorative","name","rendersUnnamed","imageProps","image","AvatarGroup","ariaLabel","ariaLabelledBy","props","items","children","limit","max","visible","hidden","overflow","unnamed","popupId","open","setOpen","isOpen","overflowLabel","parentLayerId","triggerRef","surfaceRef","surface","setSurface","triggerRefs","setReference","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","event","popup","origin","inner","next","el","target","order","index","hasUnnamedMember","node","unnamedMemberLabel","className","i","key","size","floatingProps"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { DismissLayerContext, useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar, avatarSizeClasses } from './Avatar';\nimport type { AvatarProps } from './Avatar';\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one.\n */\n max?: number;\n /** Size of the overflow button (match it to the avatars).\n * @default 'medium'\n */\n size?: Size;\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nconst DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` given by its props (`aria-label` before `name`). An `Avatar` labelled by\n * `aria-labelledby` (unless `decorative`) and every other element have none: they are rendered as\n * they are, so a link keeps working and keeps its own label.\n */\nfunction getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (!isElementOfType<AvatarProps>(member, Avatar)) return undefined;\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nfunction rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/**\n * A row of overlapping avatars (`role=\"group\"`; name it with `aria-label`).\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`). It toggles a popup that lists the hidden members' names: an\n * `Avatar` by its `aria-label` or `name` prop, text as it is. Other members, such as an `Avatar`\n * inside a link or one labelled by `aria-labelledby`, are rendered as they are, and an `Avatar` or\n * text with no accessible name is listed as `unnamedMemberLabel` with a development warning.\n * Focus moves into the popup; Escape, a press outside or the button close it and focus returns to\n * the button. Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <Avatar name=\"Ada Lovelace\" />\n * <Avatar name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = ({\n max,\n size = 'medium',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n const items = flattenChildren(children);\n const limit = max === undefined || Number.isNaN(max) ? Infinity : Math.max(0, Math.floor(max));\n const visible = items.slice(0, limit);\n const hidden = items.slice(visible.length);\n const overflow = hidden.length;\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n const popupId = useId('avatar-group-overflow');\n const [open, setOpen] = React.useState(false);\n // Nothing is hidden any more: close, so the popup does not reappear when members overflow again.\n if (open && overflow === 0) setOpen(false);\n const isOpen = open && overflow > 0;\n const label =\n typeof overflowLabel === 'function'\n ? overflowLabel(overflow)\n : (overflowLabel ?? `${overflow} more`);\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: () => setOpen(false),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n setOpen(false);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen && hidden.some(({ node }) => getMemberName(node) === undefined && rendersUnnamed(node));\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n {...props}\n className={cn('inline-flex items-center', className)}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn('inline-flex rounded-full ring-2 ring-background', i > 0 && '-ms-2')}\n >\n {node}\n </span>\n ))}\n {overflow > 0 && (\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n visible.length > 0 && '-ms-2',\n avatarSizeClasses[size],\n )}\n onClick={() => setOpen((value) => !value)}\n >\n +{overflow}\n </button>\n )}\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <ul className=\"m-0 flex list-none flex-col gap-1 p-0\">\n {hidden.map(({ key, node }) => (\n <li key={key} className=\"px-1 py-0.5\">\n {getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node)}\n </li>\n ))}\n </ul>\n </div>\n </Portal>\n )}\n </div>\n );\n};\n\nAvatarGroup.displayName = 'AvatarGroup';\n"],"mappings":"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"}
1
+ {"version":3,"file":"AvatarGroup.cjs","names":["isElementOfType","Avatar","React","flattenChildren","useId","DismissLayerContext","usePopupPosition","useDismiss","useMergedRefs","getTabbableElements","isInsideLayerTree","cn","avatarGroupClassNames","avatarSizeClasses","Portal","require_runtime","require_cn","require_children","require_dev","require_classNames","require_useId","require_focus","require_useMergedRefs","require_layers","require_useDismiss","require_Portal","require_usePopupPosition","require_useRestoreFocus","require_Avatar","react","react_jsx_runtime","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","getMemberName","member","label","labelledBy","decorative","name","rendersUnnamed","imageProps","image","AvatarGroup","ariaLabel","ariaLabelledBy","props","items","children","limit","max","visible","hidden","overflow","unnamed","popupId","open","setOpen","isOpen","overflowLabel","parentLayerId","triggerRef","surfaceRef","surface","setSurface","triggerRefs","setReference","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","event","popup","origin","inner","next","el","target","order","index","hasUnnamedMember","node","unnamedMemberLabel","className","i","key","size","floatingProps"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { avatarGroupClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { DismissLayerContext, useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar, avatarSizeClasses } from './Avatar';\nimport type { AvatarProps } from './Avatar';\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one.\n */\n max?: number;\n /** Size of the overflow button (match it to the avatars).\n * @default 'medium'\n */\n size?: Size;\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nconst DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` given by its props (`aria-label` before `name`). An `Avatar` labelled by\n * `aria-labelledby` (unless `decorative`) and every other element have none: they are rendered as\n * they are, so a link keeps working and keeps its own label.\n */\nfunction getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (!isElementOfType<AvatarProps>(member, Avatar)) return undefined;\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nfunction rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/**\n * A row of overlapping avatars (`role=\"group\"`; name it with `aria-label`).\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`). It toggles a popup that lists the hidden members' names: an\n * `Avatar` by its `aria-label` or `name` prop, text as it is. Other members, such as an `Avatar`\n * inside a link or one labelled by `aria-labelledby`, are rendered as they are, and an `Avatar` or\n * text with no accessible name is listed as `unnamedMemberLabel` with a development warning.\n * Focus moves into the popup; Escape, a press outside or the button close it and focus returns to\n * the button. Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <Avatar name=\"Ada Lovelace\" />\n * <Avatar name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = ({\n max,\n size = 'medium',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n const items = flattenChildren(children);\n const limit = max === undefined || Number.isNaN(max) ? Infinity : Math.max(0, Math.floor(max));\n const visible = items.slice(0, limit);\n const hidden = items.slice(visible.length);\n const overflow = hidden.length;\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n const popupId = useId('avatar-group-overflow');\n const [open, setOpen] = React.useState(false);\n // Nothing is hidden any more: close, so the popup does not reappear when members overflow again.\n if (open && overflow === 0) setOpen(false);\n const isOpen = open && overflow > 0;\n const label =\n typeof overflowLabel === 'function'\n ? overflowLabel(overflow)\n : (overflowLabel ?? `${overflow} more`);\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: () => setOpen(false),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n setOpen(false);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen && hidden.some(({ node }) => getMemberName(node) === undefined && rendersUnnamed(node));\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n {...props}\n className={cn(avatarGroupClassNames.root, 'inline-flex items-center', className)}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn(\n avatarGroupClassNames.item,\n 'inline-flex rounded-full ring-2 ring-background',\n i > 0 && '-ms-2',\n )}\n >\n {node}\n </span>\n ))}\n {overflow > 0 && (\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n avatarGroupClassNames.overflowButton,\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n visible.length > 0 && '-ms-2',\n avatarSizeClasses[size],\n )}\n onClick={() => setOpen((value) => !value)}\n >\n +{overflow}\n </button>\n )}\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n avatarGroupClassNames.surface,\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <ul className={cn(avatarGroupClassNames.list, 'm-0 flex list-none flex-col gap-1 p-0')}>\n {hidden.map(({ key, node }) => (\n <li key={key} className={cn(avatarGroupClassNames.listItem, 'px-1 py-0.5')}>\n {getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node)}\n </li>\n ))}\n </ul>\n </div>\n </Portal>\n )}\n </div>\n );\n};\n\nAvatarGroup.displayName = 'AvatarGroup';\n"],"mappings":"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"}
@@ -2,133 +2,134 @@
2
2
  import { cn as e } from "../../lib/cn.mjs";
3
3
  import { flattenChildren as t, isElementOfType as n } from "../../lib/children.mjs";
4
4
  import { warnOnce as r } from "../../lib/dev.mjs";
5
- import { useId as i } from "../../hooks/useId.mjs";
6
- import { getTabbableElements as a } from "../../lib/focus.mjs";
7
- import { useMergedRefs as o } from "../../hooks/useMergedRefs.mjs";
8
- import { isInsideLayerTree as s } from "../../lib/layers.mjs";
9
- import { DismissLayerContext as c, useDismiss as l } from "../../hooks/useDismiss.mjs";
10
- import { Portal as u } from "../portal/Portal.mjs";
5
+ import { avatarGroupClassNames as i } from "../../lib/classNames.mjs";
6
+ import { useId as a } from "../../hooks/useId.mjs";
7
+ import { getTabbableElements as o } from "../../lib/focus.mjs";
8
+ import { useMergedRefs as s } from "../../hooks/useMergedRefs.mjs";
9
+ import { isInsideLayerTree as c } from "../../lib/layers.mjs";
10
+ import { DismissLayerContext as l, useDismiss as u } from "../../hooks/useDismiss.mjs";
11
+ import { Portal as d } from "../portal/Portal.mjs";
11
12
  import "../../lib/styles.mjs";
12
- import { usePopupPosition as d } from "../../hooks/usePopupPosition.mjs";
13
- import { useRestoreFocus as f } from "../../hooks/useRestoreFocus.mjs";
14
- import { Avatar as p, avatarSizeClasses as m } from "./Avatar.mjs";
15
- import * as h from "react";
16
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
13
+ import { usePopupPosition as f } from "../../hooks/usePopupPosition.mjs";
14
+ import { useRestoreFocus as p } from "../../hooks/useRestoreFocus.mjs";
15
+ import { Avatar as m, avatarSizeClasses as h } from "./Avatar.mjs";
16
+ import * as g from "react";
17
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
17
18
  //#region src/components/data-display/AvatarGroup.tsx
18
- var v = "Unnamed member", y = (e) => typeof e == "string" && e.trim() !== "";
19
- function b(e) {
19
+ var y = "Unnamed member", b = (e) => typeof e == "string" && e.trim() !== "";
20
+ function x(e) {
20
21
  if (typeof e == "string" || typeof e == "number") return String(e).trim() || void 0;
21
- if (!n(e, p)) return;
22
+ if (!n(e, m)) return;
22
23
  let { decorative: t, name: r, "aria-label": i, "aria-labelledby": a } = e.props;
23
- if (t || !y(a)) {
24
- for (let e of [i, r]) if (y(e)) return e.trim();
24
+ if (t || !b(a)) {
25
+ for (let e of [i, r]) if (b(e)) return e.trim();
25
26
  }
26
27
  }
27
- function x(e) {
28
+ function S(e) {
28
29
  if (typeof e == "string" || typeof e == "number") return !0;
29
- if (!n(e, p)) return !1;
30
+ if (!n(e, m)) return !1;
30
31
  let { decorative: t, image: r, "aria-labelledby": i } = e.props;
31
32
  if (t) return !0;
32
- if (y(i)) return !1;
33
- let a = h.isValidElement(r) ? r.props : r;
34
- return !y(typeof a == "object" && a && "alt" in a ? a.alt : void 0);
33
+ if (b(i)) return !1;
34
+ let a = g.isValidElement(r) ? r.props : r;
35
+ return !b(typeof a == "object" && a && "alt" in a ? a.alt : void 0);
35
36
  }
36
- var S = ({ max: n, size: p = "medium", overflowLabel: y, unnamedMemberLabel: S = v, className: C, children: w, ref: T, "aria-label": E, "aria-labelledby": D, ...O }) => {
37
- let k = t(w), A = n === void 0 || Number.isNaN(n) ? Infinity : Math.max(0, Math.floor(n)), j = k.slice(0, A), M = k.slice(j.length), N = M.length, P = E === void 0 && D === void 0;
38
- h.useEffect(() => {
39
- P && r("AvatarGroup:unnamed", "AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.");
40
- }, [P]);
41
- let F = i("avatar-group-overflow"), [I, L] = h.useState(!1);
42
- I && N === 0 && L(!1);
43
- let R = I && N > 0, z = typeof y == "function" ? y(N) : y ?? `${N} more`, B = h.useContext(c), V = h.useRef(null), H = h.useRef(null), [U, W] = h.useState(null), { setReference: G, setFloating: K, floatingProps: q } = d({
44
- open: R,
37
+ var C = ({ max: n, size: m = "medium", overflowLabel: b, unnamedMemberLabel: C = y, className: w, children: T, ref: E, "aria-label": D, "aria-labelledby": O, ...k }) => {
38
+ let A = t(T), j = n === void 0 || Number.isNaN(n) ? Infinity : Math.max(0, Math.floor(n)), M = A.slice(0, j), N = A.slice(M.length), P = N.length, F = D === void 0 && O === void 0;
39
+ g.useEffect(() => {
40
+ F && r("AvatarGroup:unnamed", "AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.");
41
+ }, [F]);
42
+ let I = a("avatar-group-overflow"), [L, R] = g.useState(!1);
43
+ L && P === 0 && R(!1);
44
+ let z = L && P > 0, B = typeof b == "function" ? b(P) : b ?? `${P} more`, V = g.useContext(l), H = g.useRef(null), U = g.useRef(null), [W, G] = g.useState(null), { setReference: K, setFloating: q, floatingProps: J } = f({
45
+ open: z,
45
46
  side: "bottom",
46
47
  align: "start"
47
- }), { layerId: J } = l({
48
- open: R,
49
- onDismiss: () => L(!1),
50
- refs: [V, H],
51
- anchorRef: V,
48
+ }), { layerId: Y } = u({
49
+ open: z,
50
+ onDismiss: () => R(!1),
51
+ refs: [H, U],
52
+ anchorRef: H,
52
53
  focusOutside: !0
53
54
  });
54
- f({
55
- enabled: R,
56
- container: U,
57
- triggerRef: V,
55
+ p({
56
+ enabled: z,
57
+ container: W,
58
+ triggerRef: H,
58
59
  onlyIfFocusInside: !0
59
60
  });
60
- let Y = o(V, G), X = o(H, K, W);
61
- h.useEffect(() => {
62
- R && U && U.focus({ preventScroll: !0 });
63
- }, [R, U]);
64
- let Z = (e) => {
65
- let t = V.current;
61
+ let X = s(H, K), Z = s(U, q, G);
62
+ g.useEffect(() => {
63
+ z && W && W.focus({ preventScroll: !0 });
64
+ }, [z, W]);
65
+ let Q = (e) => {
66
+ let t = H.current;
66
67
  if (e.key !== "Tab" || e.defaultPrevented || !t) return;
67
68
  let n = e.currentTarget, r = e.target;
68
69
  if (!r || typeof r.nodeType != "number" || !n.contains(r)) return;
69
- let i = a(n), o = i.findIndex((e) => e.contains(r)) + (e.shiftKey ? -1 : 1);
70
- if (o >= 0 && o < i.length) {
71
- e.preventDefault(), i[o].focus();
70
+ let i = o(n), a = i.findIndex((e) => e.contains(r)) + (e.shiftKey ? -1 : 1);
71
+ if (a >= 0 && a < i.length) {
72
+ e.preventDefault(), i[a].focus();
72
73
  return;
73
74
  }
74
- let c = t;
75
+ let s = t;
75
76
  if (!e.shiftKey) {
76
- let e = a(t.ownerDocument.body).filter((e) => !n.contains(e)), r = e.indexOf(t);
77
- c = r === -1 ? t : e[r + 1], c && B !== null && !s(B, c) && (c = void 0);
77
+ let e = o(t.ownerDocument.body).filter((e) => !n.contains(e)), r = e.indexOf(t);
78
+ s = r === -1 ? t : e[r + 1], s && V !== null && !c(V, s) && (s = void 0);
78
79
  }
79
- L(!1), c && (e.preventDefault(), c.focus());
80
- }, Q = R && M.some(({ node: e }) => b(e) === void 0 && x(e));
81
- return h.useEffect(() => {
82
- Q && r("AvatarGroup:unnamed-member", `AvatarGroup: a hidden member has no accessible name and is listed as "${S}". Give each member a \`name\` or \`aria-label\`.`);
83
- }, [Q, S]), /* @__PURE__ */ _("div", {
84
- ref: T,
80
+ R(!1), s && (e.preventDefault(), s.focus());
81
+ }, $ = z && N.some(({ node: e }) => x(e) === void 0 && S(e));
82
+ return g.useEffect(() => {
83
+ $ && r("AvatarGroup:unnamed-member", `AvatarGroup: a hidden member has no accessible name and is listed as "${C}". Give each member a \`name\` or \`aria-label\`.`);
84
+ }, [$, C]), /* @__PURE__ */ v("div", {
85
+ ref: E,
85
86
  role: "group",
86
- "aria-label": E,
87
- "aria-labelledby": D,
88
- ...O,
89
- className: e("inline-flex items-center", C),
87
+ "aria-label": D,
88
+ "aria-labelledby": O,
89
+ ...k,
90
+ className: e(i.root, "inline-flex items-center", w),
90
91
  children: [
91
- j.map(({ key: t, node: n }, r) => /* @__PURE__ */ g("span", {
92
- className: e("inline-flex rounded-full ring-2 ring-background", r > 0 && "-ms-2"),
92
+ M.map(({ key: t, node: n }, r) => /* @__PURE__ */ _("span", {
93
+ className: e(i.item, "inline-flex rounded-full ring-2 ring-background", r > 0 && "-ms-2"),
93
94
  children: n
94
95
  }, t)),
95
- N > 0 && /* @__PURE__ */ _("button", {
96
- ref: Y,
96
+ P > 0 && /* @__PURE__ */ v("button", {
97
+ ref: X,
97
98
  type: "button",
98
- "aria-label": z,
99
+ "aria-label": B,
99
100
  "aria-haspopup": "dialog",
100
- "aria-expanded": R,
101
- "aria-controls": R ? F : void 0,
102
- className: e("inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold", "bg-muted text-muted-foreground ring-2 ring-background", "not-disabled:not-aria-disabled:hover:bg-subtle-pressed", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", j.length > 0 && "-ms-2", m[p]),
103
- onClick: () => L((e) => !e),
104
- children: ["+", N]
101
+ "aria-expanded": z,
102
+ "aria-controls": z ? I : void 0,
103
+ className: e(i.overflowButton, "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold", "bg-muted text-muted-foreground ring-2 ring-background", "not-disabled:not-aria-disabled:hover:bg-subtle-pressed", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring", M.length > 0 && "-ms-2", h[m]),
104
+ onClick: () => R((e) => !e),
105
+ children: ["+", P]
105
106
  }),
106
- R && /* @__PURE__ */ g(u, {
107
- layerId: J,
108
- children: /* @__PURE__ */ g("div", {
109
- ref: X,
110
- id: F,
107
+ z && /* @__PURE__ */ _(d, {
108
+ layerId: Y,
109
+ children: /* @__PURE__ */ _("div", {
110
+ ref: Z,
111
+ id: I,
111
112
  role: "dialog",
112
- "aria-label": z,
113
+ "aria-label": B,
113
114
  tabIndex: -1,
114
115
  "data-state": "open",
115
- ...q,
116
- className: e("min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
117
- onKeyDown: Z,
118
- children: /* @__PURE__ */ g("ul", {
119
- className: "m-0 flex list-none flex-col gap-1 p-0",
120
- children: M.map(({ key: e, node: t }) => /* @__PURE__ */ g("li", {
121
- className: "px-1 py-0.5",
122
- children: b(t) ?? (x(t) ? S : t)
123
- }, e))
116
+ ...J,
117
+ className: e(i.surface, "min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
118
+ onKeyDown: Q,
119
+ children: /* @__PURE__ */ _("ul", {
120
+ className: e(i.list, "m-0 flex list-none flex-col gap-1 p-0"),
121
+ children: N.map(({ key: t, node: n }) => /* @__PURE__ */ _("li", {
122
+ className: e(i.listItem, "px-1 py-0.5"),
123
+ children: x(n) ?? (S(n) ? C : n)
124
+ }, t))
124
125
  })
125
126
  })
126
127
  })
127
128
  ]
128
129
  });
129
130
  };
130
- S.displayName = "AvatarGroup";
131
+ C.displayName = "AvatarGroup";
131
132
  //#endregion
132
- export { S as AvatarGroup };
133
+ export { C as AvatarGroup };
133
134
 
134
135
  //# sourceMappingURL=AvatarGroup.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarGroup.mjs","names":["useId","useId$1","React","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","getMemberName","member","isElementOfType","Avatar","label","labelledBy","decorative","name","rendersUnnamed","imageProps","image","AvatarGroup","ariaLabel","ariaLabelledBy","props","items","flattenChildren","children","limit","max","visible","hidden","overflow","unnamed","warnOnce","popupId","open","setOpen","isOpen","overflowLabel","parentLayerId","DismissLayerContext","triggerRef","surfaceRef","surface","setSurface","usePopupPosition","useDismiss","triggerRefs","useMergedRefs","setReference","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","event","popup","origin","inner","getTabbableElements","next","el","target","order","index","isInsideLayerTree","hasUnnamedMember","node","unnamedMemberLabel","cn","className","i","key","avatarSizeClasses","size","Portal","floatingProps"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { DismissLayerContext, useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar, avatarSizeClasses } from './Avatar';\nimport type { AvatarProps } from './Avatar';\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one.\n */\n max?: number;\n /** Size of the overflow button (match it to the avatars).\n * @default 'medium'\n */\n size?: Size;\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nconst DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` given by its props (`aria-label` before `name`). An `Avatar` labelled by\n * `aria-labelledby` (unless `decorative`) and every other element have none: they are rendered as\n * they are, so a link keeps working and keeps its own label.\n */\nfunction getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (!isElementOfType<AvatarProps>(member, Avatar)) return undefined;\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nfunction rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/**\n * A row of overlapping avatars (`role=\"group\"`; name it with `aria-label`).\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`). It toggles a popup that lists the hidden members' names: an\n * `Avatar` by its `aria-label` or `name` prop, text as it is. Other members, such as an `Avatar`\n * inside a link or one labelled by `aria-labelledby`, are rendered as they are, and an `Avatar` or\n * text with no accessible name is listed as `unnamedMemberLabel` with a development warning.\n * Focus moves into the popup; Escape, a press outside or the button close it and focus returns to\n * the button. Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <Avatar name=\"Ada Lovelace\" />\n * <Avatar name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = ({\n max,\n size = 'medium',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n const items = flattenChildren(children);\n const limit = max === undefined || Number.isNaN(max) ? Infinity : Math.max(0, Math.floor(max));\n const visible = items.slice(0, limit);\n const hidden = items.slice(visible.length);\n const overflow = hidden.length;\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n const popupId = useId('avatar-group-overflow');\n const [open, setOpen] = React.useState(false);\n // Nothing is hidden any more: close, so the popup does not reappear when members overflow again.\n if (open && overflow === 0) setOpen(false);\n const isOpen = open && overflow > 0;\n const label =\n typeof overflowLabel === 'function'\n ? overflowLabel(overflow)\n : (overflowLabel ?? `${overflow} more`);\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: () => setOpen(false),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n setOpen(false);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen && hidden.some(({ node }) => getMemberName(node) === undefined && rendersUnnamed(node));\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n {...props}\n className={cn('inline-flex items-center', className)}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn('inline-flex rounded-full ring-2 ring-background', i > 0 && '-ms-2')}\n >\n {node}\n </span>\n ))}\n {overflow > 0 && (\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n visible.length > 0 && '-ms-2',\n avatarSizeClasses[size],\n )}\n onClick={() => setOpen((value) => !value)}\n >\n +{overflow}\n </button>\n )}\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <ul className=\"m-0 flex list-none flex-col gap-1 p-0\">\n {hidden.map(({ key, node }) => (\n <li key={key} className=\"px-1 py-0.5\">\n {getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node)}\n </li>\n ))}\n </ul>\n </div>\n </Portal>\n )}\n </div>\n );\n};\n\nAvatarGroup.displayName = 'AvatarGroup';\n"],"mappings":"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"}
1
+ {"version":3,"file":"AvatarGroup.mjs","names":["useId","useId$1","React","DEFAULT_UNNAMED_MEMBER_LABEL","nonBlank","value","getMemberName","member","isElementOfType","Avatar","label","labelledBy","decorative","name","rendersUnnamed","imageProps","image","AvatarGroup","ariaLabel","ariaLabelledBy","props","items","flattenChildren","children","limit","max","visible","hidden","overflow","unnamed","warnOnce","popupId","open","setOpen","isOpen","overflowLabel","parentLayerId","DismissLayerContext","triggerRef","surfaceRef","surface","setSurface","usePopupPosition","useDismiss","triggerRefs","useMergedRefs","setReference","surfaceRefs","setFloating","handleSurfaceKeyDown","trigger","event","popup","origin","inner","getTabbableElements","next","el","target","order","index","isInsideLayerTree","hasUnnamedMember","node","unnamedMemberLabel","cn","avatarGroupClassNames","className","i","key","avatarSizeClasses","size","Portal","floatingProps"],"sources":["../../../src/components/data-display/AvatarGroup.tsx"],"sourcesContent":["import * as React from 'react';\nimport { flattenChildren, isElementOfType } from '../../lib/children';\nimport { cn } from '../../lib/cn';\nimport { avatarGroupClassNames } from '../../lib/classNames';\nimport { warnOnce } from '../../lib/dev';\nimport { getTabbableElements } from '../../lib/focus';\nimport { isInsideLayerTree } from '../../lib/layers';\nimport { focusRing } from '../../lib/styles';\nimport type { Size } from '../../lib/types';\nimport { useId } from '../../hooks/useId';\nimport { DismissLayerContext, useDismiss } from '../../hooks/useDismiss';\nimport { useMergedRefs } from '../../hooks/useMergedRefs';\nimport { usePopupPosition } from '../../hooks/usePopupPosition';\nimport { useRestoreFocus } from '../../hooks/useRestoreFocus';\nimport { Portal } from '../portal/Portal';\nimport { Avatar, avatarSizeClasses } from './Avatar';\nimport type { AvatarProps } from './Avatar';\n\n/** Properties for the AvatarGroup component. */\nexport interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Maximum number of avatars to display. The others are replaced by an overflow button that\n * lists their names. `0` or a negative value shows only the overflow button, a fraction is\n * rounded down, and `NaN` shows every member (as without `max`). The members of a Fragment\n * count one by one.\n */\n max?: number;\n /** Size of the overflow button (match it to the avatars).\n * @default 'medium'\n */\n size?: Size;\n /**\n * Accessible name of the overflow button (and its popup). A function receives the number of\n * hidden members. Localise it here.\n * @default (count) => `${count} more`\n */\n overflowLabel?: string | ((count: number) => string);\n /**\n * Text listed in the overflow popup for a hidden member that has no accessible name (an\n * `Avatar` without `name`, `aria-label`, `aria-labelledby` or image `alt`, or blank text).\n * Localise it here; better still, give every member a name (a development warning asks for it).\n * @default 'Unnamed member'\n */\n unnamedMemberLabel?: string;\n /** Ref to the root `<div>`. */\n ref?: React.Ref<HTMLDivElement>;\n}\n\n/** Default text listed for a hidden member that would render with no accessible name. */\nconst DEFAULT_UNNAMED_MEMBER_LABEL = 'Unnamed member';\n\nconst nonBlank = (value: unknown): value is string =>\n typeof value === 'string' && value.trim() !== '';\n\n/**\n * The text name of a member for the overflow list: the text of a text member, or the accessible\n * name of an `Avatar` given by its props (`aria-label` before `name`). An `Avatar` labelled by\n * `aria-labelledby` (unless `decorative`) and every other element have none: they are rendered as\n * they are, so a link keeps working and keeps its own label.\n */\nfunction getMemberName(member: React.ReactNode): string | undefined {\n if (typeof member === 'string' || typeof member === 'number') {\n return String(member).trim() || undefined;\n }\n if (!isElementOfType<AvatarProps>(member, Avatar)) return undefined;\n const { decorative, name, 'aria-label': label, 'aria-labelledby': labelledBy } = member.props;\n if (!decorative && nonBlank(labelledBy)) return undefined;\n for (const value of [label, name]) {\n if (nonBlank(value)) return value.trim();\n }\n return undefined;\n}\n\n/**\n * Whether a member without a {@link getMemberName} name would render with no accessible name,\n * so its list item would be empty for assistive technology: blank text, or an `Avatar` that is\n * neither labelled by `aria-labelledby` nor named by its image's `alt` (or is `decorative`).\n * Other elements are rendered as they are, since their content is unknown here.\n */\nfunction rendersUnnamed(member: React.ReactNode): boolean {\n if (typeof member === 'string' || typeof member === 'number') return true;\n if (!isElementOfType<AvatarProps>(member, Avatar)) return false;\n const { decorative, image, 'aria-labelledby': labelledBy } = member.props;\n if (decorative) return true;\n if (nonBlank(labelledBy)) return false;\n const imageProps: unknown = React.isValidElement(image) ? image.props : image;\n const alt =\n typeof imageProps === 'object' && imageProps !== null && 'alt' in imageProps\n ? imageProps.alt\n : undefined;\n return !nonBlank(alt);\n}\n\n/**\n * A row of overlapping avatars (`role=\"group\"`; name it with `aria-label`).\n *\n * With `max`, the avatars beyond the limit are replaced by an overflow `<button>` (\"+3\", named\n * \"3 more\", see `overflowLabel`). It toggles a popup that lists the hidden members' names: an\n * `Avatar` by its `aria-label` or `name` prop, text as it is. Other members, such as an `Avatar`\n * inside a link or one labelled by `aria-labelledby`, are rendered as they are, and an `Avatar` or\n * text with no accessible name is listed as `unnamedMemberLabel` with a development warning.\n * Focus moves into the popup; Escape, a press outside or the button close it and focus returns to\n * the button. Tab and Shift+Tab move through the popup's own tab stops (links in rendered\n * members); Tab from the last one closes the popup and moves on to the element after the button,\n * Shift+Tab from the first one closes it and returns to the button. When the button is the last\n * tab stop (of the page, or of the Wave layer the group is in, such as a dialog), Tab is left to\n * the browser or the enclosing focus trap.\n *\n * @example\n * <AvatarGroup aria-label=\"Project team\" max={3}>\n * <Avatar name=\"Ada Lovelace\" />\n * <Avatar name=\"Alan Turing\" />\n * </AvatarGroup>\n */\nexport const AvatarGroup = ({\n max,\n size = 'medium',\n overflowLabel,\n unnamedMemberLabel = DEFAULT_UNNAMED_MEMBER_LABEL,\n className,\n children,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...props\n}: AvatarGroupProps) => {\n // Fragments are flattened, so each member counts, overlaps and is listed on its own (C-COMPOUND).\n const items = flattenChildren(children);\n const limit = max === undefined || Number.isNaN(max) ? Infinity : Math.max(0, Math.floor(max));\n const visible = items.slice(0, limit);\n const hidden = items.slice(visible.length);\n const overflow = hidden.length;\n\n const unnamed = ariaLabel === undefined && ariaLabelledBy === undefined;\n React.useEffect(() => {\n if (unnamed) {\n warnOnce(\n 'AvatarGroup:unnamed',\n 'AvatarGroup: the group has no accessible name. Pass `aria-label` (or `aria-labelledby`) describing the people it shows.',\n );\n }\n }, [unnamed]);\n\n const popupId = useId('avatar-group-overflow');\n const [open, setOpen] = React.useState(false);\n // Nothing is hidden any more: close, so the popup does not reappear when members overflow again.\n if (open && overflow === 0) setOpen(false);\n const isOpen = open && overflow > 0;\n const label =\n typeof overflowLabel === 'function'\n ? overflowLabel(overflow)\n : (overflowLabel ?? `${overflow} more`);\n\n // The layer the group is rendered in (a dialog or popover), if any: the popup's parent layer.\n const parentLayerId = React.useContext(DismissLayerContext);\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const surfaceRef = React.useRef<HTMLDivElement | null>(null);\n const [surface, setSurface] = React.useState<HTMLDivElement | null>(null);\n\n const { setReference, setFloating, floatingProps } = usePopupPosition({\n open: isOpen,\n side: 'bottom',\n align: 'start',\n });\n const { layerId } = useDismiss({\n open: isOpen,\n onDismiss: () => setOpen(false),\n refs: [triggerRef, surfaceRef],\n anchorRef: triggerRef,\n focusOutside: true,\n });\n useRestoreFocus({ enabled: isOpen, container: surface, triggerRef, onlyIfFocusInside: true });\n const triggerRefs = useMergedRefs<HTMLButtonElement>(triggerRef, setReference);\n const surfaceRefs = useMergedRefs<HTMLDivElement>(surfaceRef, setFloating, setSurface);\n\n // Move focus into the popup once it is mounted, so its list is read next.\n React.useEffect(() => {\n if (isOpen && surface) surface.focus({ preventScroll: true });\n }, [isOpen, surface]);\n\n // The popup is portaled, so native tabbing would leave it for the end of the page. Tab moves\n // through the popup's own tab stops (a rendered member can be a link); leaving the last one\n // closes the popup and moves on from the button instead (Shift+Tab before the first one: back to\n // the button), like a focus trap does for a popup anchored in a dialog. When nothing tabbable\n // follows the button, Tab is not prevented: focus returns to the button as the popup closes and\n // the browser (leaving the page) or an enclosing focus trap (wrapping) takes it from there.\n // Inside a parent layer (a dialog, a popover), a tab stop after the button that lies outside that\n // layer is treated the same way: a focus trap that leaves the page outside it interactive would\n // pull focus back from there, while its descendant-layer rule (§2.4) wraps from the button.\n // Tab stops are computed at keydown time, never cached (§2.2 focus utilities). A key that\n // bubbles through React from a portal a member opened is not the popup's: it is ignored.\n const handleSurfaceKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const trigger = triggerRef.current;\n if (event.key !== 'Tab' || event.defaultPrevented || !trigger) return;\n const popup = event.currentTarget;\n // Duck typed (`nodeType`), not `instanceof Node`: a popup portaled into another document (an\n // iframe, a popout window) holds nodes of that realm.\n const origin = event.target as Partial<Node> | null;\n if (!origin || typeof origin.nodeType !== 'number' || !popup.contains(origin as Node)) return;\n const inner = getTabbableElements(popup);\n // -1 while focus is on the popup itself (or on something that is not a tab stop).\n const current = inner.findIndex((el) => el.contains(origin as Node));\n const next = current + (event.shiftKey ? -1 : 1);\n if (next >= 0 && next < inner.length) {\n event.preventDefault();\n inner[next]!.focus();\n return;\n }\n let target: HTMLElement | undefined = trigger;\n if (!event.shiftKey) {\n const order = getTabbableElements(trigger.ownerDocument.body).filter(\n (el) => !popup.contains(el),\n );\n const index = order.indexOf(trigger);\n target = index === -1 ? trigger : order[index + 1];\n if (target && parentLayerId !== null && !isInsideLayerTree(parentLayerId, target)) {\n target = undefined;\n }\n }\n setOpen(false);\n if (!target) return;\n event.preventDefault();\n target.focus();\n };\n\n const hasUnnamedMember =\n isOpen && hidden.some(({ node }) => getMemberName(node) === undefined && rendersUnnamed(node));\n React.useEffect(() => {\n if (hasUnnamedMember) {\n warnOnce(\n 'AvatarGroup:unnamed-member',\n `AvatarGroup: a hidden member has no accessible name and is listed as \"${unnamedMemberLabel}\". Give each member a \\`name\\` or \\`aria-label\\`.`,\n );\n }\n }, [hasUnnamedMember, unnamedMemberLabel]);\n\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n {...props}\n className={cn(avatarGroupClassNames.root, 'inline-flex items-center', className)}\n >\n {visible.map(({ key, node }, i) => (\n <span\n key={key}\n className={cn(\n avatarGroupClassNames.item,\n 'inline-flex rounded-full ring-2 ring-background',\n i > 0 && '-ms-2',\n )}\n >\n {node}\n </span>\n ))}\n {overflow > 0 && (\n <button\n ref={triggerRefs}\n type=\"button\"\n aria-label={label}\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? popupId : undefined}\n className={cn(\n avatarGroupClassNames.overflowButton,\n 'inline-flex shrink-0 cursor-pointer items-center justify-center rounded-full border-0 p-0 font-semibold',\n 'bg-muted text-muted-foreground ring-2 ring-background',\n 'not-disabled:not-aria-disabled:hover:bg-subtle-pressed',\n focusRing,\n visible.length > 0 && '-ms-2',\n avatarSizeClasses[size],\n )}\n onClick={() => setOpen((value) => !value)}\n >\n +{overflow}\n </button>\n )}\n {isOpen && (\n <Portal layerId={layerId}>\n <div\n ref={surfaceRefs}\n id={popupId}\n role=\"dialog\"\n aria-label={label}\n tabIndex={-1}\n data-state=\"open\"\n {...floatingProps}\n className={cn(\n avatarGroupClassNames.surface,\n 'min-w-32 max-w-xs rounded-md border border-border bg-background p-2 text-body-1 text-foreground shadow-16',\n focusRing,\n )}\n onKeyDown={handleSurfaceKeyDown}\n >\n <ul className={cn(avatarGroupClassNames.list, 'm-0 flex list-none flex-col gap-1 p-0')}>\n {hidden.map(({ key, node }) => (\n <li key={key} className={cn(avatarGroupClassNames.listItem, 'px-1 py-0.5')}>\n {getMemberName(node) ?? (rendersUnnamed(node) ? unnamedMemberLabel : node)}\n </li>\n ))}\n </ul>\n </div>\n </Portal>\n )}\n </div>\n );\n};\n\nAvatarGroup.displayName = 'AvatarGroup';\n"],"mappings":"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"}