@tribepad/themis 1.0.0 → 1.0.1

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 (326) hide show
  1. package/dist/elements/Accordion/index.js +325 -66
  2. package/dist/elements/Accordion/index.js.map +1 -1
  3. package/dist/elements/Accordion/index.mjs +317 -3
  4. package/dist/elements/Accordion/index.mjs.map +1 -1
  5. package/dist/elements/Avatar/index.js +461 -45
  6. package/dist/elements/Avatar/index.js.map +1 -1
  7. package/dist/elements/Avatar/index.mjs +456 -3
  8. package/dist/elements/Avatar/index.mjs.map +1 -1
  9. package/dist/elements/Badge/index.js +238 -36
  10. package/dist/elements/Badge/index.js.map +1 -1
  11. package/dist/elements/Badge/index.mjs +234 -4
  12. package/dist/elements/Badge/index.mjs.map +1 -1
  13. package/dist/elements/Breadcrumbs/index.js +808 -39
  14. package/dist/elements/Breadcrumbs/index.js.map +1 -1
  15. package/dist/elements/Breadcrumbs/index.mjs +810 -7
  16. package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
  17. package/dist/elements/Button/index.js +282 -19
  18. package/dist/elements/Button/index.js.map +1 -1
  19. package/dist/elements/Button/index.mjs +283 -4
  20. package/dist/elements/Button/index.mjs.map +1 -1
  21. package/dist/elements/ButtonGroup/index.js +229 -56
  22. package/dist/elements/ButtonGroup/index.js.map +1 -1
  23. package/dist/elements/ButtonGroup/index.mjs +222 -3
  24. package/dist/elements/ButtonGroup/index.mjs.map +1 -1
  25. package/dist/elements/Card/Card.d.ts.map +1 -1
  26. package/dist/elements/Card/index.js +563 -67
  27. package/dist/elements/Card/index.js.map +1 -1
  28. package/dist/elements/Card/index.mjs +560 -6
  29. package/dist/elements/Card/index.mjs.map +1 -1
  30. package/dist/elements/Carousel/index.js +782 -14
  31. package/dist/elements/Carousel/index.js.map +1 -1
  32. package/dist/elements/Carousel/index.mjs +786 -8
  33. package/dist/elements/Carousel/index.mjs.map +1 -1
  34. package/dist/elements/Chart/index.js +1833 -36
  35. package/dist/elements/Chart/index.js.map +1 -1
  36. package/dist/elements/Chart/index.mjs +1832 -4
  37. package/dist/elements/Chart/index.mjs.map +1 -1
  38. package/dist/elements/Checkbox/index.js +310 -39
  39. package/dist/elements/Checkbox/index.js.map +1 -1
  40. package/dist/elements/Checkbox/index.mjs +306 -4
  41. package/dist/elements/Checkbox/index.mjs.map +1 -1
  42. package/dist/elements/CheckboxGroup/index.js +445 -59
  43. package/dist/elements/CheckboxGroup/index.js.map +1 -1
  44. package/dist/elements/CheckboxGroup/index.mjs +439 -4
  45. package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
  46. package/dist/elements/DatePicker/index.js +871 -89
  47. package/dist/elements/DatePicker/index.js.map +1 -1
  48. package/dist/elements/DatePicker/index.mjs +853 -4
  49. package/dist/elements/DatePicker/index.mjs.map +1 -1
  50. package/dist/elements/Dropdown/index.js +189 -35
  51. package/dist/elements/Dropdown/index.js.map +1 -1
  52. package/dist/elements/Dropdown/index.mjs +184 -2
  53. package/dist/elements/Dropdown/index.mjs.map +1 -1
  54. package/dist/elements/FileField/index.js +1532 -129
  55. package/dist/elements/FileField/index.js.map +1 -1
  56. package/dist/elements/FileField/index.mjs +1507 -7
  57. package/dist/elements/FileField/index.mjs.map +1 -1
  58. package/dist/elements/FormLayout/index.js +166 -11
  59. package/dist/elements/FormLayout/index.js.map +1 -1
  60. package/dist/elements/FormLayout/index.mjs +167 -2
  61. package/dist/elements/FormLayout/index.mjs.map +1 -1
  62. package/dist/elements/Modal/index.js +228 -46
  63. package/dist/elements/Modal/index.js.map +1 -1
  64. package/dist/elements/Modal/index.mjs +220 -1
  65. package/dist/elements/Modal/index.mjs.map +1 -1
  66. package/dist/elements/NumberField/index.js +659 -48
  67. package/dist/elements/NumberField/index.js.map +1 -1
  68. package/dist/elements/NumberField/index.mjs +654 -6
  69. package/dist/elements/NumberField/index.mjs.map +1 -1
  70. package/dist/elements/OTPInput/index.js +729 -6
  71. package/dist/elements/OTPInput/index.js.map +1 -1
  72. package/dist/elements/OTPInput/index.mjs +732 -2
  73. package/dist/elements/OTPInput/index.mjs.map +1 -1
  74. package/dist/elements/Panel/index.js +326 -27
  75. package/dist/elements/Panel/index.js.map +1 -1
  76. package/dist/elements/Panel/index.mjs +323 -2
  77. package/dist/elements/Panel/index.mjs.map +1 -1
  78. package/dist/elements/Progress/index.js +181 -22
  79. package/dist/elements/Progress/index.js.map +1 -1
  80. package/dist/elements/Progress/index.mjs +181 -3
  81. package/dist/elements/Progress/index.mjs.map +1 -1
  82. package/dist/elements/RadioGroup/index.js +358 -34
  83. package/dist/elements/RadioGroup/index.js.map +1 -1
  84. package/dist/elements/RadioGroup/index.mjs +359 -4
  85. package/dist/elements/RadioGroup/index.mjs.map +1 -1
  86. package/dist/elements/Resizable/components/ResizableHandle.d.ts +0 -8
  87. package/dist/elements/Resizable/components/ResizableHandle.d.ts.map +1 -1
  88. package/dist/elements/Resizable/components/ResizablePanel.d.ts +0 -8
  89. package/dist/elements/Resizable/components/ResizablePanel.d.ts.map +1 -1
  90. package/dist/elements/Resizable/components/ResizablePanelGroup.d.ts +0 -8
  91. package/dist/elements/Resizable/components/ResizablePanelGroup.d.ts.map +1 -1
  92. package/dist/elements/Resizable/components/ResizablePopover.d.ts +0 -8
  93. package/dist/elements/Resizable/components/ResizablePopover.d.ts.map +1 -1
  94. package/dist/elements/Resizable/index.js +1568 -51
  95. package/dist/elements/Resizable/index.js.map +1 -1
  96. package/dist/elements/Resizable/index.mjs +1566 -6
  97. package/dist/elements/Resizable/index.mjs.map +1 -1
  98. package/dist/elements/Select/index.js +580 -22
  99. package/dist/elements/Select/index.js.map +1 -1
  100. package/dist/elements/Select/index.mjs +582 -2
  101. package/dist/elements/Select/index.mjs.map +1 -1
  102. package/dist/elements/Skeleton/index.js +77 -15
  103. package/dist/elements/Skeleton/index.js.map +1 -1
  104. package/dist/elements/Skeleton/index.mjs +78 -3
  105. package/dist/elements/Skeleton/index.mjs.map +1 -1
  106. package/dist/elements/Switch/index.js +153 -21
  107. package/dist/elements/Switch/index.js.map +1 -1
  108. package/dist/elements/Switch/index.mjs +149 -5
  109. package/dist/elements/Switch/index.mjs.map +1 -1
  110. package/dist/elements/Table/index.js +589 -68
  111. package/dist/elements/Table/index.js.map +1 -1
  112. package/dist/elements/Table/index.mjs +578 -5
  113. package/dist/elements/Table/index.mjs.map +1 -1
  114. package/dist/elements/Tabs/index.js +328 -63
  115. package/dist/elements/Tabs/index.js.map +1 -1
  116. package/dist/elements/Tabs/index.mjs +320 -3
  117. package/dist/elements/Tabs/index.mjs.map +1 -1
  118. package/dist/elements/TextField/index.js +695 -51
  119. package/dist/elements/TextField/index.js.map +1 -1
  120. package/dist/elements/TextField/index.mjs +684 -7
  121. package/dist/elements/TextField/index.mjs.map +1 -1
  122. package/dist/elements/TimeField/index.js +244 -33
  123. package/dist/elements/TimeField/index.js.map +1 -1
  124. package/dist/elements/TimeField/index.mjs +238 -2
  125. package/dist/elements/TimeField/index.mjs.map +1 -1
  126. package/dist/elements/Toast/index.js +727 -48
  127. package/dist/elements/Toast/index.js.map +1 -1
  128. package/dist/elements/Toast/index.mjs +724 -5
  129. package/dist/elements/Toast/index.mjs.map +1 -1
  130. package/dist/elements/Tooltip/index.js +315 -49
  131. package/dist/elements/Tooltip/index.js.map +1 -1
  132. package/dist/elements/Tooltip/index.mjs +310 -4
  133. package/dist/elements/Tooltip/index.mjs.map +1 -1
  134. package/dist/elements/index.js +12417 -799
  135. package/dist/elements/index.js.map +1 -1
  136. package/dist/elements/index.mjs +12233 -40
  137. package/dist/elements/index.mjs.map +1 -1
  138. package/dist/index.js +12452 -825
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.mjs +12262 -42
  141. package/dist/index.mjs.map +1 -1
  142. package/dist/schemas/index.js +47 -21
  143. package/dist/schemas/index.js.map +1 -1
  144. package/dist/schemas/index.mjs +47 -2
  145. package/dist/schemas/index.mjs.map +1 -1
  146. package/dist/styles/index.js +161 -147
  147. package/dist/styles/index.js.map +1 -1
  148. package/dist/styles/index.mjs +128 -2
  149. package/dist/styles/index.mjs.map +1 -1
  150. package/dist/utils/index.js +7 -7
  151. package/dist/utils/index.js.map +1 -1
  152. package/dist/utils/index.mjs +9 -2
  153. package/dist/utils/index.mjs.map +1 -1
  154. package/package.json +1 -1
  155. package/dist/Carousel-NTZX5TOW.js +0 -16
  156. package/dist/Carousel-NTZX5TOW.js.map +0 -1
  157. package/dist/Carousel-YH3DOQJU.mjs +0 -7
  158. package/dist/Carousel-YH3DOQJU.mjs.map +0 -1
  159. package/dist/chunk-2HIUTHMU.mjs +0 -234
  160. package/dist/chunk-2HIUTHMU.mjs.map +0 -1
  161. package/dist/chunk-34GTFTDO.js +0 -431
  162. package/dist/chunk-34GTFTDO.js.map +0 -1
  163. package/dist/chunk-3H7ASYR7.js +0 -250
  164. package/dist/chunk-3H7ASYR7.js.map +0 -1
  165. package/dist/chunk-3IEN7JOP.js +0 -316
  166. package/dist/chunk-3IEN7JOP.js.map +0 -1
  167. package/dist/chunk-3JHN4GAL.js +0 -326
  168. package/dist/chunk-3JHN4GAL.js.map +0 -1
  169. package/dist/chunk-3MJPASQU.js +0 -232
  170. package/dist/chunk-3MJPASQU.js.map +0 -1
  171. package/dist/chunk-3XD2JUL3.js +0 -572
  172. package/dist/chunk-3XD2JUL3.js.map +0 -1
  173. package/dist/chunk-3YOY2VJ6.js +0 -189
  174. package/dist/chunk-3YOY2VJ6.js.map +0 -1
  175. package/dist/chunk-4DU5JSXB.js +0 -408
  176. package/dist/chunk-4DU5JSXB.js.map +0 -1
  177. package/dist/chunk-4E4E2GSS.js +0 -352
  178. package/dist/chunk-4E4E2GSS.js.map +0 -1
  179. package/dist/chunk-4NHAP4AN.mjs +0 -3
  180. package/dist/chunk-4NHAP4AN.mjs.map +0 -1
  181. package/dist/chunk-4S33J5NY.mjs +0 -415
  182. package/dist/chunk-4S33J5NY.mjs.map +0 -1
  183. package/dist/chunk-5SMGRT3G.mjs +0 -354
  184. package/dist/chunk-5SMGRT3G.mjs.map +0 -1
  185. package/dist/chunk-5SVLJN2C.mjs +0 -22
  186. package/dist/chunk-5SVLJN2C.mjs.map +0 -1
  187. package/dist/chunk-66WTU4EB.mjs +0 -299
  188. package/dist/chunk-66WTU4EB.mjs.map +0 -1
  189. package/dist/chunk-6S25NMOT.mjs +0 -335
  190. package/dist/chunk-6S25NMOT.mjs.map +0 -1
  191. package/dist/chunk-6SP7UB3D.js +0 -4
  192. package/dist/chunk-6SP7UB3D.js.map +0 -1
  193. package/dist/chunk-6TYWWQHM.mjs +0 -565
  194. package/dist/chunk-6TYWWQHM.mjs.map +0 -1
  195. package/dist/chunk-A3YUJA6W.mjs +0 -384
  196. package/dist/chunk-A3YUJA6W.mjs.map +0 -1
  197. package/dist/chunk-A6KEDVUR.js +0 -61
  198. package/dist/chunk-A6KEDVUR.js.map +0 -1
  199. package/dist/chunk-A77RUEWL.js +0 -730
  200. package/dist/chunk-A77RUEWL.js.map +0 -1
  201. package/dist/chunk-AA4IKMPE.mjs +0 -3
  202. package/dist/chunk-AA4IKMPE.mjs.map +0 -1
  203. package/dist/chunk-AKIA6GW6.mjs +0 -163
  204. package/dist/chunk-AKIA6GW6.mjs.map +0 -1
  205. package/dist/chunk-AL6P275L.mjs +0 -435
  206. package/dist/chunk-AL6P275L.mjs.map +0 -1
  207. package/dist/chunk-AZ3RJYTB.js +0 -37
  208. package/dist/chunk-AZ3RJYTB.js.map +0 -1
  209. package/dist/chunk-B5Q4UPL6.js +0 -32
  210. package/dist/chunk-B5Q4UPL6.js.map +0 -1
  211. package/dist/chunk-B6DHPMDP.mjs +0 -335
  212. package/dist/chunk-B6DHPMDP.mjs.map +0 -1
  213. package/dist/chunk-BDXKKMBZ.mjs +0 -184
  214. package/dist/chunk-BDXKKMBZ.mjs.map +0 -1
  215. package/dist/chunk-BL6E2DLZ.mjs +0 -52
  216. package/dist/chunk-BL6E2DLZ.mjs.map +0 -1
  217. package/dist/chunk-CGFDS4XS.mjs +0 -121
  218. package/dist/chunk-CGFDS4XS.mjs.map +0 -1
  219. package/dist/chunk-CJIW5TKI.js +0 -139
  220. package/dist/chunk-CJIW5TKI.js.map +0 -1
  221. package/dist/chunk-CKNISJOQ.js +0 -314
  222. package/dist/chunk-CKNISJOQ.js.map +0 -1
  223. package/dist/chunk-D6CBOECS.mjs +0 -1757
  224. package/dist/chunk-D6CBOECS.mjs.map +0 -1
  225. package/dist/chunk-DDWEVC2S.js +0 -166
  226. package/dist/chunk-DDWEVC2S.js.map +0 -1
  227. package/dist/chunk-DZ556D2F.mjs +0 -176
  228. package/dist/chunk-DZ556D2F.mjs.map +0 -1
  229. package/dist/chunk-E2KQFV3O.mjs +0 -10
  230. package/dist/chunk-E2KQFV3O.mjs.map +0 -1
  231. package/dist/chunk-EMMLADSC.js +0 -126
  232. package/dist/chunk-EMMLADSC.js.map +0 -1
  233. package/dist/chunk-EP4WOI5D.mjs +0 -926
  234. package/dist/chunk-EP4WOI5D.mjs.map +0 -1
  235. package/dist/chunk-FJRXLJC2.mjs +0 -160
  236. package/dist/chunk-FJRXLJC2.mjs.map +0 -1
  237. package/dist/chunk-FKQI434R.js +0 -345
  238. package/dist/chunk-FKQI434R.js.map +0 -1
  239. package/dist/chunk-FPKEAJRZ.mjs +0 -100
  240. package/dist/chunk-FPKEAJRZ.mjs.map +0 -1
  241. package/dist/chunk-FWQYB22U.js +0 -183
  242. package/dist/chunk-FWQYB22U.js.map +0 -1
  243. package/dist/chunk-GD5GHTMA.js +0 -189
  244. package/dist/chunk-GD5GHTMA.js.map +0 -1
  245. package/dist/chunk-GE5XTSDZ.js +0 -447
  246. package/dist/chunk-GE5XTSDZ.js.map +0 -1
  247. package/dist/chunk-GVE47ZAX.mjs +0 -32
  248. package/dist/chunk-GVE47ZAX.mjs.map +0 -1
  249. package/dist/chunk-HK46BT5U.mjs +0 -18
  250. package/dist/chunk-HK46BT5U.mjs.map +0 -1
  251. package/dist/chunk-HQVRMR6N.js +0 -365
  252. package/dist/chunk-HQVRMR6N.js.map +0 -1
  253. package/dist/chunk-HSGBJPJO.mjs +0 -398
  254. package/dist/chunk-HSGBJPJO.mjs.map +0 -1
  255. package/dist/chunk-I3AUTOMZ.mjs +0 -125
  256. package/dist/chunk-I3AUTOMZ.mjs.map +0 -1
  257. package/dist/chunk-IEI5LD5C.mjs +0 -1161
  258. package/dist/chunk-IEI5LD5C.mjs.map +0 -1
  259. package/dist/chunk-IIPTC2X7.mjs +0 -118
  260. package/dist/chunk-IIPTC2X7.mjs.map +0 -1
  261. package/dist/chunk-J7TLHF2Q.js +0 -4
  262. package/dist/chunk-J7TLHF2Q.js.map +0 -1
  263. package/dist/chunk-JJOWXFXQ.mjs +0 -765
  264. package/dist/chunk-JJOWXFXQ.mjs.map +0 -1
  265. package/dist/chunk-JPTSS2OA.mjs +0 -3
  266. package/dist/chunk-JPTSS2OA.mjs.map +0 -1
  267. package/dist/chunk-KFXXRLTP.js +0 -396
  268. package/dist/chunk-KFXXRLTP.js.map +0 -1
  269. package/dist/chunk-KPRRBSG6.mjs +0 -272
  270. package/dist/chunk-KPRRBSG6.mjs.map +0 -1
  271. package/dist/chunk-NFSBGRDB.mjs +0 -57
  272. package/dist/chunk-NFSBGRDB.mjs.map +0 -1
  273. package/dist/chunk-NGJVCFTM.js +0 -219
  274. package/dist/chunk-NGJVCFTM.js.map +0 -1
  275. package/dist/chunk-NSQ6MZJ6.mjs +0 -728
  276. package/dist/chunk-NSQ6MZJ6.mjs.map +0 -1
  277. package/dist/chunk-NYQYHT76.mjs +0 -296
  278. package/dist/chunk-NYQYHT76.mjs.map +0 -1
  279. package/dist/chunk-OLJJGI5B.js +0 -1193
  280. package/dist/chunk-OLJJGI5B.js.map +0 -1
  281. package/dist/chunk-Q3572X2J.js +0 -292
  282. package/dist/chunk-Q3572X2J.js.map +0 -1
  283. package/dist/chunk-QH7N7D4I.mjs +0 -210
  284. package/dist/chunk-QH7N7D4I.mjs.map +0 -1
  285. package/dist/chunk-R7XUIV25.js +0 -466
  286. package/dist/chunk-R7XUIV25.js.map +0 -1
  287. package/dist/chunk-RFFO4KPM.js +0 -135
  288. package/dist/chunk-RFFO4KPM.js.map +0 -1
  289. package/dist/chunk-RFX7QKA7.mjs +0 -180
  290. package/dist/chunk-RFX7QKA7.mjs.map +0 -1
  291. package/dist/chunk-SN5LFAP3.js +0 -940
  292. package/dist/chunk-SN5LFAP3.js.map +0 -1
  293. package/dist/chunk-T4COXKQ3.js +0 -24
  294. package/dist/chunk-T4COXKQ3.js.map +0 -1
  295. package/dist/chunk-TS54QM27.js +0 -125
  296. package/dist/chunk-TS54QM27.js.map +0 -1
  297. package/dist/chunk-UE2S4PCX.mjs +0 -220
  298. package/dist/chunk-UE2S4PCX.mjs.map +0 -1
  299. package/dist/chunk-UTW3QX2A.mjs +0 -282
  300. package/dist/chunk-UTW3QX2A.mjs.map +0 -1
  301. package/dist/chunk-V74LGMAE.js +0 -1767
  302. package/dist/chunk-V74LGMAE.js.map +0 -1
  303. package/dist/chunk-VIREG536.js +0 -12
  304. package/dist/chunk-VIREG536.js.map +0 -1
  305. package/dist/chunk-VY7M7346.js +0 -4
  306. package/dist/chunk-VY7M7346.js.map +0 -1
  307. package/dist/chunk-W3TJOO7H.mjs +0 -319
  308. package/dist/chunk-W3TJOO7H.mjs.map +0 -1
  309. package/dist/chunk-WIUOB36M.js +0 -54
  310. package/dist/chunk-WIUOB36M.js.map +0 -1
  311. package/dist/chunk-WJGLM4CY.js +0 -291
  312. package/dist/chunk-WJGLM4CY.js.map +0 -1
  313. package/dist/chunk-WNURH5OO.mjs +0 -453
  314. package/dist/chunk-WNURH5OO.mjs.map +0 -1
  315. package/dist/chunk-X25TNRSD.mjs +0 -364
  316. package/dist/chunk-X25TNRSD.mjs.map +0 -1
  317. package/dist/chunk-Y3GT7ETK.js +0 -108
  318. package/dist/chunk-Y3GT7ETK.js.map +0 -1
  319. package/dist/chunk-Z4FRNOF6.mjs +0 -115
  320. package/dist/chunk-Z4FRNOF6.mjs.map +0 -1
  321. package/dist/chunk-ZMYLD3BN.js +0 -166
  322. package/dist/chunk-ZMYLD3BN.js.map +0 -1
  323. package/dist/chunk-ZP2KV6EX.js +0 -815
  324. package/dist/chunk-ZP2KV6EX.js.map +0 -1
  325. package/dist/chunk-ZVKXFELU.js +0 -366
  326. package/dist/chunk-ZVKXFELU.js.map +0 -1
@@ -1,54 +1,470 @@
1
+ "use client";
1
2
  'use strict';
2
3
 
3
- var chunkGE5XTSDZ_js = require('../../chunk-GE5XTSDZ.js');
4
- require('../../chunk-VIREG536.js');
5
- require('../../chunk-T4COXKQ3.js');
4
+ var react = require('react');
5
+ var reactAriaComponents = require('react-aria-components');
6
+ var classVarianceAuthority = require('class-variance-authority');
7
+ var clsx = require('clsx');
8
+ var tailwindMerge = require('tailwind-merge');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+ var zod = require('zod');
6
11
 
7
-
8
-
9
- Object.defineProperty(exports, "AVATAR_OVERLAP_BY_SIZE", {
10
- enumerable: true,
11
- get: function () { return chunkGE5XTSDZ_js.AVATAR_OVERLAP_BY_SIZE; }
12
- });
13
- Object.defineProperty(exports, "Avatar", {
14
- enumerable: true,
15
- get: function () { return chunkGE5XTSDZ_js.Avatar; }
16
- });
17
- Object.defineProperty(exports, "AvatarGroup", {
18
- enumerable: true,
19
- get: function () { return chunkGE5XTSDZ_js.AvatarGroup; }
20
- });
21
- Object.defineProperty(exports, "AvatarGroupPropsSchema", {
22
- enumerable: true,
23
- get: function () { return chunkGE5XTSDZ_js.AvatarGroupPropsSchema; }
24
- });
25
- Object.defineProperty(exports, "AvatarPropsSchema", {
26
- enumerable: true,
27
- get: function () { return chunkGE5XTSDZ_js.AvatarPropsSchema; }
12
+ // src/elements/Avatar/Avatar.tsx
13
+ function cn(...inputs) {
14
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
15
+ }
16
+ function getInitials(name) {
17
+ if (!name || !name.trim()) return "";
18
+ const parts = name.trim().split(/\s+/).filter(Boolean);
19
+ if (parts.length === 0) return "";
20
+ const firstPart = parts[0];
21
+ if (parts.length === 1) {
22
+ return firstPart ? firstPart.charAt(0).toUpperCase() : "";
23
+ }
24
+ const lastPart = parts[parts.length - 1];
25
+ const first = firstPart ? firstPart.charAt(0).toUpperCase() : "";
26
+ const last = lastPart ? lastPart.charAt(0).toUpperCase() : "";
27
+ return first + last;
28
+ }
29
+ var avatarOuterVariants = classVarianceAuthority.cva(
30
+ "relative inline-flex items-center justify-center",
31
+ {
32
+ variants: {
33
+ size: {
34
+ sm: "min-h-[44px] min-w-[44px]",
35
+ default: "min-h-[44px] min-w-[44px]",
36
+ lg: "min-h-[48px] min-w-[48px]"
37
+ },
38
+ interactive: {
39
+ true: [
40
+ "cursor-pointer",
41
+ "focus-visible:outline-none",
42
+ "focus-visible:ring-2",
43
+ "focus-visible:ring-[var(--ring)]",
44
+ "focus-visible:ring-offset-2",
45
+ "focus-visible:ring-offset-[var(--page-background)]",
46
+ "motion-safe:transition-transform",
47
+ "motion-safe:duration-150",
48
+ "motion-safe:ease-out",
49
+ "motion-safe:hover:scale-105"
50
+ ],
51
+ false: ""
52
+ }
53
+ },
54
+ defaultVariants: {
55
+ size: "default",
56
+ interactive: false
57
+ }
58
+ }
59
+ );
60
+ var avatarVisualVariants = classVarianceAuthority.cva(
61
+ [
62
+ "relative inline-flex items-center justify-center",
63
+ "overflow-hidden",
64
+ "bg-[var(--accent-background)]",
65
+ "text-[var(--accent-foreground)]",
66
+ "font-medium",
67
+ "select-none"
68
+ ],
69
+ {
70
+ variants: {
71
+ size: {
72
+ sm: "h-8 w-8 text-xs",
73
+ default: "h-11 w-11 text-sm",
74
+ lg: "h-12 w-12 text-base"
75
+ },
76
+ shape: {
77
+ circle: "rounded-full",
78
+ rounded: "rounded-lg"
79
+ },
80
+ status: {
81
+ online: "ring-[3px] ring-[var(--status-online,#22c55e)]",
82
+ offline: "ring-[3px] ring-[var(--status-offline,#6b7280)]",
83
+ busy: "ring-[3px] ring-[var(--status-busy,#ef4444)]",
84
+ away: "ring-[3px] ring-[var(--status-away,#eab308)]"
85
+ }
86
+ },
87
+ defaultVariants: {
88
+ size: "default",
89
+ shape: "circle"
90
+ }
91
+ }
92
+ );
93
+ var Avatar = react.memo(
94
+ react.forwardRef(
95
+ ({
96
+ src,
97
+ alt,
98
+ name,
99
+ size = "default",
100
+ shape = "circle",
101
+ status,
102
+ onPress,
103
+ className,
104
+ id,
105
+ "data-testid": dataTestId,
106
+ "aria-label": ariaLabel,
107
+ "aria-labelledby": ariaLabelledBy,
108
+ "aria-describedby": ariaDescribedBy,
109
+ _groupIndex
110
+ }, ref) => {
111
+ const [loadingState, setLoadingState] = react.useState(
112
+ src ? "loading" : "error"
113
+ );
114
+ const handleImageLoad = react.useCallback(() => {
115
+ setLoadingState("loaded");
116
+ }, []);
117
+ const handleImageError = react.useCallback(() => {
118
+ setLoadingState("error");
119
+ }, []);
120
+ const initials = getInitials(name);
121
+ const isInteractive = Boolean(onPress);
122
+ const computedAriaLabel = ariaLabel || (isInteractive && status ? `${alt}, ${status}` : void 0);
123
+ const groupBrightness = _groupIndex !== void 0 ? 1 - _groupIndex * 0.05 : void 0;
124
+ const visualContent = /* @__PURE__ */ jsxRuntime.jsxs(
125
+ "div",
126
+ {
127
+ "data-testid": "avatar-visual",
128
+ className: cn(avatarVisualVariants({ size, shape, status })),
129
+ style: groupBrightness !== void 0 ? { filter: `brightness(${groupBrightness})` } : void 0,
130
+ children: [
131
+ loadingState === "loading" && /* @__PURE__ */ jsxRuntime.jsx(
132
+ "div",
133
+ {
134
+ "data-testid": "avatar-skeleton",
135
+ className: cn(
136
+ "absolute inset-0 animate-pulse bg-[var(--accent-background)]",
137
+ shape === "circle" ? "rounded-full" : "rounded-lg"
138
+ ),
139
+ "aria-hidden": "true"
140
+ }
141
+ ),
142
+ src && loadingState !== "error" && // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- onLoad/onError are lifecycle events, not interactions
143
+ /* @__PURE__ */ jsxRuntime.jsx(
144
+ "img",
145
+ {
146
+ src,
147
+ alt: isInteractive ? "" : alt,
148
+ onLoad: handleImageLoad,
149
+ onError: handleImageError,
150
+ className: cn(
151
+ "h-full w-full object-cover",
152
+ loadingState === "loading" && "invisible"
153
+ )
154
+ }
155
+ ),
156
+ loadingState === "error" && initials && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: initials })
157
+ ]
158
+ }
159
+ );
160
+ if (isInteractive) {
161
+ return /* @__PURE__ */ jsxRuntime.jsx(
162
+ reactAriaComponents.Button,
163
+ {
164
+ ref,
165
+ id,
166
+ "data-testid": dataTestId || "avatar-outer",
167
+ "aria-label": computedAriaLabel || alt,
168
+ "aria-labelledby": ariaLabelledBy,
169
+ "aria-describedby": ariaDescribedBy,
170
+ onPress,
171
+ className: cn(
172
+ avatarOuterVariants({ size, interactive: true }),
173
+ className
174
+ ),
175
+ children: visualContent
176
+ }
177
+ );
178
+ }
179
+ return /* @__PURE__ */ jsxRuntime.jsx(
180
+ "div",
181
+ {
182
+ ref,
183
+ id,
184
+ "data-testid": dataTestId || "avatar-outer",
185
+ "aria-label": ariaLabel,
186
+ "aria-labelledby": ariaLabelledBy,
187
+ "aria-describedby": ariaDescribedBy,
188
+ className: cn(
189
+ avatarOuterVariants({ size, interactive: false }),
190
+ className
191
+ ),
192
+ children: visualContent
193
+ }
194
+ );
195
+ }
196
+ )
197
+ );
198
+ Avatar.displayName = "Avatar";
199
+ var OVERLAP_CLASS_BY_SIZE = {
200
+ sm: "-ml-2",
201
+ default: "-ml-3",
202
+ lg: "-ml-4"
203
+ };
204
+ var avatarOverflowVariants = classVarianceAuthority.cva(
205
+ [
206
+ "relative inline-flex items-center justify-center",
207
+ "bg-[var(--secondary)]",
208
+ "text-[var(--secondary-foreground)]",
209
+ "font-semibold",
210
+ "select-none",
211
+ "border-2 border-[var(--page-background)]"
212
+ ],
213
+ {
214
+ variants: {
215
+ size: {
216
+ sm: "h-8 w-8 text-xs",
217
+ default: "h-11 w-11 text-sm",
218
+ lg: "h-12 w-12 text-base"
219
+ },
220
+ shape: {
221
+ circle: "rounded-full",
222
+ rounded: "rounded-lg"
223
+ }
224
+ },
225
+ defaultVariants: {
226
+ size: "default",
227
+ shape: "circle"
228
+ }
229
+ }
230
+ );
231
+ var avatarGroupVariants = classVarianceAuthority.cva("inline-flex items-center", {
232
+ variants: {
233
+ interactive: {
234
+ true: [
235
+ "cursor-pointer",
236
+ "focus-visible:outline-none",
237
+ "focus-visible:ring-2",
238
+ "focus-visible:ring-[var(--ring)]",
239
+ "focus-visible:ring-offset-2",
240
+ "focus-visible:ring-offset-[var(--page-background)]"
241
+ ],
242
+ false: ""
243
+ }
244
+ },
245
+ defaultVariants: {
246
+ interactive: false
247
+ }
28
248
  });
29
- Object.defineProperty(exports, "AvatarShapeSchema", {
30
- enumerable: true,
31
- get: function () { return chunkGE5XTSDZ_js.AvatarShapeSchema; }
249
+ var AvatarGroup = react.memo(
250
+ react.forwardRef(
251
+ ({
252
+ max = 3,
253
+ shape = "circle",
254
+ size = "default",
255
+ onPress,
256
+ className,
257
+ children,
258
+ id,
259
+ "data-testid": dataTestId,
260
+ "aria-label": ariaLabel,
261
+ "aria-labelledby": ariaLabelledBy,
262
+ "aria-describedby": ariaDescribedBy
263
+ }, ref) => {
264
+ const childArray = react.Children.toArray(children);
265
+ const totalCount = childArray.length;
266
+ const visibleCount = Math.min(totalCount, max);
267
+ const overflowCount = totalCount - visibleCount;
268
+ const isGroupInteractive = Boolean(onPress);
269
+ const overlapClass = OVERLAP_CLASS_BY_SIZE[size];
270
+ if (process.env.NODE_ENV !== "production" && isGroupInteractive) {
271
+ react.Children.forEach(children, (child) => {
272
+ if (react.isValidElement(child) && child.props.onPress) {
273
+ console.warn(
274
+ "[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. Actions are mutually exclusive - use group onPress OR individual onPress, not both."
275
+ );
276
+ }
277
+ });
278
+ }
279
+ const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {
280
+ if (!react.isValidElement(child)) return null;
281
+ const childProps = child.props;
282
+ const zIndex = visibleCount - index;
283
+ const individualOnPress = isGroupInteractive ? void 0 : childProps.onPress;
284
+ return /* @__PURE__ */ jsxRuntime.jsx(
285
+ "div",
286
+ {
287
+ "data-testid": "avatar-group-item",
288
+ className: cn(
289
+ "relative",
290
+ // 2px border for visual separation (Clarification 1 & 7)
291
+ "border-2 border-[var(--page-background)]",
292
+ shape === "circle" ? "rounded-full" : "rounded-lg",
293
+ // Overlap negative margin (except first)
294
+ index > 0 && overlapClass,
295
+ // Hover lift animation when group is NOT actionable
296
+ // Individual avatars get lift effect regardless of their own onPress
297
+ !isGroupInteractive && [
298
+ "motion-safe:transition-transform",
299
+ "motion-safe:duration-150",
300
+ "motion-safe:ease-out",
301
+ "motion-safe:hover:scale-[1.15]",
302
+ "motion-safe:hover:z-10"
303
+ ]
304
+ ),
305
+ style: { zIndex },
306
+ children: react.cloneElement(child, {
307
+ size,
308
+ shape,
309
+ // Individual onPress is ignored if group has onPress
310
+ onPress: individualOnPress,
311
+ // Pass group index for progressive darkening
312
+ _groupIndex: index,
313
+ // Remove outer wrapper styling since we handle it
314
+ className: cn(
315
+ childProps.className,
316
+ // Remove min dimensions since group item handles touch target
317
+ "!min-h-0 !min-w-0"
318
+ )
319
+ })
320
+ },
321
+ index
322
+ );
323
+ });
324
+ const overflowIndicator = overflowCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
325
+ "div",
326
+ {
327
+ "data-testid": "avatar-overflow",
328
+ className: cn(
329
+ avatarOverflowVariants({ size, shape }),
330
+ overlapClass
331
+ ),
332
+ style: { zIndex: 0 },
333
+ "aria-hidden": "true",
334
+ children: [
335
+ "+",
336
+ overflowCount
337
+ ]
338
+ }
339
+ ) : null;
340
+ const computedAriaLabel = ariaLabel || `${totalCount} ${totalCount === 1 ? "member" : "members"}`;
341
+ const groupContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
342
+ visibleAvatars,
343
+ overflowIndicator
344
+ ] });
345
+ if (isGroupInteractive) {
346
+ return /* @__PURE__ */ jsxRuntime.jsx(
347
+ reactAriaComponents.Button,
348
+ {
349
+ ref,
350
+ id,
351
+ "data-testid": dataTestId,
352
+ "aria-label": computedAriaLabel,
353
+ "aria-labelledby": ariaLabelledBy,
354
+ "aria-describedby": ariaDescribedBy,
355
+ onPress,
356
+ className: cn(avatarGroupVariants({ interactive: true }), className),
357
+ children: groupContent
358
+ }
359
+ );
360
+ }
361
+ return /* @__PURE__ */ jsxRuntime.jsx(
362
+ "div",
363
+ {
364
+ ref,
365
+ id,
366
+ "data-testid": dataTestId,
367
+ role: "group",
368
+ "aria-label": computedAriaLabel,
369
+ "aria-labelledby": ariaLabelledBy,
370
+ "aria-describedby": ariaDescribedBy,
371
+ className: cn(avatarGroupVariants({ interactive: false }), className),
372
+ children: groupContent
373
+ }
374
+ );
375
+ }
376
+ )
377
+ );
378
+ AvatarGroup.displayName = "AvatarGroup";
379
+ var BaseComponentPropsSchema = zod.z.object({
380
+ // Styling
381
+ className: zod.z.string().optional(),
382
+ // React
383
+ children: zod.z.any().optional(),
384
+ // ReactNode not directly supported by Zod
385
+ id: zod.z.string().optional(),
386
+ // Accessibility (WCAG 2.2 AA requirements)
387
+ "aria-label": zod.z.string().optional(),
388
+ "aria-labelledby": zod.z.string().optional(),
389
+ "aria-describedby": zod.z.string().optional(),
390
+ "aria-live": zod.z.enum(["off", "polite", "assertive"]).optional(),
391
+ "aria-hidden": zod.z.boolean().optional(),
392
+ // Testing & Development
393
+ "data-testid": zod.z.string().optional()
32
394
  });
33
- Object.defineProperty(exports, "AvatarSizeSchema", {
34
- enumerable: true,
35
- get: function () { return chunkGE5XTSDZ_js.AvatarSizeSchema; }
36
- });
37
- Object.defineProperty(exports, "AvatarStatusSchema", {
38
- enumerable: true,
39
- get: function () { return chunkGE5XTSDZ_js.AvatarStatusSchema; }
40
- });
41
- Object.defineProperty(exports, "avatarOuterVariants", {
42
- enumerable: true,
43
- get: function () { return chunkGE5XTSDZ_js.avatarOuterVariants; }
44
- });
45
- Object.defineProperty(exports, "avatarOverflowVariants", {
46
- enumerable: true,
47
- get: function () { return chunkGE5XTSDZ_js.avatarOverflowVariants; }
395
+
396
+ // src/elements/Avatar/Avatar.types.ts
397
+ var AvatarSizeSchema = zod.z.enum(["sm", "default", "lg"]);
398
+ var AvatarShapeSchema = zod.z.enum(["circle", "rounded"]);
399
+ var AvatarStatusSchema = zod.z.enum(["online", "offline", "busy", "away"]);
400
+ var AVATAR_OVERLAP_BY_SIZE = {
401
+ sm: 8,
402
+ default: 12,
403
+ lg: 16
404
+ };
405
+ var AvatarPropsSchema = BaseComponentPropsSchema.extend({
406
+ /** Image source URL */
407
+ src: zod.z.string().optional(),
408
+ /**
409
+ * Alt text for the avatar image (required for accessibility)
410
+ * When actionable, this is used for the aria-label
411
+ */
412
+ alt: zod.z.string().min(1, "alt is required for accessibility"),
413
+ /**
414
+ * Name used to generate initials fallback
415
+ * - "Jane Doe" → "JD"
416
+ * - "Jane" → "J"
417
+ * - "Jane Marie Doe" → "JD" (first and last)
418
+ */
419
+ name: zod.z.string().optional(),
420
+ /** Visual size variant (touch target always 44x44px min for sm/default) */
421
+ size: AvatarSizeSchema.optional().default("default"),
422
+ /** Shape variant */
423
+ shape: AvatarShapeSchema.optional().default("circle"),
424
+ /** Status ring indicator */
425
+ status: AvatarStatusSchema.optional(),
426
+ /**
427
+ * Click handler for actionable avatar
428
+ * When provided, renders as Button with 3-layer architecture
429
+ * @see plan.md Clarification 2 (mutually exclusive with group onPress)
430
+ */
431
+ onPress: zod.z.function().optional()
48
432
  });
49
- Object.defineProperty(exports, "avatarVisualVariants", {
50
- enumerable: true,
51
- get: function () { return chunkGE5XTSDZ_js.avatarVisualVariants; }
433
+ var AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({
434
+ /**
435
+ * Maximum avatars to display before overflow (+X indicator)
436
+ * Default: 3
437
+ */
438
+ max: zod.z.number().int().positive().optional().default(3),
439
+ /**
440
+ * Shape inherited by all child avatars
441
+ * Individual avatar shape props are ignored in groups
442
+ */
443
+ shape: AvatarShapeSchema.optional().default("circle"),
444
+ /**
445
+ * Size inherited by all child avatars
446
+ * Individual avatar size props are ignored in groups
447
+ * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)
448
+ */
449
+ size: AvatarSizeSchema.optional().default("default"),
450
+ /**
451
+ * Click handler for the entire group
452
+ * When provided, individual avatar onPress handlers are ignored
453
+ * @see plan.md Clarification 2 (Mutually exclusive)
454
+ */
455
+ onPress: zod.z.function().optional()
52
456
  });
457
+
458
+ exports.AVATAR_OVERLAP_BY_SIZE = AVATAR_OVERLAP_BY_SIZE;
459
+ exports.Avatar = Avatar;
460
+ exports.AvatarGroup = AvatarGroup;
461
+ exports.AvatarGroupPropsSchema = AvatarGroupPropsSchema;
462
+ exports.AvatarPropsSchema = AvatarPropsSchema;
463
+ exports.AvatarShapeSchema = AvatarShapeSchema;
464
+ exports.AvatarSizeSchema = AvatarSizeSchema;
465
+ exports.AvatarStatusSchema = AvatarStatusSchema;
466
+ exports.avatarOuterVariants = avatarOuterVariants;
467
+ exports.avatarOverflowVariants = avatarOverflowVariants;
468
+ exports.avatarVisualVariants = avatarVisualVariants;
53
469
  //# sourceMappingURL=index.js.map
54
470
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
1
+ {"version":3,"sources":["../../../src/utils/cn.ts","../../../src/elements/Avatar/Avatar.tsx","../../../src/elements/Avatar/AvatarGroup.tsx","../../../src/schemas/BaseComponentProps.ts","../../../src/elements/Avatar/Avatar.types.ts"],"names":["twMerge","clsx","cva","memo","forwardRef","useState","useCallback","jsxs","jsx","AriaButton","Children","isValidElement","Fragment","z"],"mappings":";;;;;;;;;;;AAcO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAOA,qBAAA,CAAQC,SAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACkBA,SAAS,YAAY,IAAA,EAAkC;AACrD,EAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,CAAK,IAAA,IAAQ,OAAO,EAAA;AAElC,EAAA,MAAM,KAAA,GAAQ,KAAK,IAAA,EAAK,CAAE,MAAM,KAAK,CAAA,CAAE,OAAO,OAAO,CAAA;AACrD,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAE/B,EAAA,MAAM,SAAA,GAAY,MAAM,CAAC,CAAA;AACzB,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,YAAY,SAAA,CAAU,MAAA,CAAO,CAAC,CAAA,CAAE,aAAY,GAAI,EAAA;AAAA,EACzD;AAGA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AACvC,EAAA,MAAM,QAAQ,SAAA,GAAY,SAAA,CAAU,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,EAAA;AAC9D,EAAA,MAAM,OAAO,QAAA,GAAW,QAAA,CAAS,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,EAAA;AAC3D,EAAA,OAAO,KAAA,GAAQ,IAAA;AACjB;AAOO,IAAM,mBAAA,GAAsBC,0BAAA;AAAA,EACjC,kDAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,2BAAA;AAAA,QACJ,OAAA,EAAS,2BAAA;AAAA,QACT,EAAA,EAAI;AAAA,OACN;AAAA,MACA,WAAA,EAAa;AAAA,QACX,IAAA,EAAM;AAAA,UACJ,gBAAA;AAAA,UACA,4BAAA;AAAA,UACA,sBAAA;AAAA,UACA,kCAAA;AAAA,UACA,6BAAA;AAAA,UACA,oDAAA;AAAA,UACA,kCAAA;AAAA,UACA,0BAAA;AAAA,UACA,sBAAA;AAAA,UACA;AAAA,SACF;AAAA,QACA,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM,SAAA;AAAA,MACN,WAAA,EAAa;AAAA;AACf;AAEJ;AAMO,IAAM,oBAAA,GAAuBA,0BAAA;AAAA,EAClC;AAAA,IACE,kDAAA;AAAA,IACA,iBAAA;AAAA,IACA,+BAAA;AAAA,IACA,iCAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,iBAAA;AAAA,QACJ,OAAA,EAAS,mBAAA;AAAA,QACT,EAAA,EAAI;AAAA,OACN;AAAA,MACA,KAAA,EAAO;AAAA,QACL,MAAA,EAAQ,cAAA;AAAA,QACR,OAAA,EAAS;AAAA,OACX;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,MAAA,EAAQ,gDAAA;AAAA,QACR,OAAA,EAAS,iDAAA;AAAA,QACT,IAAA,EAAM,8CAAA;AAAA,QACN,IAAA,EAAM;AAAA;AACR,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO;AAAA;AACT;AAEJ;AAUA,IAAM,MAAA,GAASC,UAAA;AAAA,EACbC,gBAAA;AAAA,IACE,CACE;AAAA,MACE,GAAA;AAAA,MACA,GAAA;AAAA,MACA,IAAA;AAAA,MACA,IAAA,GAAO,SAAA;AAAA,MACP,KAAA,GAAQ,QAAA;AAAA,MACR,MAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,EAAA;AAAA,MACA,aAAA,EAAe,UAAA;AAAA,MACf,YAAA,EAAc,SAAA;AAAA,MACd,iBAAA,EAAmB,cAAA;AAAA,MACnB,kBAAA,EAAoB,eAAA;AAAA,MACpB;AAAA,OAEF,GAAA,KACiB;AAEjB,MAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAIC,cAAA;AAAA,QACtC,MAAM,SAAA,GAAY;AAAA,OACpB;AAEA,MAAA,MAAM,eAAA,GAAkBC,kBAAY,MAAM;AACxC,QAAA,eAAA,CAAgB,QAAQ,CAAA;AAAA,MAC1B,CAAA,EAAG,EAAE,CAAA;AAEL,MAAA,MAAM,gBAAA,GAAmBA,kBAAY,MAAM;AACzC,QAAA,eAAA,CAAgB,OAAO,CAAA;AAAA,MACzB,CAAA,EAAG,EAAE,CAAA;AAEL,MAAA,MAAM,QAAA,GAAW,YAAY,IAAI,CAAA;AACjC,MAAA,MAAM,aAAA,GAAgB,QAAQ,OAAO,CAAA;AAGrC,MAAA,MAAM,iBAAA,GACJ,cACC,aAAA,IAAiB,MAAA,GAAS,GAAG,GAAG,CAAA,EAAA,EAAK,MAAM,CAAA,CAAA,GAAK,MAAA,CAAA;AAInD,MAAA,MAAM,eAAA,GACJ,WAAA,KAAgB,MAAA,GAAY,CAAA,GAAI,cAAc,IAAA,GAAO,MAAA;AAGvD,MAAA,MAAM,aAAA,mBACJC,eAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,aAAA,EAAY,eAAA;AAAA,UACZ,SAAA,EAAW,GAAG,oBAAA,CAAqB,EAAE,MAAM,KAAA,EAAO,MAAA,EAAQ,CAAC,CAAA;AAAA,UAC3D,KAAA,EAAO,oBAAoB,MAAA,GAAY,EAAE,QAAQ,CAAA,WAAA,EAAc,eAAe,KAAI,GAAI,MAAA;AAAA,UAKrF,QAAA,EAAA;AAAA,YAAA,YAAA,KAAiB,SAAA,oBAChBC,cAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,aAAA,EAAY,iBAAA;AAAA,gBACZ,SAAA,EAAW,EAAA;AAAA,kBACT,8DAAA;AAAA,kBACA,KAAA,KAAU,WAAW,cAAA,GAAiB;AAAA,iBACxC;AAAA,gBACA,aAAA,EAAY;AAAA;AAAA,aACd;AAAA,YAID,OAAO,YAAA,KAAiB,OAAA;AAAA,4BAEvBA,cAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,GAAA;AAAA,gBACA,GAAA,EAAK,gBAAgB,EAAA,GAAK,GAAA;AAAA,gBAC1B,MAAA,EAAQ,eAAA;AAAA,gBACR,OAAA,EAAS,gBAAA;AAAA,gBACT,SAAA,EAAW,EAAA;AAAA,kBACT,4BAAA;AAAA,kBACA,iBAAiB,SAAA,IAAa;AAAA;AAChC;AAAA,aACF;AAAA,YAID,iBAAiB,OAAA,IAAW,QAAA,mCAC1B,MAAA,EAAA,EAAK,aAAA,EAAY,QAAQ,QAAA,EAAA,QAAA,EAAS;AAAA;AAAA;AAAA,OAEvC;AAIF,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,uBACEA,cAAA;AAAA,UAACC,0BAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,EAAA;AAAA,YACA,eAAa,UAAA,IAAc,cAAA;AAAA,YAC3B,cAAY,iBAAA,IAAqB,GAAA;AAAA,YACjC,iBAAA,EAAiB,cAAA;AAAA,YACjB,kBAAA,EAAkB,eAAA;AAAA,YAClB,OAAA;AAAA,YACA,SAAA,EAAW,EAAA;AAAA,cACT,mBAAA,CAAoB,EAAE,IAAA,EAAM,WAAA,EAAa,MAAM,CAAA;AAAA,cAC/C;AAAA,aACF;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,MAEJ;AAGA,MAAA,uBACED,cAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,EAAA;AAAA,UACA,eAAa,UAAA,IAAc,cAAA;AAAA,UAC3B,YAAA,EAAY,SAAA;AAAA,UACZ,iBAAA,EAAiB,cAAA;AAAA,UACjB,kBAAA,EAAkB,eAAA;AAAA,UAClB,SAAA,EAAW,EAAA;AAAA,YACT,mBAAA,CAAoB,EAAE,IAAA,EAAM,WAAA,EAAa,OAAO,CAAA;AAAA,YAChD;AAAA,WACF;AAAA,UAEC,QAAA,EAAA;AAAA;AAAA,OACH;AAAA,IAEJ;AAAA;AAEJ;AAEA,MAAA,CAAO,WAAA,GAAc,QAAA;ACxOrB,IAAM,qBAAA,GAAoD;AAAA,EACxD,EAAA,EAAI,OAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EACT,EAAA,EAAI;AACN,CAAA;AAKA,IAAM,sBAAA,GAAyBN,0BAAAA;AAAA,EAC7B;AAAA,IACE,kDAAA;AAAA,IACA,uBAAA;AAAA,IACA,oCAAA;AAAA,IACA,eAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,iBAAA;AAAA,QACJ,OAAA,EAAS,mBAAA;AAAA,QACT,EAAA,EAAI;AAAA,OACN;AAAA,MACA,KAAA,EAAO;AAAA,QACL,MAAA,EAAQ,cAAA;AAAA,QACR,OAAA,EAAS;AAAA;AACX,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO;AAAA;AACT;AAEJ;AAKA,IAAM,mBAAA,GAAsBA,2BAAI,0BAAA,EAA4B;AAAA,EAC1D,QAAA,EAAU;AAAA,IACR,WAAA,EAAa;AAAA,MACX,IAAA,EAAM;AAAA,QACJ,gBAAA;AAAA,QACA,4BAAA;AAAA,QACA,sBAAA;AAAA,QACA,kCAAA;AAAA,QACA,6BAAA;AAAA,QACA;AAAA,OACF;AAAA,MACA,KAAA,EAAO;AAAA;AACT,GACF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,WAAA,EAAa;AAAA;AAEjB,CAAC,CAAA;AAMD,IAAM,WAAA,GAAcC,UAAAA;AAAA,EAClBC,gBAAAA;AAAA,IACE,CACE;AAAA,MACE,GAAA,GAAM,CAAA;AAAA,MACN,KAAA,GAAQ,QAAA;AAAA,MACR,IAAA,GAAO,SAAA;AAAA,MACP,OAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,EAAA;AAAA,MACA,aAAA,EAAe,UAAA;AAAA,MACf,YAAA,EAAc,SAAA;AAAA,MACd,iBAAA,EAAmB,cAAA;AAAA,MACnB,kBAAA,EAAoB;AAAA,OAEtB,GAAA,KACiB;AACjB,MAAA,MAAM,UAAA,GAAaM,cAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA;AAC5C,MAAA,MAAM,aAAa,UAAA,CAAW,MAAA;AAC9B,MAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,UAAA,EAAY,GAAG,CAAA;AAC7C,MAAA,MAAM,gBAAgB,UAAA,GAAa,YAAA;AAEnC,MAAA,MAAM,kBAAA,GAAqB,QAAQ,OAAO,CAAA;AAC1C,MAAA,MAAM,YAAA,GAAe,sBAAsB,IAAI,CAAA;AAI/C,MAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,IAAgB,kBAAA,EAAoB;AAC/D,QAAAA,cAAA,CAAS,OAAA,CAAQ,QAAA,EAAU,CAAC,KAAA,KAAU;AACpC,UAAA,IACEC,oBAAA,CAAe,KAAK,CAAA,IACnB,KAAA,CAAM,MAAgC,OAAA,EACvC;AACA,YAAA,OAAA,CAAQ,IAAA;AAAA,cACN;AAAA,aAEF;AAAA,UACF;AAAA,QACF,CAAC,CAAA;AAAA,MACH;AAGA,MAAA,MAAM,cAAA,GAAiB,WAAW,KAAA,CAAM,CAAA,EAAG,YAAY,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,EAAO,KAAA,KAAU;AAC7E,QAAA,IAAI,CAACA,oBAAA,CAAe,KAAK,CAAA,EAAG,OAAO,IAAA;AAGnC,QAAA,MAAM,aAAa,KAAA,CAAM,KAAA;AAQzB,QAAA,MAAM,SAAS,YAAA,GAAe,KAAA;AAI9B,QAAA,MAAM,iBAAA,GAAoB,kBAAA,GACtB,MAAA,GACA,UAAA,CAAW,OAAA;AAEf,QAAA,uBACEH,cAAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YAEC,aAAA,EAAY,mBAAA;AAAA,YACZ,SAAA,EAAW,EAAA;AAAA,cACT,UAAA;AAAA;AAAA,cAEA,0CAAA;AAAA,cACA,KAAA,KAAU,WAAW,cAAA,GAAiB,YAAA;AAAA;AAAA,cAEtC,QAAQ,CAAA,IAAK,YAAA;AAAA;AAAA;AAAA,cAGb,CAAC,kBAAA,IAAsB;AAAA,gBACrB,kCAAA;AAAA,gBACA,0BAAA;AAAA,gBACA,sBAAA;AAAA,gBACA,gCAAA;AAAA,gBACA;AAAA;AACF,aACF;AAAA,YACA,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,YAEf,6BAAa,KAAA,EAAuB;AAAA,cACnC,IAAA;AAAA,cACA,KAAA;AAAA;AAAA,cAEA,OAAA,EAAS,iBAAA;AAAA;AAAA,cAET,WAAA,EAAa,KAAA;AAAA;AAAA,cAEb,SAAA,EAAW,EAAA;AAAA,gBACT,UAAA,CAAW,SAAA;AAAA;AAAA,gBAEX;AAAA;AACF,aAC6B;AAAA,WAAA;AAAA,UAlC1B;AAAA,SAmCP;AAAA,MAEJ,CAAC,CAAA;AAGD,MAAA,MAAM,iBAAA,GACJ,aAAA,GAAgB,CAAA,mBACdD,eAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,aAAA,EAAY,iBAAA;AAAA,UACZ,SAAA,EAAW,EAAA;AAAA,YACT,sBAAA,CAAuB,EAAE,IAAA,EAAM,KAAA,EAAO,CAAA;AAAA,YACtC;AAAA,WACF;AAAA,UACA,KAAA,EAAO,EAAE,MAAA,EAAQ,CAAA,EAAE;AAAA,UACnB,aAAA,EAAY,MAAA;AAAA,UACb,QAAA,EAAA;AAAA,YAAA,GAAA;AAAA,YACG;AAAA;AAAA;AAAA,OACJ,GACE,IAAA;AAGN,MAAA,MAAM,iBAAA,GACJ,aACA,CAAA,EAAG,UAAU,IAAI,UAAA,KAAe,CAAA,GAAI,WAAW,SAAS,CAAA,CAAA;AAG1D,MAAA,MAAM,YAAA,mBACJA,eAAAA,CAAAK,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,QAAA,cAAA;AAAA,QACA;AAAA,OAAA,EACH,CAAA;AAKF,MAAA,IAAI,kBAAA,EAAoB;AACtB,QAAA,uBACEJ,cAAAA;AAAA,UAACC,0BAAAA;AAAA,UAAA;AAAA,YACC,GAAA;AAAA,YACA,EAAA;AAAA,YACA,aAAA,EAAa,UAAA;AAAA,YACb,YAAA,EAAY,iBAAA;AAAA,YACZ,iBAAA,EAAiB,cAAA;AAAA,YACjB,kBAAA,EAAkB,eAAA;AAAA,YAClB,OAAA;AAAA,YACA,SAAA,EAAW,GAAG,mBAAA,CAAoB,EAAE,aAAa,IAAA,EAAM,GAAG,SAAS,CAAA;AAAA,YAElE,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,MAEJ;AAGA,MAAA,uBACED,cAAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA;AAAA,UACA,EAAA;AAAA,UACA,aAAA,EAAa,UAAA;AAAA,UACb,IAAA,EAAK,OAAA;AAAA,UACL,YAAA,EAAY,iBAAA;AAAA,UACZ,iBAAA,EAAiB,cAAA;AAAA,UACjB,kBAAA,EAAkB,eAAA;AAAA,UAClB,SAAA,EAAW,GAAG,mBAAA,CAAoB,EAAE,aAAa,KAAA,EAAO,GAAG,SAAS,CAAA;AAAA,UAEnE,QAAA,EAAA;AAAA;AAAA,OACH;AAAA,IAEJ;AAAA;AAEJ;AAEA,WAAA,CAAY,WAAA,GAAc,aAAA;ACpQnB,IAAM,wBAAA,GAA2BK,MAAE,MAAA,CAAO;AAAA;AAAA,EAE/C,SAAA,EAAWA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAG/B,QAAA,EAAUA,KAAA,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS;AAAA;AAAA,EAC3B,EAAA,EAAIA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAGxB,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAClC,iBAAA,EAAmBA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACvC,kBAAA,EAAoBA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACxC,WAAA,EAAaA,MAAE,IAAA,CAAK,CAAC,OAAO,QAAA,EAAU,WAAW,CAAC,CAAA,CAAE,QAAA,EAAS;AAAA,EAC7D,aAAA,EAAeA,KAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,EAAS;AAAA;AAAA,EAGpC,aAAA,EAAeA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAC5B,CAAC,CAAA;;;ACXM,IAAM,mBAAmBA,KAAAA,CAAE,IAAA,CAAK,CAAC,IAAA,EAAM,SAAA,EAAW,IAAI,CAAC;AAQvD,IAAM,oBAAoBA,KAAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,SAAS,CAAC;AAYtD,IAAM,kBAAA,GAAqBA,MAAE,IAAA,CAAK,CAAC,UAAU,SAAA,EAAW,MAAA,EAAQ,MAAM,CAAC;AAOvE,IAAM,sBAAA,GAAqD;AAAA,EAChE,EAAA,EAAI,CAAA;AAAA,EACJ,OAAA,EAAS,EAAA;AAAA,EACT,EAAA,EAAI;AACN;AAUO,IAAM,iBAAA,GAAoB,yBAAyB,MAAA,CAAO;AAAA;AAAA,EAE/D,GAAA,EAAKA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzB,KAAKA,KAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,GAAG,mCAAmC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1D,IAAA,EAAMA,KAAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA;AAAA,EAG1B,IAAA,EAAM,gBAAA,CAAiB,QAAA,EAAS,CAAE,QAAQ,SAAS,CAAA;AAAA;AAAA,EAGnD,KAAA,EAAO,iBAAA,CAAkB,QAAA,EAAS,CAAE,QAAQ,QAAQ,CAAA;AAAA;AAAA,EAGpD,MAAA,EAAQ,mBAAmB,QAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpC,OAAA,EAASA,KAAAA,CAAE,QAAA,EAAS,CAAE,QAAA;AACxB,CAAC;AAoBM,IAAM,sBAAA,GAAyB,yBAAyB,MAAA,CAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpE,GAAA,EAAKA,KAAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrD,KAAA,EAAO,iBAAA,CAAkB,QAAA,EAAS,CAAE,QAAQ,QAAQ,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpD,IAAA,EAAM,gBAAA,CAAiB,QAAA,EAAS,CAAE,QAAQ,SAAS,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOnD,OAAA,EAASA,KAAAA,CAAE,QAAA,EAAS,CAAE,QAAA;AACxB,CAAC","file":"index.js","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","'use client';\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Accessible avatar with React Aria primitives and CVA styling\n *\n * Architecture:\n * - Layer 1: Touch Target (44x44px WCAG AAA compliant)\n * - Layer 2: Visual Avatar (configurable size, shape, status ring)\n * - Layer 3: Content (image, initials, skeleton shimmer)\n *\n * @see plan.md for implementation details\n * @see avatar-prd.md for requirements\n * @see constitution.md Principle IV (Accessibility First)\n */\n\nimport { forwardRef, memo, useState, useCallback, type ReactElement } from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type {\n AvatarProps,\n AvatarSize,\n AvatarShape,\n AvatarStatus,\n AvatarLoadingState,\n} from './Avatar.types';\n\n/**\n * Extract initials from a name\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\nfunction getInitials(name: string | undefined): string {\n if (!name || !name.trim()) return '';\n\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return '';\n\n const firstPart = parts[0];\n if (parts.length === 1) {\n return firstPart ? firstPart.charAt(0).toUpperCase() : '';\n }\n\n // First and last initials (max 2 characters)\n const lastPart = parts[parts.length - 1];\n const first = firstPart ? firstPart.charAt(0).toUpperCase() : '';\n const last = lastPart ? lastPart.charAt(0).toUpperCase() : '';\n return first + last;\n}\n\n/**\n * Layer 1: Transparent outer touch target (44x44px minimum)\n * Handles WCAG 2.2 AAA touch target requirement\n * IMPORTANT: Focus ring stays on Layer 1 for AAA compliance (2.4.13)\n */\nexport const avatarOuterVariants = cva(\n 'relative inline-flex items-center justify-center',\n {\n variants: {\n size: {\n sm: 'min-h-[44px] min-w-[44px]',\n default: 'min-h-[44px] min-w-[44px]',\n lg: 'min-h-[48px] min-w-[48px]',\n },\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-105',\n ],\n false: '',\n },\n },\n defaultVariants: {\n size: 'default',\n interactive: false,\n },\n }\n);\n\n/**\n * Layer 2: Visual avatar appearance\n * Provides the visual appearance with configurable size, shape, and status ring\n */\nexport const avatarVisualVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'overflow-hidden',\n 'bg-[var(--accent-background)]',\n 'text-[var(--accent-foreground)]',\n 'font-medium',\n 'select-none',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n status: {\n online: 'ring-[3px] ring-[var(--status-online,#22c55e)]',\n offline: 'ring-[3px] ring-[var(--status-offline,#6b7280)]',\n busy: 'ring-[3px] ring-[var(--status-busy,#ef4444)]',\n away: 'ring-[3px] ring-[var(--status-away,#eab308)]',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Avatar Component - 3-Layer Architecture\n * Displays user avatar with image or initials fallback\n *\n * Layer 1: Touch Target - 44x44px WCAG AAA compliant\n * Layer 2: Visual Avatar - configurable appearance\n * Layer 3: Content - image, initials, or loading skeleton\n */\nconst Avatar = memo(\n forwardRef<HTMLDivElement, AvatarProps>(\n (\n {\n src,\n alt,\n name,\n size = 'default',\n shape = 'circle',\n status,\n onPress,\n className,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n _groupIndex,\n },\n ref\n ): ReactElement => {\n // Loading state machine: loading → loaded | error\n const [loadingState, setLoadingState] = useState<AvatarLoadingState>(\n src ? 'loading' : 'error'\n );\n\n const handleImageLoad = useCallback(() => {\n setLoadingState('loaded');\n }, []);\n\n const handleImageError = useCallback(() => {\n setLoadingState('error');\n }, []);\n\n const initials = getInitials(name);\n const isInteractive = Boolean(onPress);\n\n // Build aria-label for actionable avatars with status\n const computedAriaLabel =\n ariaLabel ||\n (isInteractive && status ? `${alt}, ${status}` : undefined);\n\n // Calculate brightness for group darkening effect\n // First avatar (index 0) = 100%, each subsequent gets 5% darker\n const groupBrightness =\n _groupIndex !== undefined ? 1 - _groupIndex * 0.05 : undefined;\n\n // Common visual layer content\n const visualContent = (\n <div\n data-testid=\"avatar-visual\"\n className={cn(avatarVisualVariants({ size, shape, status }))}\n style={groupBrightness !== undefined ? { filter: `brightness(${groupBrightness})` } : undefined}\n >\n {/* Layer 3: Content */}\n\n {/* Skeleton shimmer while loading */}\n {loadingState === 'loading' && (\n <div\n data-testid=\"avatar-skeleton\"\n className={cn(\n 'absolute inset-0 animate-pulse bg-[var(--accent-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg'\n )}\n aria-hidden=\"true\"\n />\n )}\n\n {/* Image (hidden during loading, removed on error) */}\n {src && loadingState !== 'error' && (\n // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- onLoad/onError are lifecycle events, not interactions\n <img\n src={src}\n alt={isInteractive ? '' : alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n className={cn(\n 'h-full w-full object-cover',\n loadingState === 'loading' && 'invisible'\n )}\n />\n )}\n\n {/* Initials fallback (shown when no src or on error) */}\n {loadingState === 'error' && initials && (\n <span aria-hidden=\"true\">{initials}</span>\n )}\n </div>\n );\n\n // Render as button if interactive\n if (isInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={computedAriaLabel || alt}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(\n avatarOuterVariants({ size, interactive: true }),\n className\n )}\n >\n {visualContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId || 'avatar-outer'}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(\n avatarOuterVariants({ size, interactive: false }),\n className\n )}\n >\n {visualContent}\n </div>\n );\n }\n )\n);\n\nAvatar.displayName = 'Avatar';\n\nexport { Avatar };\nexport type { AvatarProps, AvatarSize, AvatarShape, AvatarStatus };\n","'use client';\n\n/**\n * AvatarGroup Component\n * Displays multiple avatars with overlap, overflow indicator, and optional actionability\n *\n * Features:\n * - Overlapping display with configurable overlap\n * - Overflow indicator (+X) when exceeding max\n * - 2px border for visual separation\n * - First avatar on top (highest z-index)\n * - Mutually exclusive actions (group OR individual, not both)\n * - Shape/size inheritance to children\n *\n * @see plan.md Clarifications 1, 2, 5, 6, 7\n * @see avatar-prd.md US-5, US-7\n */\n\nimport {\n forwardRef,\n memo,\n Children,\n isValidElement,\n cloneElement,\n type ReactElement,\n} from 'react';\nimport { Button as AriaButton } from 'react-aria-components';\nimport { cva } from 'class-variance-authority';\nimport { cn } from '../../utils/cn';\nimport type { AvatarGroupProps, AvatarSize, AvatarShape } from './Avatar.types';\n\n/**\n * Overlap class by size (proportional)\n * sm: 8px (-ml-2), default: 12px (-ml-3), lg: 16px (-ml-4)\n */\nconst OVERLAP_CLASS_BY_SIZE: Record<AvatarSize, string> = {\n sm: '-ml-2',\n default: '-ml-3',\n lg: '-ml-4',\n};\n\n/**\n * Overflow indicator variants (the +X badge)\n */\nconst avatarOverflowVariants = cva(\n [\n 'relative inline-flex items-center justify-center',\n 'bg-[var(--secondary)]',\n 'text-[var(--secondary-foreground)]',\n 'font-semibold',\n 'select-none',\n 'border-2 border-[var(--page-background)]',\n ],\n {\n variants: {\n size: {\n sm: 'h-8 w-8 text-xs',\n default: 'h-11 w-11 text-sm',\n lg: 'h-12 w-12 text-base',\n },\n shape: {\n circle: 'rounded-full',\n rounded: 'rounded-lg',\n },\n },\n defaultVariants: {\n size: 'default',\n shape: 'circle',\n },\n }\n);\n\n/**\n * Group outer variants\n */\nconst avatarGroupVariants = cva('inline-flex items-center', {\n variants: {\n interactive: {\n true: [\n 'cursor-pointer',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n 'focus-visible:ring-offset-[var(--page-background)]',\n ],\n false: '',\n },\n },\n defaultVariants: {\n interactive: false,\n },\n});\n\n/**\n * AvatarGroup Component\n * Groups multiple Avatar components with overlap and overflow handling\n */\nconst AvatarGroup = memo(\n forwardRef<HTMLDivElement, AvatarGroupProps>(\n (\n {\n max = 3,\n shape = 'circle',\n size = 'default',\n onPress,\n className,\n children,\n id,\n 'data-testid': dataTestId,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n },\n ref\n ): ReactElement => {\n const childArray = Children.toArray(children);\n const totalCount = childArray.length;\n const visibleCount = Math.min(totalCount, max);\n const overflowCount = totalCount - visibleCount;\n\n const isGroupInteractive = Boolean(onPress);\n const overlapClass = OVERLAP_CLASS_BY_SIZE[size];\n\n // Warn about mutually exclusive actions (Clarification 2)\n // Uses !== 'production' to work in both development and test modes\n if (process.env.NODE_ENV !== 'production' && isGroupInteractive) {\n Children.forEach(children, (child) => {\n if (\n isValidElement(child) &&\n (child.props as { onPress?: unknown }).onPress\n ) {\n console.warn(\n '[AvatarGroup] onPress on individual avatars is ignored when AvatarGroup has onPress. ' +\n 'Actions are mutually exclusive - use group onPress OR individual onPress, not both.'\n );\n }\n });\n }\n\n // Build visible avatars with proper styling\n const visibleAvatars = childArray.slice(0, visibleCount).map((child, index) => {\n if (!isValidElement(child)) return null;\n\n // Clone child with inherited props and group styling\n const childProps = child.props as {\n size?: AvatarSize;\n shape?: AvatarShape;\n onPress?: unknown;\n className?: string;\n };\n\n // First avatar has highest z-index (Clarification 5)\n const zIndex = visibleCount - index;\n\n // Determine if individual avatar is interactive\n // If group has onPress, individual onPress is ignored\n const individualOnPress = isGroupInteractive\n ? undefined\n : childProps.onPress;\n\n return (\n <div\n key={index}\n data-testid=\"avatar-group-item\"\n className={cn(\n 'relative',\n // 2px border for visual separation (Clarification 1 & 7)\n 'border-2 border-[var(--page-background)]',\n shape === 'circle' ? 'rounded-full' : 'rounded-lg',\n // Overlap negative margin (except first)\n index > 0 && overlapClass,\n // Hover lift animation when group is NOT actionable\n // Individual avatars get lift effect regardless of their own onPress\n !isGroupInteractive && [\n 'motion-safe:transition-transform',\n 'motion-safe:duration-150',\n 'motion-safe:ease-out',\n 'motion-safe:hover:scale-[1.15]',\n 'motion-safe:hover:z-10',\n ]\n )}\n style={{ zIndex }}\n >\n {cloneElement(child as ReactElement, {\n size,\n shape,\n // Individual onPress is ignored if group has onPress\n onPress: individualOnPress,\n // Pass group index for progressive darkening\n _groupIndex: index,\n // Remove outer wrapper styling since we handle it\n className: cn(\n childProps.className,\n // Remove min dimensions since group item handles touch target\n '!min-h-0 !min-w-0'\n ),\n } as Partial<typeof childProps>)}\n </div>\n );\n });\n\n // Overflow indicator (+X)\n const overflowIndicator =\n overflowCount > 0 ? (\n <div\n data-testid=\"avatar-overflow\"\n className={cn(\n avatarOverflowVariants({ size, shape }),\n overlapClass\n )}\n style={{ zIndex: 0 }}\n aria-hidden=\"true\"\n >\n +{overflowCount}\n </div>\n ) : null;\n\n // Auto-generate aria-label if not provided\n const computedAriaLabel =\n ariaLabel ||\n `${totalCount} ${totalCount === 1 ? 'member' : 'members'}`;\n\n // Group content\n const groupContent = (\n <>\n {visibleAvatars}\n {overflowIndicator}\n </>\n );\n\n // Render as button if group is interactive\n // Button inherently has role=\"button\", aria-label provides group context\n if (isGroupInteractive) {\n return (\n <AriaButton\n ref={ref as React.Ref<HTMLButtonElement>}\n id={id}\n data-testid={dataTestId}\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n onPress={onPress}\n className={cn(avatarGroupVariants({ interactive: true }), className)}\n >\n {groupContent}\n </AriaButton>\n );\n }\n\n // Render as non-interactive div with role=\"group\"\n return (\n <div\n ref={ref}\n id={id}\n data-testid={dataTestId}\n role=\"group\"\n aria-label={computedAriaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n className={cn(avatarGroupVariants({ interactive: false }), className)}\n >\n {groupContent}\n </div>\n );\n }\n )\n);\n\nAvatarGroup.displayName = 'AvatarGroup';\n\nexport { AvatarGroup, avatarOverflowVariants };\nexport type { AvatarGroupProps };\n","import { z } from 'zod';\n\n/**\n * Base props schema for all Themis components\n * Ensures consistent accessibility and styling APIs across the library\n *\n * @see spec.md FR-009 to FR-014 (Accessibility Requirements)\n * @see constitution.md Principle IV (Accessibility First - WCAG 2.2 AA minimum)\n */\nexport const BaseComponentPropsSchema = z.object({\n // Styling\n className: z.string().optional(),\n\n // React\n children: z.any().optional(), // ReactNode not directly supported by Zod\n id: z.string().optional(),\n\n // Accessibility (WCAG 2.2 AA requirements)\n 'aria-label': z.string().optional(),\n 'aria-labelledby': z.string().optional(),\n 'aria-describedby': z.string().optional(),\n 'aria-live': z.enum(['off', 'polite', 'assertive']).optional(),\n 'aria-hidden': z.boolean().optional(),\n\n // Testing & Development\n 'data-testid': z.string().optional(),\n});\n\nexport type BaseComponentProps = z.infer<typeof BaseComponentPropsSchema>;\n","import { z } from 'zod';\nimport type { PressEvent } from 'react-aria-components';\nimport { BaseComponentPropsSchema } from '../../schemas/BaseComponentProps';\n\n/**\n * Avatar size schema\n *\n * Size mappings (visual | touch target | overlap):\n * - sm: 32px | 44x44px | 8px\n * - default: 44px | 44x44px | 12px\n * - lg: 48px | 48x48px | 16px\n *\n * @see plan.md Clarification 6 (Overlap Scale)\n * @see spec.md FR-009 (WCAG 2.2 AAA - 44x44px touch targets)\n */\nexport const AvatarSizeSchema = z.enum(['sm', 'default', 'lg']);\nexport type AvatarSize = z.infer<typeof AvatarSizeSchema>;\n\n/**\n * Avatar shape schema\n * - circle: Fully rounded (rounded-full)\n * - rounded: Rounded corners (rounded-lg)\n */\nexport const AvatarShapeSchema = z.enum(['circle', 'rounded']);\nexport type AvatarShape = z.infer<typeof AvatarShapeSchema>;\n\n/**\n * Avatar status schema for ring indicators\n *\n * Status ring colors:\n * - online: green (#22c55e)\n * - offline: gray (#6b7280)\n * - busy: red (#ef4444)\n * - away: yellow (#eab308)\n */\nexport const AvatarStatusSchema = z.enum(['online', 'offline', 'busy', 'away']);\nexport type AvatarStatus = z.infer<typeof AvatarStatusSchema>;\n\n/**\n * Overlap values by size (in pixels)\n * @see plan.md Clarification 6 (Overlap Scale)\n */\nexport const AVATAR_OVERLAP_BY_SIZE: Record<AvatarSize, number> = {\n sm: 8,\n default: 12,\n lg: 16,\n};\n\n/**\n * Avatar props schema extending BaseComponentProps\n *\n * @see React Aria Button: https://react-aria.adobe.com/Button\n * @see ShadCN Avatar: https://ui.shadcn.com/docs/components/avatar\n * @see plan.md Phase 1 (Type System)\n * @see constitution.md Principle IV (Accessibility First)\n */\nexport const AvatarPropsSchema = BaseComponentPropsSchema.extend({\n /** Image source URL */\n src: z.string().optional(),\n\n /**\n * Alt text for the avatar image (required for accessibility)\n * When actionable, this is used for the aria-label\n */\n alt: z.string().min(1, 'alt is required for accessibility'),\n\n /**\n * Name used to generate initials fallback\n * - \"Jane Doe\" → \"JD\"\n * - \"Jane\" → \"J\"\n * - \"Jane Marie Doe\" → \"JD\" (first and last)\n */\n name: z.string().optional(),\n\n /** Visual size variant (touch target always 44x44px min for sm/default) */\n size: AvatarSizeSchema.optional().default('default'),\n\n /** Shape variant */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /** Status ring indicator */\n status: AvatarStatusSchema.optional(),\n\n /**\n * Click handler for actionable avatar\n * When provided, renders as Button with 3-layer architecture\n * @see plan.md Clarification 2 (mutually exclusive with group onPress)\n */\n onPress: z.function().optional(),\n});\n\nexport type AvatarProps = Omit<z.infer<typeof AvatarPropsSchema>, 'onPress'> & {\n /** Click handler for actionable avatar */\n onPress?: (e: PressEvent) => void;\n /**\n * Internal: Position index within AvatarGroup (0-indexed)\n * Used to progressively darken backgrounds in groups\n * @internal Set by AvatarGroup, do not use directly\n */\n _groupIndex?: number;\n};\n\n/**\n * Avatar group props schema extending BaseComponentProps\n *\n * @see plan.md Clarification 1 (Group Border)\n * @see plan.md Clarification 5 (Stack Order - first on top)\n * @see plan.md Clarification 7 (Border Scope - groups only)\n */\nexport const AvatarGroupPropsSchema = BaseComponentPropsSchema.extend({\n /**\n * Maximum avatars to display before overflow (+X indicator)\n * Default: 3\n */\n max: z.number().int().positive().optional().default(3),\n\n /**\n * Shape inherited by all child avatars\n * Individual avatar shape props are ignored in groups\n */\n shape: AvatarShapeSchema.optional().default('circle'),\n\n /**\n * Size inherited by all child avatars\n * Individual avatar size props are ignored in groups\n * Also determines overlap spacing (sm: 8px, default: 12px, lg: 16px)\n */\n size: AvatarSizeSchema.optional().default('default'),\n\n /**\n * Click handler for the entire group\n * When provided, individual avatar onPress handlers are ignored\n * @see plan.md Clarification 2 (Mutually exclusive)\n */\n onPress: z.function().optional(),\n});\n\nexport type AvatarGroupProps = Omit<z.infer<typeof AvatarGroupPropsSchema>, 'onPress'> & {\n /** Click handler for the entire group */\n onPress?: (e: PressEvent) => void;\n};\n\n/**\n * Internal loading state for avatar images\n * @see plan.md Clarification 4 (Loading State)\n */\nexport type AvatarLoadingState = 'loading' | 'loaded' | 'error';\n"]}