se-design 1.0.90-dev.0 → 1.0.90

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 (462) hide show
  1. package/dist/assets/style.css +1 -1
  2. package/dist/components/AnchoredOverlay/index.d.ts +41 -0
  3. package/dist/components/index.d.ts +2 -0
  4. package/dist/index.js +132 -130
  5. package/dist/index.js.map +1 -1
  6. package/dist/index100.js +1 -1
  7. package/dist/index100.js.map +1 -1
  8. package/dist/index101.js +1 -1
  9. package/dist/index101.js.map +1 -1
  10. package/dist/index102.js +1 -1
  11. package/dist/index102.js.map +1 -1
  12. package/dist/index103.js +1 -1
  13. package/dist/index103.js.map +1 -1
  14. package/dist/index104.js +1 -1
  15. package/dist/index104.js.map +1 -1
  16. package/dist/index105.js +1 -1
  17. package/dist/index105.js.map +1 -1
  18. package/dist/index106.js +1 -1
  19. package/dist/index106.js.map +1 -1
  20. package/dist/index107.js +1 -1
  21. package/dist/index107.js.map +1 -1
  22. package/dist/index108.js +1 -1
  23. package/dist/index108.js.map +1 -1
  24. package/dist/index109.js +1 -1
  25. package/dist/index109.js.map +1 -1
  26. package/dist/index11.js +1 -1
  27. package/dist/index110.js +1 -1
  28. package/dist/index110.js.map +1 -1
  29. package/dist/index111.js +1 -1
  30. package/dist/index111.js.map +1 -1
  31. package/dist/index112.js +1 -1
  32. package/dist/index112.js.map +1 -1
  33. package/dist/index113.js +1 -1
  34. package/dist/index113.js.map +1 -1
  35. package/dist/index114.js +2 -2
  36. package/dist/index114.js.map +1 -1
  37. package/dist/index115.js +1 -1
  38. package/dist/index115.js.map +1 -1
  39. package/dist/index116.js +2 -2
  40. package/dist/index116.js.map +1 -1
  41. package/dist/index117.js +1 -1
  42. package/dist/index117.js.map +1 -1
  43. package/dist/index118.js +1 -1
  44. package/dist/index118.js.map +1 -1
  45. package/dist/index119.js +2 -2
  46. package/dist/index119.js.map +1 -1
  47. package/dist/index12.js +3 -3
  48. package/dist/index120.js +1 -1
  49. package/dist/index120.js.map +1 -1
  50. package/dist/index121.js +2 -2
  51. package/dist/index121.js.map +1 -1
  52. package/dist/index122.js +1 -1
  53. package/dist/index122.js.map +1 -1
  54. package/dist/index123.js +1 -1
  55. package/dist/index123.js.map +1 -1
  56. package/dist/index124.js +1 -1
  57. package/dist/index124.js.map +1 -1
  58. package/dist/index125.js +1 -1
  59. package/dist/index125.js.map +1 -1
  60. package/dist/index126.js +2 -2
  61. package/dist/index126.js.map +1 -1
  62. package/dist/index127.js +1 -1
  63. package/dist/index127.js.map +1 -1
  64. package/dist/index128.js +2 -2
  65. package/dist/index128.js.map +1 -1
  66. package/dist/index129.js +2 -2
  67. package/dist/index129.js.map +1 -1
  68. package/dist/index13.js +3 -3
  69. package/dist/index130.js +1 -1
  70. package/dist/index130.js.map +1 -1
  71. package/dist/index131.js +2 -2
  72. package/dist/index131.js.map +1 -1
  73. package/dist/index132.js +2 -2
  74. package/dist/index132.js.map +1 -1
  75. package/dist/index133.js +1 -1
  76. package/dist/index133.js.map +1 -1
  77. package/dist/index134.js +1 -1
  78. package/dist/index134.js.map +1 -1
  79. package/dist/index135.js +1 -1
  80. package/dist/index135.js.map +1 -1
  81. package/dist/index136.js +2 -2
  82. package/dist/index136.js.map +1 -1
  83. package/dist/index137.js +2 -2
  84. package/dist/index137.js.map +1 -1
  85. package/dist/index138.js +2 -2
  86. package/dist/index138.js.map +1 -1
  87. package/dist/index139.js +1 -1
  88. package/dist/index139.js.map +1 -1
  89. package/dist/index140.js +1 -1
  90. package/dist/index140.js.map +1 -1
  91. package/dist/index141.js +1 -1
  92. package/dist/index141.js.map +1 -1
  93. package/dist/index142.js +1 -1
  94. package/dist/index142.js.map +1 -1
  95. package/dist/index143.js +1 -1
  96. package/dist/index143.js.map +1 -1
  97. package/dist/index144.js +1 -1
  98. package/dist/index144.js.map +1 -1
  99. package/dist/index145.js +1 -1
  100. package/dist/index145.js.map +1 -1
  101. package/dist/index146.js +1 -1
  102. package/dist/index146.js.map +1 -1
  103. package/dist/index147.js +2 -2
  104. package/dist/index147.js.map +1 -1
  105. package/dist/index148.js +2 -2
  106. package/dist/index148.js.map +1 -1
  107. package/dist/index149.js +1 -1
  108. package/dist/index149.js.map +1 -1
  109. package/dist/index150.js +1 -1
  110. package/dist/index150.js.map +1 -1
  111. package/dist/index151.js +1 -1
  112. package/dist/index151.js.map +1 -1
  113. package/dist/index152.js +1 -1
  114. package/dist/index152.js.map +1 -1
  115. package/dist/index153.js +1 -1
  116. package/dist/index153.js.map +1 -1
  117. package/dist/index154.js +1 -1
  118. package/dist/index154.js.map +1 -1
  119. package/dist/index155.js +1 -1
  120. package/dist/index155.js.map +1 -1
  121. package/dist/index156.js +2 -2
  122. package/dist/index156.js.map +1 -1
  123. package/dist/index157.js +1 -1
  124. package/dist/index157.js.map +1 -1
  125. package/dist/index158.js +2 -2
  126. package/dist/index158.js.map +1 -1
  127. package/dist/index159.js +2 -2
  128. package/dist/index159.js.map +1 -1
  129. package/dist/index16.js +3 -3
  130. package/dist/index160.js +2 -2
  131. package/dist/index160.js.map +1 -1
  132. package/dist/index161.js +2 -2
  133. package/dist/index161.js.map +1 -1
  134. package/dist/index162.js +1 -1
  135. package/dist/index162.js.map +1 -1
  136. package/dist/index163.js +1 -1
  137. package/dist/index163.js.map +1 -1
  138. package/dist/index164.js +1 -1
  139. package/dist/index164.js.map +1 -1
  140. package/dist/index165.js +1 -1
  141. package/dist/index165.js.map +1 -1
  142. package/dist/index166.js +1 -1
  143. package/dist/index166.js.map +1 -1
  144. package/dist/index167.js +1 -1
  145. package/dist/index167.js.map +1 -1
  146. package/dist/index168.js +1 -1
  147. package/dist/index168.js.map +1 -1
  148. package/dist/index169.js +1 -1
  149. package/dist/index169.js.map +1 -1
  150. package/dist/index170.js +1 -1
  151. package/dist/index170.js.map +1 -1
  152. package/dist/index171.js +1 -1
  153. package/dist/index171.js.map +1 -1
  154. package/dist/index172.js +1 -1
  155. package/dist/index172.js.map +1 -1
  156. package/dist/index173.js +1 -1
  157. package/dist/index173.js.map +1 -1
  158. package/dist/index174.js +1 -1
  159. package/dist/index174.js.map +1 -1
  160. package/dist/index175.js +1 -1
  161. package/dist/index175.js.map +1 -1
  162. package/dist/index176.js +1 -1
  163. package/dist/index176.js.map +1 -1
  164. package/dist/index177.js +1 -1
  165. package/dist/index177.js.map +1 -1
  166. package/dist/index178.js +1 -1
  167. package/dist/index178.js.map +1 -1
  168. package/dist/index179.js +1 -1
  169. package/dist/index179.js.map +1 -1
  170. package/dist/index18.js +3 -3
  171. package/dist/index180.js +1 -1
  172. package/dist/index180.js.map +1 -1
  173. package/dist/index181.js +1 -1
  174. package/dist/index181.js.map +1 -1
  175. package/dist/index182.js +1 -1
  176. package/dist/index182.js.map +1 -1
  177. package/dist/index183.js +1 -1
  178. package/dist/index183.js.map +1 -1
  179. package/dist/index184.js +1 -1
  180. package/dist/index184.js.map +1 -1
  181. package/dist/index185.js +1 -1
  182. package/dist/index185.js.map +1 -1
  183. package/dist/index186.js +2 -2
  184. package/dist/index186.js.map +1 -1
  185. package/dist/index187.js +2 -2
  186. package/dist/index187.js.map +1 -1
  187. package/dist/index188.js +1 -1
  188. package/dist/index188.js.map +1 -1
  189. package/dist/index189.js +1 -1
  190. package/dist/index189.js.map +1 -1
  191. package/dist/index19.js +5 -5
  192. package/dist/index190.js +2 -2
  193. package/dist/index190.js.map +1 -1
  194. package/dist/index191.js +2 -2
  195. package/dist/index191.js.map +1 -1
  196. package/dist/index192.js +1 -1
  197. package/dist/index192.js.map +1 -1
  198. package/dist/index193.js +1 -1
  199. package/dist/index193.js.map +1 -1
  200. package/dist/index194.js +1 -1
  201. package/dist/index194.js.map +1 -1
  202. package/dist/index195.js +1 -1
  203. package/dist/index195.js.map +1 -1
  204. package/dist/index196.js +1 -1
  205. package/dist/index196.js.map +1 -1
  206. package/dist/index197.js +1 -1
  207. package/dist/index197.js.map +1 -1
  208. package/dist/index198.js +1 -1
  209. package/dist/index198.js.map +1 -1
  210. package/dist/index199.js +1 -1
  211. package/dist/index199.js.map +1 -1
  212. package/dist/index20.js +96 -18
  213. package/dist/index20.js.map +1 -1
  214. package/dist/index200.js +1 -1
  215. package/dist/index200.js.map +1 -1
  216. package/dist/index201.js +2 -2
  217. package/dist/index201.js.map +1 -1
  218. package/dist/index202.js +2 -2
  219. package/dist/index202.js.map +1 -1
  220. package/dist/index203.js +2 -2
  221. package/dist/index203.js.map +1 -1
  222. package/dist/index204.js +2 -149
  223. package/dist/index204.js.map +1 -1
  224. package/dist/index205.js +149 -9
  225. package/dist/index205.js.map +1 -1
  226. package/dist/index206.js +8 -5
  227. package/dist/index206.js.map +1 -1
  228. package/dist/index207.js +5 -42
  229. package/dist/index207.js.map +1 -1
  230. package/dist/index208.js +40 -18
  231. package/dist/index208.js.map +1 -1
  232. package/dist/index209.js +24 -0
  233. package/dist/index209.js.map +1 -0
  234. package/dist/index21.js +17 -32
  235. package/dist/index21.js.map +1 -1
  236. package/dist/index216.js +24 -68
  237. package/dist/index216.js.map +1 -1
  238. package/dist/index217.js +75 -0
  239. package/dist/index217.js.map +1 -0
  240. package/dist/index22.js +33 -66
  241. package/dist/index22.js.map +1 -1
  242. package/dist/{index223.js → index224.js} +1 -1
  243. package/dist/{index223.js.map → index224.js.map} +1 -1
  244. package/dist/{index225.js → index226.js} +1 -1
  245. package/dist/{index225.js.map → index226.js.map} +1 -1
  246. package/dist/index23.js +62 -83
  247. package/dist/index23.js.map +1 -1
  248. package/dist/{index233.js → index234.js} +1 -1
  249. package/dist/{index233.js.map → index234.js.map} +1 -1
  250. package/dist/{index236.js → index237.js} +1 -1
  251. package/dist/{index236.js.map → index237.js.map} +1 -1
  252. package/dist/index24.js +85 -21
  253. package/dist/index24.js.map +1 -1
  254. package/dist/index246.js +169 -3
  255. package/dist/index246.js.map +1 -1
  256. package/dist/index247.js +7 -0
  257. package/dist/index247.js.map +1 -0
  258. package/dist/index249.js +18 -9
  259. package/dist/index249.js.map +1 -1
  260. package/dist/index25.js +19 -401
  261. package/dist/index25.js.map +1 -1
  262. package/dist/index250.js +9 -9
  263. package/dist/index250.js.map +1 -1
  264. package/dist/index251.js +9 -4
  265. package/dist/index251.js.map +1 -1
  266. package/dist/index252.js +5 -170
  267. package/dist/index252.js.map +1 -1
  268. package/dist/index253.js +170 -11
  269. package/dist/index253.js.map +1 -1
  270. package/dist/index254.js +11 -6
  271. package/dist/index254.js.map +1 -1
  272. package/dist/index255.js +5 -5
  273. package/dist/index255.js.map +1 -1
  274. package/dist/index256.js +5 -37
  275. package/dist/index256.js.map +1 -1
  276. package/dist/index257.js +38 -2
  277. package/dist/index257.js.map +1 -1
  278. package/dist/index258.js +2 -8
  279. package/dist/index258.js.map +1 -1
  280. package/dist/index259.js +7 -326
  281. package/dist/index259.js.map +1 -1
  282. package/dist/index26.js +400 -44
  283. package/dist/index26.js.map +1 -1
  284. package/dist/index260.js +324 -47
  285. package/dist/index260.js.map +1 -1
  286. package/dist/index261.js +50 -2
  287. package/dist/index261.js.map +1 -1
  288. package/dist/index262.js +2 -76
  289. package/dist/index262.js.map +1 -1
  290. package/dist/index263.js +65 -82
  291. package/dist/index263.js.map +1 -1
  292. package/dist/index264.js +89 -48
  293. package/dist/index264.js.map +1 -1
  294. package/dist/index265.js +51 -7
  295. package/dist/index265.js.map +1 -1
  296. package/dist/index266.js +7 -4
  297. package/dist/index266.js.map +1 -1
  298. package/dist/index267.js +4 -51
  299. package/dist/index267.js.map +1 -1
  300. package/dist/index268.js +52 -2
  301. package/dist/index268.js.map +1 -1
  302. package/dist/index269.js +2 -2
  303. package/dist/index27.js +48 -23
  304. package/dist/index27.js.map +1 -1
  305. package/dist/index270.js +5 -0
  306. package/dist/index270.js.map +1 -0
  307. package/dist/index28.js +21 -108
  308. package/dist/index28.js.map +1 -1
  309. package/dist/index29.js +107 -147
  310. package/dist/index29.js.map +1 -1
  311. package/dist/index3.js +3 -3
  312. package/dist/index30.js +146 -132
  313. package/dist/index30.js.map +1 -1
  314. package/dist/index31.js +136 -76
  315. package/dist/index31.js.map +1 -1
  316. package/dist/index32.js +73 -94
  317. package/dist/index32.js.map +1 -1
  318. package/dist/index33.js +95 -73
  319. package/dist/index33.js.map +1 -1
  320. package/dist/index34.js +72 -38
  321. package/dist/index34.js.map +1 -1
  322. package/dist/index35.js +40 -72
  323. package/dist/index35.js.map +1 -1
  324. package/dist/index36.js +68 -94
  325. package/dist/index36.js.map +1 -1
  326. package/dist/index37.js +98 -77
  327. package/dist/index37.js.map +1 -1
  328. package/dist/index38.js +75 -311
  329. package/dist/index38.js.map +1 -1
  330. package/dist/index39.js +313 -75
  331. package/dist/index39.js.map +1 -1
  332. package/dist/index4.js +4 -4
  333. package/dist/index40.js +72 -79
  334. package/dist/index40.js.map +1 -1
  335. package/dist/index41.js +82 -32
  336. package/dist/index41.js.map +1 -1
  337. package/dist/index42.js +26 -43
  338. package/dist/index42.js.map +1 -1
  339. package/dist/index43.js +48 -109
  340. package/dist/index43.js.map +1 -1
  341. package/dist/index44.js +104 -50
  342. package/dist/index44.js.map +1 -1
  343. package/dist/index45.js +53 -142
  344. package/dist/index45.js.map +1 -1
  345. package/dist/index46.js +137 -188
  346. package/dist/index46.js.map +1 -1
  347. package/dist/index47.js +195 -35
  348. package/dist/index47.js.map +1 -1
  349. package/dist/index48.js +34 -32
  350. package/dist/index48.js.map +1 -1
  351. package/dist/index49.js +34 -147
  352. package/dist/index49.js.map +1 -1
  353. package/dist/index50.js +130 -67
  354. package/dist/index50.js.map +1 -1
  355. package/dist/index51.js +78 -453
  356. package/dist/index51.js.map +1 -1
  357. package/dist/index52.js +459 -96
  358. package/dist/index52.js.map +1 -1
  359. package/dist/index53.js +98 -43
  360. package/dist/index53.js.map +1 -1
  361. package/dist/index54.js +40 -35
  362. package/dist/index54.js.map +1 -1
  363. package/dist/index55.js +33 -87
  364. package/dist/index55.js.map +1 -1
  365. package/dist/index56.js +88 -159
  366. package/dist/index56.js.map +1 -1
  367. package/dist/index57.js +157 -143
  368. package/dist/index57.js.map +1 -1
  369. package/dist/index58.js +149 -33
  370. package/dist/index58.js.map +1 -1
  371. package/dist/index59.js +33 -86
  372. package/dist/index59.js.map +1 -1
  373. package/dist/index6.js +125 -125
  374. package/dist/index60.js +80 -113
  375. package/dist/index60.js.map +1 -1
  376. package/dist/index61.js +114 -73
  377. package/dist/index61.js.map +1 -1
  378. package/dist/index62.js +73 -147
  379. package/dist/index62.js.map +1 -1
  380. package/dist/index63.js +146 -312
  381. package/dist/index63.js.map +1 -1
  382. package/dist/index64.js +317 -46
  383. package/dist/index64.js.map +1 -1
  384. package/dist/index65.js +45 -142
  385. package/dist/index65.js.map +1 -1
  386. package/dist/index66.js +144 -12
  387. package/dist/index66.js.map +1 -1
  388. package/dist/index67.js +10 -53
  389. package/dist/index67.js.map +1 -1
  390. package/dist/index68.js +52 -110
  391. package/dist/index68.js.map +1 -1
  392. package/dist/index69.js +111 -17
  393. package/dist/index69.js.map +1 -1
  394. package/dist/index7.js +2 -2
  395. package/dist/index70.js +17 -64
  396. package/dist/index70.js.map +1 -1
  397. package/dist/index71.js +64 -25
  398. package/dist/index71.js.map +1 -1
  399. package/dist/index72.js +24 -105
  400. package/dist/index72.js.map +1 -1
  401. package/dist/index73.js +104 -58
  402. package/dist/index73.js.map +1 -1
  403. package/dist/index74.js +60 -19
  404. package/dist/index74.js.map +1 -1
  405. package/dist/index75.js +18 -168
  406. package/dist/index75.js.map +1 -1
  407. package/dist/index76.js +171 -18
  408. package/dist/index76.js.map +1 -1
  409. package/dist/index77.js +17 -10
  410. package/dist/index77.js.map +1 -1
  411. package/dist/index78.js +11 -5
  412. package/dist/index78.js.map +1 -1
  413. package/dist/index79.js +5 -62
  414. package/dist/index79.js.map +1 -1
  415. package/dist/index80.js +58 -23
  416. package/dist/index80.js.map +1 -1
  417. package/dist/index81.js +25 -9
  418. package/dist/index81.js.map +1 -1
  419. package/dist/index82.js +14 -0
  420. package/dist/index82.js.map +1 -0
  421. package/dist/index84.js +2 -2
  422. package/dist/index84.js.map +1 -1
  423. package/dist/index85.js +2 -2
  424. package/dist/index85.js.map +1 -1
  425. package/dist/index86.js +2 -2
  426. package/dist/index86.js.map +1 -1
  427. package/dist/index87.js +2 -2
  428. package/dist/index87.js.map +1 -1
  429. package/dist/index88.js +1 -1
  430. package/dist/index88.js.map +1 -1
  431. package/dist/index89.js +1 -1
  432. package/dist/index89.js.map +1 -1
  433. package/dist/index9.js +4 -4
  434. package/dist/index90.js +1 -1
  435. package/dist/index90.js.map +1 -1
  436. package/dist/index91.js +1 -1
  437. package/dist/index91.js.map +1 -1
  438. package/dist/index92.js +1 -1
  439. package/dist/index92.js.map +1 -1
  440. package/dist/index93.js +1 -1
  441. package/dist/index93.js.map +1 -1
  442. package/dist/index94.js +1 -1
  443. package/dist/index94.js.map +1 -1
  444. package/dist/index95.js +1 -1
  445. package/dist/index95.js.map +1 -1
  446. package/dist/index96.js +1 -1
  447. package/dist/index96.js.map +1 -1
  448. package/dist/index97.js +1 -1
  449. package/dist/index97.js.map +1 -1
  450. package/dist/index98.js +1 -1
  451. package/dist/index98.js.map +1 -1
  452. package/dist/index99.js +1 -1
  453. package/dist/index99.js.map +1 -1
  454. package/package.json +1 -1
  455. package/dist/index215.js +0 -31
  456. package/dist/index215.js.map +0 -1
  457. package/dist/index245.js +0 -173
  458. package/dist/index245.js.map +0 -1
  459. package/dist/index248.js +0 -22
  460. package/dist/index248.js.map +0 -1
  461. package/dist/index83.js +0 -5
  462. package/dist/index83.js.map +0 -1
package/dist/index74.js CHANGED
@@ -1,24 +1,65 @@
1
- import { useEffect as u } from "react";
2
- import { getFirstFocusableElement as f } from "./index71.js";
3
- function s({
4
- focusRef: r,
5
- containerRef: t,
6
- isReady: e,
7
- shouldFocus: n
1
+ import { useCallback as c } from "react";
2
+ import { useRovingFocus as d } from "./index71.js";
3
+ const s = (r, t) => `tab${t ? `-${t}` : ""}-${r}`, l = (r, t) => `panel${t ? `-${t}` : ""}-${r}`, C = (r, t) => ({
4
+ id: l(r, t),
5
+ role: "tabpanel",
6
+ "aria-labelledby": s(r, t)
7
+ });
8
+ function R({
9
+ itemIds: r,
10
+ orientation: t = "horizontal",
11
+ activeItem: n,
12
+ idBase: e = "",
13
+ includePanelLinks: a = !0,
14
+ externalPanelId: u
8
15
  }) {
9
- u(() => {
10
- if (e && !(n && !n())) {
11
- if (r?.current) {
12
- r.current.focus();
13
- return;
14
- }
15
- t?.current && f({
16
- container: t.current
17
- })?.focus();
18
- }
19
- }, [e]);
16
+ const {
17
+ setFocusedId: f,
18
+ handleKeyDown: p,
19
+ getRovingItemProps: b,
20
+ getContainerProps: g
21
+ } = d({
22
+ itemIds: r,
23
+ defaultFocusedId: n,
24
+ orientation: t,
25
+ loop: !0,
26
+ role: "tablist"
27
+ }), P = c((o) => {
28
+ const {
29
+ ref: $,
30
+ tabIndex: x,
31
+ onFocus: F
32
+ } = b(o), y = n === o;
33
+ return {
34
+ ref: $,
35
+ tabIndex: x,
36
+ onFocus: F,
37
+ id: s(o, e),
38
+ ...a && {
39
+ "aria-controls": u ?? l(o, e)
40
+ },
41
+ "aria-selected": y,
42
+ role: "tab"
43
+ };
44
+ }, [n, b, e, a, u]), T = c((o) => ({
45
+ id: l(o, e),
46
+ role: "tabpanel",
47
+ "aria-labelledby": s(o, e)
48
+ }), [e]);
49
+ return {
50
+ getTabProps: P,
51
+ getPanelProps: T,
52
+ getTabListProps: g,
53
+ setFocusedTabId: f,
54
+ handleKeyDown: p,
55
+ getPanelId: (o) => l(o, e),
56
+ getTabId: (o) => s(o, e)
57
+ };
20
58
  }
21
59
  export {
22
- s as useFocusOnReady
60
+ l as getPanelId,
61
+ s as getTabId,
62
+ C as getTabPanelProps,
63
+ R as useTabsA11y
23
64
  };
24
65
  //# sourceMappingURL=index74.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index74.js","sources":["../src/utils/a11y/useFocusOnReady.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { getFirstFocusableElement } from './focusableElements';\n\nexport interface UseFocusOnReadyOptions {\n /**\n * Specific element to focus. Takes priority over containerRef.\n */\n focusRef?: RefObject<HTMLElement | null>;\n /**\n * Container to search for first focusable element.\n * Used as fallback when focusRef is not provided or its current is null.\n */\n containerRef?: RefObject<HTMLElement | null>;\n /**\n * When this transitions to true, focus is triggered.\n */\n isReady: boolean;\n /**\n * Optional guard for async content evaluated at the moment isReady becomes true.\n * Return false to skip focusing (e.g. if the user has tabbed away while content was loading).\n * Not needed for synchronous open/close like modals or dropdowns where focus is immediate.\n */\n shouldFocus?: () => boolean;\n}\n\n/**\n * Moves focus to an element when an async condition becomes ready.\n *\n * When `focusRef` is provided, focuses that element directly.\n * When only `containerRef` is provided, finds and focuses the first focusable child.\n * When both are provided, `focusRef` takes priority; `containerRef` is the fallback.\n *\n * @example Focus first focusable in container after loading\n * ```tsx\n * useFocusOnReady({\n * containerRef: panelRef,\n * isReady: !isLoading,\n * shouldFocus: () => document.activeElement === panelRef.current\n * });\n * ```\n *\n * @example Focus a specific element when modal opens\n * ```tsx\n * useFocusOnReady({\n * focusRef: closeButtonRef,\n * isReady: isOpen\n * });\n * ```\n *\n * @example Specific element with container fallback\n * ```tsx\n * useFocusOnReady({\n * focusRef: preferredRef,\n * containerRef: dialogRef,\n * isReady: isOpen\n * });\n * ```\n */\nexport function useFocusOnReady({\n focusRef,\n containerRef,\n isReady,\n shouldFocus\n}: UseFocusOnReadyOptions): void {\n useEffect(() => {\n if (!isReady) return;\n if (shouldFocus && !shouldFocus()) return;\n\n if (focusRef?.current) {\n focusRef.current.focus();\n return;\n }\n\n if (containerRef?.current) {\n const first = getFirstFocusableElement({ container: containerRef.current });\n first?.focus();\n }\n }, [isReady]);\n}\n"],"names":["useEffect","getFirstFocusableElement","useFocusOnReady","focusRef","containerRef","isReady","shouldFocus","current","focus","container"],"mappings":"AA2DO,SAAA,aAAAA,SAAA;AAAA,SAAA,4BAAAC,SAAA;AAAA,SAASC,EAAgB;AAAA,EAC9BC,UAAAA;AAAAA,EACAC,cAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,aAAAA;AACsB,GAAS;AAC/BN,EAAAA,EAAU,MAAM;AACd,QAAKK,KACDC,EAAAA,KAAe,CAACA,MAEpB;AAAA,UAAIH,GAAUI,SAAS;AACrBJ,QAAAA,EAASI,QAAQC,MAAAA;AACjB;AAAA,MACF;AAEA,MAAIJ,GAAcG,WACFN,EAAyB;AAAA,QAAEQ,WAAWL,EAAaG;AAAAA,MAAAA,CAAS,GACnEC,MAAAA;AAAAA;AAAAA,EAEX,GAAG,CAACH,CAAO,CAAC;AACd;"}
1
+ {"version":3,"file":"index74.js","sources":["../src/utils/a11y/useTabsA11y.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useRovingFocus } from './useRovingFocus';\n\nexport const getTabId = (itemId: string, idBase: string) => {\n const suffix = idBase ? `-${idBase}` : '';\n return `tab${suffix}-${itemId}`;\n};\n\nexport const getPanelId = (itemId: string, idBase: string) => {\n const suffix = idBase ? `-${idBase}` : '';\n return `panel${suffix}-${itemId}`;\n};\n\nexport const getTabPanelProps = (tabId: string, idBase: string) => ({\n id: getPanelId(tabId, idBase),\n role: 'tabpanel' as const,\n 'aria-labelledby': getTabId(tabId, idBase)\n});\n\ninterface UseTabsA11yProps {\n itemIds: string[];\n orientation?: 'horizontal' | 'vertical';\n activeItem: string;\n idBase?: string;\n includePanelLinks?: boolean; // Set to false when consumer manages their own panels externally\n externalPanelId?: string; // When set, all tabs point aria-controls to this single external panel id\n}\n\ninterface TabA11yProps {\n ref: (el: HTMLElement | null) => void;\n tabIndex: number;\n onFocus: () => void;\n id: string;\n 'aria-controls'?: string;\n 'aria-selected': boolean;\n role: 'tab';\n}\n\ninterface PanelA11yProps {\n id: string;\n role: 'tabpanel';\n 'aria-labelledby': string;\n}\n\n/**\n * Hook for managing Tabs (NavigationBar) focus and ARIA props.\n * Handles roving tabindex pattern and generates tab-specific ARIA attributes.\n * \n * @example\n * const itemIds = useMemo(() => navigationItems.map(item => item.id), [navigationItems]);\n * const { getTabProps, setFocusedTabId, handleKeyDown } = useTabsA11y({\n * itemIds,\n * activeNavigationItem: 'tab-1',\n * orientation: 'horizontal',\n * idBase: 'settings'\n * });\n */\nexport function useTabsA11y({\n itemIds,\n orientation = 'horizontal',\n activeItem,\n idBase = '',\n includePanelLinks = true,\n externalPanelId\n}: UseTabsA11yProps) {\n const {\n setFocusedId: setFocusedTabId,\n handleKeyDown,\n getRovingItemProps,\n getContainerProps\n } = useRovingFocus({\n itemIds,\n defaultFocusedId: activeItem,\n orientation,\n loop: true,\n role: 'tablist'\n });\n\n /**\n * Get all props needed for a tab button.\n * Combines roving focus props (ref, tabIndex) with tab-specific ARIA attributes.\n */\n const getTabProps = useCallback((itemId: string): TabA11yProps => {\n const { ref, tabIndex, onFocus } = getRovingItemProps(itemId);\n const isSelected = activeItem === itemId;\n\n return {\n ref,\n tabIndex,\n onFocus,\n id: getTabId(itemId, idBase),\n ...(includePanelLinks && { 'aria-controls': externalPanelId ?? getPanelId(itemId, idBase) }),\n 'aria-selected': isSelected,\n role: 'tab'\n };\n }, [activeItem, getRovingItemProps, idBase, includePanelLinks, externalPanelId]);\n\n /**\n * Get all props needed for a tab panel.\n * Provides ARIA attributes to associate the panel with its tab.\n */\n const getPanelProps = useCallback((itemId: string): PanelA11yProps => {\n return {\n id: getPanelId(itemId, idBase),\n role: 'tabpanel',\n 'aria-labelledby': getTabId(itemId, idBase)\n };\n }, [idBase]);\n\n return {\n getTabProps,\n getPanelProps,\n getTabListProps: getContainerProps,\n setFocusedTabId,\n handleKeyDown,\n getPanelId: (itemId: string) => getPanelId(itemId, idBase),\n getTabId: (itemId: string) => getTabId(itemId, idBase)\n };\n}\n\n"],"names":["useCallback","useRovingFocus","getTabId","itemId","idBase","getPanelId","getTabPanelProps","tabId","id","role","useTabsA11y","itemIds","orientation","activeItem","includePanelLinks","externalPanelId","setFocusedId","setFocusedTabId","handleKeyDown","getRovingItemProps","getContainerProps","defaultFocusedId","loop","getTabProps","ref","tabIndex","onFocus","isSelected","getPanelProps","getTabListProps"],"mappings":"AAGO,SAAA,eAAAA,SAAA;AAAA,SAAA,kBAAAC,SAAA;AAAA,MAAMC,IAAWA,CAACC,GAAgBC,MAEhC,MADQA,IAAS,IAAIA,CAAM,KAAK,EACpB,IAAID,CAAM,IAGlBE,IAAaA,CAACF,GAAgBC,MAElC,QADQA,IAAS,IAAIA,CAAM,KAAK,EAClB,IAAID,CAAM,IAGpBG,IAAmBA,CAACC,GAAeH,OAAoB;AAAA,EAClEI,IAAIH,EAAWE,GAAOH,CAAM;AAAA,EAC5BK,MAAM;AAAA,EACN,mBAAmBP,EAASK,GAAOH,CAAM;AAC3C;AAwCO,SAASM,EAAY;AAAA,EAC1BC,SAAAA;AAAAA,EACAC,aAAAA,IAAc;AAAA,EACdC,YAAAA;AAAAA,EACAT,QAAAA,IAAS;AAAA,EACTU,mBAAAA,IAAoB;AAAA,EACpBC,iBAAAA;AACgB,GAAG;AACnB,QAAM;AAAA,IACJC,cAAcC;AAAAA,IACdC,eAAAA;AAAAA,IACAC,oBAAAA;AAAAA,IACAC,mBAAAA;AAAAA,EAAAA,IACEnB,EAAe;AAAA,IACjBU,SAAAA;AAAAA,IACAU,kBAAkBR;AAAAA,IAClBD,aAAAA;AAAAA,IACAU,MAAM;AAAA,IACNb,MAAM;AAAA,EAAA,CACP,GAMKc,IAAcvB,EAAY,CAACG,MAAiC;AAChE,UAAM;AAAA,MAAEqB,KAAAA;AAAAA,MAAKC,UAAAA;AAAAA,MAAUC,SAAAA;AAAAA,IAAAA,IAAYP,EAAmBhB,CAAM,GACtDwB,IAAad,MAAeV;AAElC,WAAO;AAAA,MACLqB,KAAAA;AAAAA,MACAC,UAAAA;AAAAA,MACAC,SAAAA;AAAAA,MACAlB,IAAIN,EAASC,GAAQC,CAAM;AAAA,MAC3B,GAAIU,KAAqB;AAAA,QAAE,iBAAiBC,KAAmBV,EAAWF,GAAQC,CAAM;AAAA,MAAA;AAAA,MACxF,iBAAiBuB;AAAAA,MACjBlB,MAAM;AAAA,IAAA;AAAA,EAEV,GAAG,CAACI,GAAYM,GAAoBf,GAAQU,GAAmBC,CAAe,CAAC,GAMzEa,IAAgB5B,EAAY,CAACG,OAC1B;AAAA,IACLK,IAAIH,EAAWF,GAAQC,CAAM;AAAA,IAC7BK,MAAM;AAAA,IACN,mBAAmBP,EAASC,GAAQC,CAAM;AAAA,EAAA,IAE3C,CAACA,CAAM,CAAC;AAEX,SAAO;AAAA,IACLmB,aAAAA;AAAAA,IACAK,eAAAA;AAAAA,IACAC,iBAAiBT;AAAAA,IACjBH,iBAAAA;AAAAA,IACAC,eAAAA;AAAAA,IACAb,YAAYA,CAACF,MAAmBE,EAAWF,GAAQC,CAAM;AAAA,IACzDF,UAAUA,CAACC,MAAmBD,EAASC,GAAQC,CAAM;AAAA,EAAA;AAEzD;"}
package/dist/index75.js CHANGED
@@ -1,174 +1,24 @@
1
- var m = Object.defineProperty;
2
- var g = (r, e, t) => e in r ? m(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
- var o = (r, e, t) => g(r, typeof e != "symbol" ? e + "" : e, t);
4
- const u = "sr-only", h = "data-batch-id", f = "data-modal-announcer";
5
- let c = null;
6
- function p() {
7
- return c || (c = new T()), c;
8
- }
9
- class T {
10
- constructor() {
11
- o(this, "container", null);
12
- o(this, "assertiveLog", null);
13
- o(this, "politeLog", null);
14
- o(this, "cleanupTimers", /* @__PURE__ */ new Map());
15
- o(this, "delayTimers", /* @__PURE__ */ new Map());
16
- o(this, "pendingRafByBatchId", /* @__PURE__ */ new Map());
17
- o(this, "ready", !1);
18
- o(this, "pendingQueue", []);
19
- typeof document > "u" || (this.container = document.createElement("div"), this.container.dataset.liveAnnouncer = "true", this.container.className = u, this.assertiveLog = this.createLog("assertive"), this.politeLog = this.createLog("polite"), this.container.appendChild(this.assertiveLog), this.container.appendChild(this.politeLog), document.body.prepend(this.container), this.isTestEnvironment() ? this.ready = !0 : setTimeout(() => {
20
- this.ready = !0, this.flushPendingQueue();
21
- }, 100));
22
- }
23
- // -----------------------------------------------------------------------
24
- // Public
25
- // -----------------------------------------------------------------------
26
- announce(e, t = {}) {
27
- const i = {
28
- assertiveness: t.assertiveness ?? "polite",
29
- timeout: t.timeout ?? 7e3,
30
- batchId: t.batchId ?? "",
31
- delay: t.delay ?? 0
32
- };
33
- if (i.batchId) {
34
- const n = this.delayTimers.get(i.batchId);
35
- n && (clearTimeout(n), this.delayTimers.delete(i.batchId));
36
- }
37
- if (!this.ready) {
38
- this.pendingQueue.push({
39
- message: e,
40
- options: i
41
- });
42
- return;
43
- }
44
- if (i.delay > 0) {
45
- const n = i.batchId || `__delay_${Date.now()}_${Math.random()}`, a = setTimeout(() => {
46
- this.delayTimers.delete(n), this.doAnnounce(e, i);
47
- }, i.delay);
48
- this.delayTimers.set(n, a);
49
- return;
50
- }
51
- this.doAnnounce(e, i);
52
- }
53
- clear(e) {
54
- (!e || e === "assertive") && this.clearLog(this.assertiveLog), (!e || e === "polite") && this.clearLog(this.politeLog);
55
- }
56
- destroy() {
57
- this.cleanupTimers.forEach((e) => clearTimeout(e)), this.cleanupTimers.clear(), this.delayTimers.forEach((e) => clearTimeout(e)), this.delayTimers.clear(), this.pendingRafByBatchId.forEach((e) => cancelAnimationFrame(e)), this.pendingRafByBatchId.clear(), this.container?.remove(), this.container = null, this.assertiveLog = null, this.politeLog = null, this.ready = !1, this.pendingQueue = [], c === this && (c = null);
58
- }
59
- isAttached() {
60
- return this.container?.isConnected ?? !1;
61
- }
62
- // -----------------------------------------------------------------------
63
- // Private
64
- // -----------------------------------------------------------------------
65
- createLog(e) {
66
- const t = document.createElement("div");
67
- return t.setAttribute("role", "log"), t.setAttribute("aria-live", e), t.setAttribute("aria-relevant", "additions"), t;
68
- }
69
- /**
70
- * Returns the appropriate log element for the current context.
71
- * When an aria-modal dialog is active, announcements must be made from
72
- * within the modal (screen readers ignore content outside aria-modal).
73
- * A live region is lazily created inside the modal and cleaned up when
74
- * the modal is removed from the DOM.
75
- */
76
- getLogForContext(e) {
77
- const t = this.findVisibleModal();
78
- if (!t)
79
- return e === "assertive" ? this.assertiveLog : this.politeLog;
80
- let i = t.querySelector(`[${f}]`);
81
- if (!i) {
82
- i = document.createElement("div"), i.setAttribute(f, "true"), i.className = u;
83
- const s = this.createLog("assertive"), d = this.createLog("polite");
84
- i.appendChild(s), i.appendChild(d), t.appendChild(i);
85
- }
86
- const n = i.querySelectorAll('[role="log"]'), a = n[0], l = n[1];
87
- return e === "assertive" ? a : l;
88
- }
89
- /**
90
- * Returns the first aria-modal element that is actually visible.
91
- * A modal is considered hidden if:
92
- * - it or an ancestor has aria-hidden="true", or
93
- * - its computed visibility is "hidden" (se-design Modal uses the
94
- * `invisible` CSS class on the wrapper when closed).
95
- */
96
- findVisibleModal() {
97
- const e = document.querySelectorAll('[aria-modal="true"]');
98
- for (const t of e) {
99
- const i = t;
100
- if (!i.closest('[aria-hidden="true"]') && getComputedStyle(i).visibility !== "hidden")
101
- return i;
102
- }
103
- return null;
104
- }
105
- doAnnounce(e, t) {
106
- const i = this.getLogForContext(t.assertiveness);
107
- if (!i) return;
108
- let n = !1;
109
- if (t.batchId) {
110
- const s = i.querySelector(`[${h}="${CSS.escape(t.batchId)}"]`);
111
- s && (this.removeNode(s), n = !0);
112
- }
113
- const a = document.createElement("div");
114
- a.textContent = e, t.batchId && a.setAttribute(h, t.batchId);
115
- const l = () => {
116
- if (i.isConnected && (i.appendChild(a), e !== "")) {
117
- const s = setTimeout(() => {
118
- this.removeNode(a);
119
- }, t.timeout);
120
- this.cleanupTimers.set(a, s);
1
+ import { useEffect as u } from "react";
2
+ import { getFirstFocusableElement as f } from "./index72.js";
3
+ function s({
4
+ focusRef: r,
5
+ containerRef: t,
6
+ isReady: e,
7
+ shouldFocus: n
8
+ }) {
9
+ u(() => {
10
+ if (e && !(n && !n())) {
11
+ if (r?.current) {
12
+ r.current.focus();
13
+ return;
121
14
  }
122
- };
123
- if (n) {
124
- const s = this.pendingRafByBatchId.get(t.batchId);
125
- s !== void 0 && cancelAnimationFrame(s);
126
- const d = requestAnimationFrame(() => {
127
- this.pendingRafByBatchId.delete(t.batchId), l();
128
- });
129
- this.pendingRafByBatchId.set(t.batchId, d);
130
- } else
131
- l();
132
- }
133
- removeNode(e) {
134
- const t = this.cleanupTimers.get(e);
135
- t && (clearTimeout(t), this.cleanupTimers.delete(e)), e.remove();
136
- }
137
- clearLog(e) {
138
- e && (Array.from(e.children).forEach((t) => {
139
- const i = this.cleanupTimers.get(t);
140
- i && (clearTimeout(i), this.cleanupTimers.delete(t));
141
- }), e.innerHTML = "");
142
- }
143
- flushPendingQueue() {
144
- this.pendingQueue.splice(0).forEach(({
145
- message: t,
146
- options: i
147
- }) => this.doAnnounce(t, i));
148
- }
149
- isTestEnvironment() {
150
- try {
151
- if (typeof IS_REACT_ACT_ENVIRONMENT == "boolean" && IS_REACT_ACT_ENVIRONMENT || typeof jest < "u" || typeof vitest < "u") return !0;
152
- } catch {
15
+ t?.current && f({
16
+ container: t.current
17
+ })?.focus();
153
18
  }
154
- return !1;
155
- }
156
- }
157
- function A(r, e) {
158
- const t = typeof e == "string" ? {
159
- assertiveness: e
160
- } : e ?? {};
161
- p().announce(r, t);
162
- }
163
- function L(r) {
164
- c?.clear(r);
165
- }
166
- function I() {
167
- c?.destroy();
19
+ }, [e]);
168
20
  }
169
21
  export {
170
- A as announce,
171
- L as clearAnnouncer,
172
- I as destroyAnnouncer
22
+ s as useFocusOnReady
173
23
  };
174
24
  //# sourceMappingURL=index75.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index75.js","sources":["../src/utils/a11y/liveAnnouncer/LiveAnnouncer.ts"],"sourcesContent":["/**\n * LiveAnnouncer — singleton vanilla-DOM announcer for screen readers.\n *\n * Architecture:\n * - Prepends a visually-hidden container to document.body on first use.\n * - Two <div role=\"log\" aria-live=\"...\"> regions: one polite, one assertive.\n * - Each announce() appends a NEW child node (aria-relevant=\"additions\" makes\n * screen readers announce only additions, never re-read existing content).\n * - Child nodes auto-remove after a timeout (default 7 s) to keep the DOM clean.\n * - batchId option: if a node with the same batchId already exists in a log,\n * it is replaced — so only the final value is announced (e.g. \"N results\").\n *\n * No React dependency. No role=\"status\" or role=\"alert\" (avoids double-announce\n * bugs on iOS VoiceOver).\n */\n\nexport type Assertiveness = 'assertive' | 'polite';\n\nexport interface AnnounceOptions {\n /** Default: 'polite' */\n assertiveness?: Assertiveness;\n /** Auto-clear timeout in ms. Default: 7000 */\n timeout?: number;\n /**\n * When provided, any pending node with the same batchId is replaced rather\n * than appending a second node. Useful for rapidly-updating counts\n * (e.g. search results, selection count).\n */\n batchId?: string;\n /**\n * Delay in ms before the announcement is made. Useful when focus changes\n * would otherwise swallow the announcement (e.g. after adding a tag in\n * a combobox). A subsequent call with the same batchId cancels any\n * pending delayed announcement.\n */\n delay?: number;\n}\n\nconst DEFAULT_TIMEOUT = 7000;\nconst SAFARI_INIT_DELAY = 100;\n\nconst SR_ONLY_CLASS = 'sr-only';\n\n/** Attribute used to find batchId nodes for replacement. */\nconst BATCH_ATTR = 'data-batch-id';\n\n/** Attribute used to identify announcer containers injected into modals. */\nconst MODAL_ANNOUNCER_ATTR = 'data-modal-announcer';\n\n// ---------------------------------------------------------------------------\n// Singleton\n// ---------------------------------------------------------------------------\n\nlet instance: LiveAnnouncer | null = null;\n\n/**\n * Get or create the singleton LiveAnnouncer.\n * Safe to call multiple times — returns the same instance.\n */\nfunction getInstance(): LiveAnnouncer {\n if (!instance) {\n instance = new LiveAnnouncer();\n }\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Class\n// ---------------------------------------------------------------------------\n\nclass LiveAnnouncer {\n private container: HTMLDivElement | null = null;\n private assertiveLog: HTMLDivElement | null = null;\n private politeLog: HTMLDivElement | null = null;\n private cleanupTimers: Map<HTMLElement, ReturnType<typeof setTimeout>> = new Map();\n private delayTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private pendingRafByBatchId: Map<string, number> = new Map();\n private ready = false;\n private pendingQueue: Array<{ message: string; options: Required<AnnounceOptions> }> = [];\n\n constructor() {\n if (typeof document === 'undefined') return;\n\n this.container = document.createElement('div');\n this.container.dataset.liveAnnouncer = 'true';\n this.container.className = SR_ONLY_CLASS;\n\n this.assertiveLog = this.createLog('assertive');\n this.politeLog = this.createLog('polite');\n\n this.container.appendChild(this.assertiveLog);\n this.container.appendChild(this.politeLog);\n\n // Prepend so screen readers encounter it early in the DOM.\n document.body.prepend(this.container);\n\n // Safari/VoiceOver ignores content injected within ~100 ms of the live\n // region being added to the DOM. Delay readiness accordingly.\n // In test environments, skip the delay.\n if (this.isTestEnvironment()) {\n this.ready = true;\n } else {\n setTimeout(() => {\n this.ready = true;\n this.flushPendingQueue();\n }, SAFARI_INIT_DELAY);\n }\n }\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n announce(message: string, options: AnnounceOptions = {}): void {\n const resolved: Required<AnnounceOptions> = {\n assertiveness: options.assertiveness ?? 'polite',\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n batchId: options.batchId ?? '',\n delay: options.delay ?? 0,\n };\n\n // Cancel any pending delayed announcement with the same batchId.\n if (resolved.batchId) {\n const pendingTimer = this.delayTimers.get(resolved.batchId);\n if (pendingTimer) {\n clearTimeout(pendingTimer);\n this.delayTimers.delete(resolved.batchId);\n }\n }\n\n if (!this.ready) {\n this.pendingQueue.push({ message, options: resolved });\n return;\n }\n\n if (resolved.delay > 0) {\n const key = resolved.batchId || `__delay_${Date.now()}_${Math.random()}`;\n const timer = setTimeout(() => {\n this.delayTimers.delete(key);\n this.doAnnounce(message, resolved);\n }, resolved.delay);\n this.delayTimers.set(key, timer);\n return;\n }\n\n this.doAnnounce(message, resolved);\n }\n\n clear(assertiveness?: Assertiveness): void {\n if (!assertiveness || assertiveness === 'assertive') {\n this.clearLog(this.assertiveLog);\n }\n if (!assertiveness || assertiveness === 'polite') {\n this.clearLog(this.politeLog);\n }\n }\n\n destroy(): void {\n // Cancel all pending auto-clear timers.\n this.cleanupTimers.forEach((timer) => clearTimeout(timer));\n this.cleanupTimers.clear();\n // Cancel all pending delayed announcements.\n this.delayTimers.forEach((timer) => clearTimeout(timer));\n this.delayTimers.clear();\n // Cancel any pending rAF deferred appends.\n this.pendingRafByBatchId.forEach((rafId) => cancelAnimationFrame(rafId));\n this.pendingRafByBatchId.clear();\n\n this.container?.remove();\n this.container = null;\n this.assertiveLog = null;\n this.politeLog = null;\n this.ready = false;\n this.pendingQueue = [];\n\n if (instance === this) {\n instance = null;\n }\n }\n\n isAttached(): boolean {\n return this.container?.isConnected ?? false;\n }\n\n // -----------------------------------------------------------------------\n // Private\n // -----------------------------------------------------------------------\n\n private createLog(ariaLive: Assertiveness): HTMLDivElement {\n const log = document.createElement('div');\n log.setAttribute('role', 'log');\n log.setAttribute('aria-live', ariaLive);\n log.setAttribute('aria-relevant', 'additions');\n return log;\n }\n\n /**\n * Returns the appropriate log element for the current context.\n * When an aria-modal dialog is active, announcements must be made from\n * within the modal (screen readers ignore content outside aria-modal).\n * A live region is lazily created inside the modal and cleaned up when\n * the modal is removed from the DOM.\n */\n private getLogForContext(assertiveness: Assertiveness): HTMLDivElement | null {\n // Find the first *visible* aria-modal dialog. Hidden modals (e.g. pre-\n // rendered paywall, upload-contacts) must be skipped — otherwise the\n // announcement lands inside a hidden container and is never read.\n const modal = this.findVisibleModal();\n if (!modal) {\n return assertiveness === 'assertive' ? this.assertiveLog : this.politeLog;\n }\n\n // Find or create a live region container inside the modal.\n let modalAnnouncer = modal.querySelector(`[${MODAL_ANNOUNCER_ATTR}]`) as HTMLDivElement | null;\n if (!modalAnnouncer) {\n modalAnnouncer = document.createElement('div');\n modalAnnouncer.setAttribute(MODAL_ANNOUNCER_ATTR, 'true');\n modalAnnouncer.className = SR_ONLY_CLASS;\n\n const assertiveLog = this.createLog('assertive');\n const politeLog = this.createLog('polite');\n modalAnnouncer.appendChild(assertiveLog);\n modalAnnouncer.appendChild(politeLog);\n\n modal.appendChild(modalAnnouncer);\n }\n\n const logs = modalAnnouncer.querySelectorAll('[role=\"log\"]');\n const assertiveLog = logs[0] as HTMLDivElement | null;\n const politeLog = logs[1] as HTMLDivElement | null;\n\n return assertiveness === 'assertive' ? assertiveLog : politeLog;\n }\n\n /**\n * Returns the first aria-modal element that is actually visible.\n * A modal is considered hidden if:\n * - it or an ancestor has aria-hidden=\"true\", or\n * - its computed visibility is \"hidden\" (se-design Modal uses the\n * `invisible` CSS class on the wrapper when closed).\n */\n private findVisibleModal(): HTMLElement | null {\n const modals = document.querySelectorAll('[aria-modal=\"true\"]');\n for (const m of modals) {\n const el = m as HTMLElement;\n if (el.closest('[aria-hidden=\"true\"]')) continue;\n if (getComputedStyle(el).visibility === 'hidden') continue;\n return el;\n }\n return null;\n }\n\n private doAnnounce(message: string, options: Required<AnnounceOptions>): void {\n const log = this.getLogForContext(options.assertiveness);\n if (!log) return;\n\n // batchId dedup: remove any existing node with the same batchId.\n let hadExisting = false;\n if (options.batchId) {\n const existing = log.querySelector(`[${BATCH_ATTR}=\"${CSS.escape(options.batchId)}\"]`) as HTMLElement | null;\n if (existing) {\n this.removeNode(existing);\n hadExisting = true;\n }\n }\n\n const node = document.createElement('div');\n node.textContent = message;\n\n if (options.batchId) {\n node.setAttribute(BATCH_ATTR, options.batchId);\n }\n\n const appendNode = () => {\n // Guard: destroy() may have been called during the rAF window\n if (!log.isConnected) return;\n log.appendChild(node);\n // Auto-remove after timeout to keep the DOM clean.\n if (message !== '') {\n const timer = setTimeout(() => {\n this.removeNode(node);\n }, options.timeout);\n this.cleanupTimers.set(node, timer);\n }\n };\n\n // When replacing a batched node, defer the append by one animation frame.\n // Remove + append in the same tick looks like a mutation to screen readers\n // (aria-relevant=\"additions\" is not triggered), so the announcement is missed.\n // Deferring ensures the SR sees a clean addition event.\n // Cancel any pending rAF for this batchId to avoid duplicate nodes if two\n // announce() calls fire before the first rAF runs.\n if (hadExisting) {\n const pendingRaf = this.pendingRafByBatchId.get(options.batchId);\n if (pendingRaf !== undefined) {\n cancelAnimationFrame(pendingRaf);\n }\n const rafId = requestAnimationFrame(() => {\n this.pendingRafByBatchId.delete(options.batchId);\n appendNode();\n });\n this.pendingRafByBatchId.set(options.batchId, rafId);\n } else {\n appendNode();\n }\n }\n\n private removeNode(node: HTMLElement): void {\n const timer = this.cleanupTimers.get(node);\n if (timer) {\n clearTimeout(timer);\n this.cleanupTimers.delete(node);\n }\n node.remove();\n }\n\n private clearLog(log: HTMLDivElement | null): void {\n if (!log) return;\n // Cancel timers for all children in this log.\n Array.from(log.children).forEach((child) => {\n const timer = this.cleanupTimers.get(child as HTMLElement);\n if (timer) {\n clearTimeout(timer);\n this.cleanupTimers.delete(child as HTMLElement);\n }\n });\n log.innerHTML = '';\n }\n\n private flushPendingQueue(): void {\n const queued = this.pendingQueue.splice(0);\n queued.forEach(({ message, options }) => this.doAnnounce(message, options));\n }\n\n private isTestEnvironment(): boolean {\n try {\n // React 18+ test flag\n // @ts-expect-error – global test flag\n if (typeof IS_REACT_ACT_ENVIRONMENT === 'boolean' && IS_REACT_ACT_ENVIRONMENT) return true;\n // Jest / Vitest\n // @ts-expect-error – jest global may not be typed\n if (typeof jest !== 'undefined') return true;\n // @ts-expect-error – Vitest global\n if (typeof vitest !== 'undefined') return true;\n } catch {\n // ignore\n }\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public imperative API (module-level)\n// ---------------------------------------------------------------------------\n\n/**\n * Announce a message to screen readers.\n *\n * Works anywhere — React components, event handlers, thunks, vanilla JS.\n * The singleton live region is lazily created on first call.\n *\n * @example\n * announce('5 results found');\n * announce('Error: upload failed', { assertiveness: 'assertive' });\n * announce('3 items selected', { batchId: 'selection-count' });\n * announce('Invalid email', { assertiveness: 'assertive', delay: 300 });\n */\nexport function announce(message: string, options?: AnnounceOptions): void;\nexport function announce(message: string, assertiveness?: Assertiveness): void;\nexport function announce(\n message: string,\n optionsOrAssertiveness?: AnnounceOptions | Assertiveness\n): void {\n const options: AnnounceOptions =\n typeof optionsOrAssertiveness === 'string'\n ? { assertiveness: optionsOrAssertiveness }\n : optionsOrAssertiveness ?? {};\n\n getInstance().announce(message, options);\n}\n\n/**\n * Clear all pending announcements.\n * @param assertiveness — if omitted, clears both polite and assertive logs.\n */\nexport function clearAnnouncer(assertiveness?: Assertiveness): void {\n instance?.clear(assertiveness);\n}\n\n/**\n * Remove the live-region container from the DOM and reset the singleton.\n * Typically called when the host app unmounts (via LiveAnnouncerProvider).\n */\nexport function destroyAnnouncer(): void {\n instance?.destroy();\n}\n\n/**\n * Check whether the live-region container is currently in the DOM.\n */\nexport function isAnnouncerAttached(): boolean {\n return instance?.isAttached() ?? false;\n}\n"],"names":["SR_ONLY_CLASS","BATCH_ATTR","MODAL_ANNOUNCER_ATTR","instance","getInstance","LiveAnnouncer","constructor","container","assertiveLog","politeLog","cleanupTimers","Map","delayTimers","pendingRafByBatchId","ready","pendingQueue","document","createElement","dataset","liveAnnouncer","className","createLog","appendChild","body","prepend","isTestEnvironment","setTimeout","flushPendingQueue","SAFARI_INIT_DELAY","announce","message","options","resolved","assertiveness","timeout","DEFAULT_TIMEOUT","batchId","delay","pendingTimer","get","clearTimeout","delete","push","key","Date","now","Math","random","timer","doAnnounce","set","clear","clearLog","destroy","forEach","rafId","cancelAnimationFrame","remove","isAttached","isConnected","ariaLive","log","setAttribute","getLogForContext","modal","findVisibleModal","modalAnnouncer","querySelector","logs","querySelectorAll","modals","m","el","closest","getComputedStyle","visibility","hadExisting","existing","CSS","escape","removeNode","node","textContent","appendNode","pendingRaf","undefined","requestAnimationFrame","Array","from","children","child","innerHTML","queued","splice","IS_REACT_ACT_ENVIRONMENT","jest","vitest","optionsOrAssertiveness","clearAnnouncer","destroyAnnouncer"],"mappings":"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"}
1
+ {"version":3,"file":"index75.js","sources":["../src/utils/a11y/useFocusOnReady.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport type { RefObject } from 'react';\nimport { getFirstFocusableElement } from './focusableElements';\n\nexport interface UseFocusOnReadyOptions {\n /**\n * Specific element to focus. Takes priority over containerRef.\n */\n focusRef?: RefObject<HTMLElement | null>;\n /**\n * Container to search for first focusable element.\n * Used as fallback when focusRef is not provided or its current is null.\n */\n containerRef?: RefObject<HTMLElement | null>;\n /**\n * When this transitions to true, focus is triggered.\n */\n isReady: boolean;\n /**\n * Optional guard for async content — evaluated at the moment isReady becomes true.\n * Return false to skip focusing (e.g. if the user has tabbed away while content was loading).\n * Not needed for synchronous open/close like modals or dropdowns where focus is immediate.\n */\n shouldFocus?: () => boolean;\n}\n\n/**\n * Moves focus to an element when an async condition becomes ready.\n *\n * When `focusRef` is provided, focuses that element directly.\n * When only `containerRef` is provided, finds and focuses the first focusable child.\n * When both are provided, `focusRef` takes priority; `containerRef` is the fallback.\n *\n * @example Focus first focusable in container after loading\n * ```tsx\n * useFocusOnReady({\n * containerRef: panelRef,\n * isReady: !isLoading,\n * shouldFocus: () => document.activeElement === panelRef.current\n * });\n * ```\n *\n * @example Focus a specific element when modal opens\n * ```tsx\n * useFocusOnReady({\n * focusRef: closeButtonRef,\n * isReady: isOpen\n * });\n * ```\n *\n * @example Specific element with container fallback\n * ```tsx\n * useFocusOnReady({\n * focusRef: preferredRef,\n * containerRef: dialogRef,\n * isReady: isOpen\n * });\n * ```\n */\nexport function useFocusOnReady({\n focusRef,\n containerRef,\n isReady,\n shouldFocus\n}: UseFocusOnReadyOptions): void {\n useEffect(() => {\n if (!isReady) return;\n if (shouldFocus && !shouldFocus()) return;\n\n if (focusRef?.current) {\n focusRef.current.focus();\n return;\n }\n\n if (containerRef?.current) {\n const first = getFirstFocusableElement({ container: containerRef.current });\n first?.focus();\n }\n }, [isReady]);\n}\n"],"names":["useEffect","getFirstFocusableElement","useFocusOnReady","focusRef","containerRef","isReady","shouldFocus","current","focus","container"],"mappings":"AA2DO,SAAA,aAAAA,SAAA;AAAA,SAAA,4BAAAC,SAAA;AAAA,SAASC,EAAgB;AAAA,EAC9BC,UAAAA;AAAAA,EACAC,cAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,aAAAA;AACsB,GAAS;AAC/BN,EAAAA,EAAU,MAAM;AACd,QAAKK,KACDC,EAAAA,KAAe,CAACA,MAEpB;AAAA,UAAIH,GAAUI,SAAS;AACrBJ,QAAAA,EAASI,QAAQC,MAAAA;AACjB;AAAA,MACF;AAEA,MAAIJ,GAAcG,WACFN,EAAyB;AAAA,QAAEQ,WAAWL,EAAaG;AAAAA,MAAAA,CAAS,GACnEC,MAAAA;AAAAA;AAAAA,EAEX,GAAG,CAACH,CAAO,CAAC;AACd;"}
package/dist/index76.js CHANGED
@@ -1,21 +1,174 @@
1
- import o, { useEffect as t, useMemo as c, createContext as u } from "react";
2
- import { announce as e, destroyAnnouncer as a, clearAnnouncer as l } from "./index75.js";
3
- const i = /* @__PURE__ */ u(null), f = ({
4
- children: n
5
- }) => {
6
- t(() => (e(""), () => {
7
- a();
8
- }), []);
9
- const r = c(() => ({
10
- announce: e,
11
- clear: l
12
- }), []);
13
- return /* @__PURE__ */ o.createElement(i.Provider, {
14
- value: r
15
- }, n);
16
- };
1
+ var m = Object.defineProperty;
2
+ var g = (r, e, t) => e in r ? m(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var o = (r, e, t) => g(r, typeof e != "symbol" ? e + "" : e, t);
4
+ const u = "sr-only", h = "data-batch-id", f = "data-modal-announcer";
5
+ let c = null;
6
+ function p() {
7
+ return c || (c = new T()), c;
8
+ }
9
+ class T {
10
+ constructor() {
11
+ o(this, "container", null);
12
+ o(this, "assertiveLog", null);
13
+ o(this, "politeLog", null);
14
+ o(this, "cleanupTimers", /* @__PURE__ */ new Map());
15
+ o(this, "delayTimers", /* @__PURE__ */ new Map());
16
+ o(this, "pendingRafByBatchId", /* @__PURE__ */ new Map());
17
+ o(this, "ready", !1);
18
+ o(this, "pendingQueue", []);
19
+ typeof document > "u" || (this.container = document.createElement("div"), this.container.dataset.liveAnnouncer = "true", this.container.className = u, this.assertiveLog = this.createLog("assertive"), this.politeLog = this.createLog("polite"), this.container.appendChild(this.assertiveLog), this.container.appendChild(this.politeLog), document.body.prepend(this.container), this.isTestEnvironment() ? this.ready = !0 : setTimeout(() => {
20
+ this.ready = !0, this.flushPendingQueue();
21
+ }, 100));
22
+ }
23
+ // -----------------------------------------------------------------------
24
+ // Public
25
+ // -----------------------------------------------------------------------
26
+ announce(e, t = {}) {
27
+ const i = {
28
+ assertiveness: t.assertiveness ?? "polite",
29
+ timeout: t.timeout ?? 7e3,
30
+ batchId: t.batchId ?? "",
31
+ delay: t.delay ?? 0
32
+ };
33
+ if (i.batchId) {
34
+ const n = this.delayTimers.get(i.batchId);
35
+ n && (clearTimeout(n), this.delayTimers.delete(i.batchId));
36
+ }
37
+ if (!this.ready) {
38
+ this.pendingQueue.push({
39
+ message: e,
40
+ options: i
41
+ });
42
+ return;
43
+ }
44
+ if (i.delay > 0) {
45
+ const n = i.batchId || `__delay_${Date.now()}_${Math.random()}`, a = setTimeout(() => {
46
+ this.delayTimers.delete(n), this.doAnnounce(e, i);
47
+ }, i.delay);
48
+ this.delayTimers.set(n, a);
49
+ return;
50
+ }
51
+ this.doAnnounce(e, i);
52
+ }
53
+ clear(e) {
54
+ (!e || e === "assertive") && this.clearLog(this.assertiveLog), (!e || e === "polite") && this.clearLog(this.politeLog);
55
+ }
56
+ destroy() {
57
+ this.cleanupTimers.forEach((e) => clearTimeout(e)), this.cleanupTimers.clear(), this.delayTimers.forEach((e) => clearTimeout(e)), this.delayTimers.clear(), this.pendingRafByBatchId.forEach((e) => cancelAnimationFrame(e)), this.pendingRafByBatchId.clear(), this.container?.remove(), this.container = null, this.assertiveLog = null, this.politeLog = null, this.ready = !1, this.pendingQueue = [], c === this && (c = null);
58
+ }
59
+ isAttached() {
60
+ return this.container?.isConnected ?? !1;
61
+ }
62
+ // -----------------------------------------------------------------------
63
+ // Private
64
+ // -----------------------------------------------------------------------
65
+ createLog(e) {
66
+ const t = document.createElement("div");
67
+ return t.setAttribute("role", "log"), t.setAttribute("aria-live", e), t.setAttribute("aria-relevant", "additions"), t;
68
+ }
69
+ /**
70
+ * Returns the appropriate log element for the current context.
71
+ * When an aria-modal dialog is active, announcements must be made from
72
+ * within the modal (screen readers ignore content outside aria-modal).
73
+ * A live region is lazily created inside the modal and cleaned up when
74
+ * the modal is removed from the DOM.
75
+ */
76
+ getLogForContext(e) {
77
+ const t = this.findVisibleModal();
78
+ if (!t)
79
+ return e === "assertive" ? this.assertiveLog : this.politeLog;
80
+ let i = t.querySelector(`[${f}]`);
81
+ if (!i) {
82
+ i = document.createElement("div"), i.setAttribute(f, "true"), i.className = u;
83
+ const s = this.createLog("assertive"), d = this.createLog("polite");
84
+ i.appendChild(s), i.appendChild(d), t.appendChild(i);
85
+ }
86
+ const n = i.querySelectorAll('[role="log"]'), a = n[0], l = n[1];
87
+ return e === "assertive" ? a : l;
88
+ }
89
+ /**
90
+ * Returns the first aria-modal element that is actually visible.
91
+ * A modal is considered hidden if:
92
+ * - it or an ancestor has aria-hidden="true", or
93
+ * - its computed visibility is "hidden" (se-design Modal uses the
94
+ * `invisible` CSS class on the wrapper when closed).
95
+ */
96
+ findVisibleModal() {
97
+ const e = document.querySelectorAll('[aria-modal="true"]');
98
+ for (const t of e) {
99
+ const i = t;
100
+ if (!i.closest('[aria-hidden="true"]') && getComputedStyle(i).visibility !== "hidden")
101
+ return i;
102
+ }
103
+ return null;
104
+ }
105
+ doAnnounce(e, t) {
106
+ const i = this.getLogForContext(t.assertiveness);
107
+ if (!i) return;
108
+ let n = !1;
109
+ if (t.batchId) {
110
+ const s = i.querySelector(`[${h}="${CSS.escape(t.batchId)}"]`);
111
+ s && (this.removeNode(s), n = !0);
112
+ }
113
+ const a = document.createElement("div");
114
+ a.textContent = e, t.batchId && a.setAttribute(h, t.batchId);
115
+ const l = () => {
116
+ if (i.isConnected && (i.appendChild(a), e !== "")) {
117
+ const s = setTimeout(() => {
118
+ this.removeNode(a);
119
+ }, t.timeout);
120
+ this.cleanupTimers.set(a, s);
121
+ }
122
+ };
123
+ if (n) {
124
+ const s = this.pendingRafByBatchId.get(t.batchId);
125
+ s !== void 0 && cancelAnimationFrame(s);
126
+ const d = requestAnimationFrame(() => {
127
+ this.pendingRafByBatchId.delete(t.batchId), l();
128
+ });
129
+ this.pendingRafByBatchId.set(t.batchId, d);
130
+ } else
131
+ l();
132
+ }
133
+ removeNode(e) {
134
+ const t = this.cleanupTimers.get(e);
135
+ t && (clearTimeout(t), this.cleanupTimers.delete(e)), e.remove();
136
+ }
137
+ clearLog(e) {
138
+ e && (Array.from(e.children).forEach((t) => {
139
+ const i = this.cleanupTimers.get(t);
140
+ i && (clearTimeout(i), this.cleanupTimers.delete(t));
141
+ }), e.innerHTML = "");
142
+ }
143
+ flushPendingQueue() {
144
+ this.pendingQueue.splice(0).forEach(({
145
+ message: t,
146
+ options: i
147
+ }) => this.doAnnounce(t, i));
148
+ }
149
+ isTestEnvironment() {
150
+ try {
151
+ if (typeof IS_REACT_ACT_ENVIRONMENT == "boolean" && IS_REACT_ACT_ENVIRONMENT || typeof jest < "u" || typeof vitest < "u") return !0;
152
+ } catch {
153
+ }
154
+ return !1;
155
+ }
156
+ }
157
+ function A(r, e) {
158
+ const t = typeof e == "string" ? {
159
+ assertiveness: e
160
+ } : e ?? {};
161
+ p().announce(r, t);
162
+ }
163
+ function L(r) {
164
+ c?.clear(r);
165
+ }
166
+ function I() {
167
+ c?.destroy();
168
+ }
17
169
  export {
18
- i as LiveAnnouncerContext,
19
- f as LiveAnnouncerProvider
170
+ A as announce,
171
+ L as clearAnnouncer,
172
+ I as destroyAnnouncer
20
173
  };
21
174
  //# sourceMappingURL=index76.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index76.js","sources":["../src/utils/a11y/liveAnnouncer/LiveAnnouncerProvider.tsx"],"sourcesContent":["import React, { createContext, useEffect, useMemo } from 'react';\nimport {\n announce,\n clearAnnouncer,\n destroyAnnouncer,\n type AnnounceOptions,\n type Assertiveness,\n} from './LiveAnnouncer';\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nexport interface LiveAnnouncerContextValue {\n announce: (message: string, options?: AnnounceOptions) => void;\n clear: (assertiveness?: Assertiveness) => void;\n}\n\nexport const LiveAnnouncerContext = createContext<LiveAnnouncerContextValue | null>(null);\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport interface LiveAnnouncerProviderProps {\n children: React.ReactNode;\n}\n\n/**\n * Mounts the singleton live-region container on mount and tears it down on\n * unmount. Provides `{ announce, clear }` via React context so descendants\n * can use the `useLiveAnnouncer` hook.\n *\n * Optional — `announce()` works without a provider (the singleton auto-creates\n * on first call). The provider simply gives you explicit lifecycle control.\n *\n * @example\n * <LiveAnnouncerProvider>\n * <App />\n * </LiveAnnouncerProvider>\n */\nexport const LiveAnnouncerProvider: React.FC<LiveAnnouncerProviderProps> = ({ children }) => {\n // Trigger singleton creation on mount (if not already created).\n // The announce import already lazily creates, but calling it with an empty\n // string ensures the container is in the DOM before any child renders.\n useEffect(() => {\n // Force singleton init by importing — the constructor runs on getInstance().\n // We don't announce anything; just ensure the container exists.\n announce('');\n return () => {\n destroyAnnouncer();\n };\n }, []);\n\n const value = useMemo<LiveAnnouncerContextValue>(\n () => ({ announce, clear: clearAnnouncer }),\n []\n );\n\n return (\n <LiveAnnouncerContext.Provider value={value}>\n {children}\n </LiveAnnouncerContext.Provider>\n );\n};\n"],"names":["React__default","useEffect","useMemo","createContext","announce","destroyAnnouncer","clearAnnouncer","LiveAnnouncerContext","LiveAnnouncerProvider","children","value","clear","React","createElement","Provider"],"mappings":"AAkBO,OAAAA,KAAA,aAAAC,GAAA,WAAAC,GAAA,iBAAAC,SAAA;AAAA,SAAA,YAAAC,GAAA,oBAAAC,GAAA,kBAAAC,SAAA;AAAA,MAAMC,sBAAuE,IAAI,GAuB3EC,IAA8DA,CAAC;AAAA,EAAEC,UAAAA;AAAS,MAAM;AAI3FR,EAAAA,EAAU,OAGRG,EAAS,EAAE,GACJ,MAAM;AACXC,IAAAA,EAAAA;AAAAA,EACF,IACC,CAAA,CAAE;AAEL,QAAMK,IAAQR,EACZ,OAAO;AAAA,IAAEE,UAAAA;AAAAA,IAAUO,OAAOL;AAAAA,EAAAA,IAC1B,CAAA,CACF;AAEA,SACEM,gBAAAA,EAAAC,cAACN,EAAqBO,UAAQ;AAAA,IAACJ,OAAAA;AAAAA,EAAAA,GAC5BD,CAC4B;AAEnC;"}
1
+ {"version":3,"file":"index76.js","sources":["../src/utils/a11y/liveAnnouncer/LiveAnnouncer.ts"],"sourcesContent":["/**\n * LiveAnnouncer — singleton vanilla-DOM announcer for screen readers.\n *\n * Architecture:\n * - Prepends a visually-hidden container to document.body on first use.\n * - Two <div role=\"log\" aria-live=\"...\"> regions: one polite, one assertive.\n * - Each announce() appends a NEW child node (aria-relevant=\"additions\" makes\n * screen readers announce only additions, never re-read existing content).\n * - Child nodes auto-remove after a timeout (default 7 s) to keep the DOM clean.\n * - batchId option: if a node with the same batchId already exists in a log,\n * it is replaced — so only the final value is announced (e.g. \"N results\").\n *\n * No React dependency. No role=\"status\" or role=\"alert\" (avoids double-announce\n * bugs on iOS VoiceOver).\n */\n\nexport type Assertiveness = 'assertive' | 'polite';\n\nexport interface AnnounceOptions {\n /** Default: 'polite' */\n assertiveness?: Assertiveness;\n /** Auto-clear timeout in ms. Default: 7000 */\n timeout?: number;\n /**\n * When provided, any pending node with the same batchId is replaced rather\n * than appending a second node. Useful for rapidly-updating counts\n * (e.g. search results, selection count).\n */\n batchId?: string;\n /**\n * Delay in ms before the announcement is made. Useful when focus changes\n * would otherwise swallow the announcement (e.g. after adding a tag in\n * a combobox). A subsequent call with the same batchId cancels any\n * pending delayed announcement.\n */\n delay?: number;\n}\n\nconst DEFAULT_TIMEOUT = 7000;\nconst SAFARI_INIT_DELAY = 100;\n\nconst SR_ONLY_CLASS = 'sr-only';\n\n/** Attribute used to find batchId nodes for replacement. */\nconst BATCH_ATTR = 'data-batch-id';\n\n/** Attribute used to identify announcer containers injected into modals. */\nconst MODAL_ANNOUNCER_ATTR = 'data-modal-announcer';\n\n// ---------------------------------------------------------------------------\n// Singleton\n// ---------------------------------------------------------------------------\n\nlet instance: LiveAnnouncer | null = null;\n\n/**\n * Get or create the singleton LiveAnnouncer.\n * Safe to call multiple times — returns the same instance.\n */\nfunction getInstance(): LiveAnnouncer {\n if (!instance) {\n instance = new LiveAnnouncer();\n }\n return instance;\n}\n\n// ---------------------------------------------------------------------------\n// Class\n// ---------------------------------------------------------------------------\n\nclass LiveAnnouncer {\n private container: HTMLDivElement | null = null;\n private assertiveLog: HTMLDivElement | null = null;\n private politeLog: HTMLDivElement | null = null;\n private cleanupTimers: Map<HTMLElement, ReturnType<typeof setTimeout>> = new Map();\n private delayTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private pendingRafByBatchId: Map<string, number> = new Map();\n private ready = false;\n private pendingQueue: Array<{ message: string; options: Required<AnnounceOptions> }> = [];\n\n constructor() {\n if (typeof document === 'undefined') return;\n\n this.container = document.createElement('div');\n this.container.dataset.liveAnnouncer = 'true';\n this.container.className = SR_ONLY_CLASS;\n\n this.assertiveLog = this.createLog('assertive');\n this.politeLog = this.createLog('polite');\n\n this.container.appendChild(this.assertiveLog);\n this.container.appendChild(this.politeLog);\n\n // Prepend so screen readers encounter it early in the DOM.\n document.body.prepend(this.container);\n\n // Safari/VoiceOver ignores content injected within ~100 ms of the live\n // region being added to the DOM. Delay readiness accordingly.\n // In test environments, skip the delay.\n if (this.isTestEnvironment()) {\n this.ready = true;\n } else {\n setTimeout(() => {\n this.ready = true;\n this.flushPendingQueue();\n }, SAFARI_INIT_DELAY);\n }\n }\n\n // -----------------------------------------------------------------------\n // Public\n // -----------------------------------------------------------------------\n\n announce(message: string, options: AnnounceOptions = {}): void {\n const resolved: Required<AnnounceOptions> = {\n assertiveness: options.assertiveness ?? 'polite',\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n batchId: options.batchId ?? '',\n delay: options.delay ?? 0,\n };\n\n // Cancel any pending delayed announcement with the same batchId.\n if (resolved.batchId) {\n const pendingTimer = this.delayTimers.get(resolved.batchId);\n if (pendingTimer) {\n clearTimeout(pendingTimer);\n this.delayTimers.delete(resolved.batchId);\n }\n }\n\n if (!this.ready) {\n this.pendingQueue.push({ message, options: resolved });\n return;\n }\n\n if (resolved.delay > 0) {\n const key = resolved.batchId || `__delay_${Date.now()}_${Math.random()}`;\n const timer = setTimeout(() => {\n this.delayTimers.delete(key);\n this.doAnnounce(message, resolved);\n }, resolved.delay);\n this.delayTimers.set(key, timer);\n return;\n }\n\n this.doAnnounce(message, resolved);\n }\n\n clear(assertiveness?: Assertiveness): void {\n if (!assertiveness || assertiveness === 'assertive') {\n this.clearLog(this.assertiveLog);\n }\n if (!assertiveness || assertiveness === 'polite') {\n this.clearLog(this.politeLog);\n }\n }\n\n destroy(): void {\n // Cancel all pending auto-clear timers.\n this.cleanupTimers.forEach((timer) => clearTimeout(timer));\n this.cleanupTimers.clear();\n // Cancel all pending delayed announcements.\n this.delayTimers.forEach((timer) => clearTimeout(timer));\n this.delayTimers.clear();\n // Cancel any pending rAF deferred appends.\n this.pendingRafByBatchId.forEach((rafId) => cancelAnimationFrame(rafId));\n this.pendingRafByBatchId.clear();\n\n this.container?.remove();\n this.container = null;\n this.assertiveLog = null;\n this.politeLog = null;\n this.ready = false;\n this.pendingQueue = [];\n\n if (instance === this) {\n instance = null;\n }\n }\n\n isAttached(): boolean {\n return this.container?.isConnected ?? false;\n }\n\n // -----------------------------------------------------------------------\n // Private\n // -----------------------------------------------------------------------\n\n private createLog(ariaLive: Assertiveness): HTMLDivElement {\n const log = document.createElement('div');\n log.setAttribute('role', 'log');\n log.setAttribute('aria-live', ariaLive);\n log.setAttribute('aria-relevant', 'additions');\n return log;\n }\n\n /**\n * Returns the appropriate log element for the current context.\n * When an aria-modal dialog is active, announcements must be made from\n * within the modal (screen readers ignore content outside aria-modal).\n * A live region is lazily created inside the modal and cleaned up when\n * the modal is removed from the DOM.\n */\n private getLogForContext(assertiveness: Assertiveness): HTMLDivElement | null {\n // Find the first *visible* aria-modal dialog. Hidden modals (e.g. pre-\n // rendered paywall, upload-contacts) must be skipped — otherwise the\n // announcement lands inside a hidden container and is never read.\n const modal = this.findVisibleModal();\n if (!modal) {\n return assertiveness === 'assertive' ? this.assertiveLog : this.politeLog;\n }\n\n // Find or create a live region container inside the modal.\n let modalAnnouncer = modal.querySelector(`[${MODAL_ANNOUNCER_ATTR}]`) as HTMLDivElement | null;\n if (!modalAnnouncer) {\n modalAnnouncer = document.createElement('div');\n modalAnnouncer.setAttribute(MODAL_ANNOUNCER_ATTR, 'true');\n modalAnnouncer.className = SR_ONLY_CLASS;\n\n const assertiveLog = this.createLog('assertive');\n const politeLog = this.createLog('polite');\n modalAnnouncer.appendChild(assertiveLog);\n modalAnnouncer.appendChild(politeLog);\n\n modal.appendChild(modalAnnouncer);\n }\n\n const logs = modalAnnouncer.querySelectorAll('[role=\"log\"]');\n const assertiveLog = logs[0] as HTMLDivElement | null;\n const politeLog = logs[1] as HTMLDivElement | null;\n\n return assertiveness === 'assertive' ? assertiveLog : politeLog;\n }\n\n /**\n * Returns the first aria-modal element that is actually visible.\n * A modal is considered hidden if:\n * - it or an ancestor has aria-hidden=\"true\", or\n * - its computed visibility is \"hidden\" (se-design Modal uses the\n * `invisible` CSS class on the wrapper when closed).\n */\n private findVisibleModal(): HTMLElement | null {\n const modals = document.querySelectorAll('[aria-modal=\"true\"]');\n for (const m of modals) {\n const el = m as HTMLElement;\n if (el.closest('[aria-hidden=\"true\"]')) continue;\n if (getComputedStyle(el).visibility === 'hidden') continue;\n return el;\n }\n return null;\n }\n\n private doAnnounce(message: string, options: Required<AnnounceOptions>): void {\n const log = this.getLogForContext(options.assertiveness);\n if (!log) return;\n\n // batchId dedup: remove any existing node with the same batchId.\n let hadExisting = false;\n if (options.batchId) {\n const existing = log.querySelector(`[${BATCH_ATTR}=\"${CSS.escape(options.batchId)}\"]`) as HTMLElement | null;\n if (existing) {\n this.removeNode(existing);\n hadExisting = true;\n }\n }\n\n const node = document.createElement('div');\n node.textContent = message;\n\n if (options.batchId) {\n node.setAttribute(BATCH_ATTR, options.batchId);\n }\n\n const appendNode = () => {\n // Guard: destroy() may have been called during the rAF window\n if (!log.isConnected) return;\n log.appendChild(node);\n // Auto-remove after timeout to keep the DOM clean.\n if (message !== '') {\n const timer = setTimeout(() => {\n this.removeNode(node);\n }, options.timeout);\n this.cleanupTimers.set(node, timer);\n }\n };\n\n // When replacing a batched node, defer the append by one animation frame.\n // Remove + append in the same tick looks like a mutation to screen readers\n // (aria-relevant=\"additions\" is not triggered), so the announcement is missed.\n // Deferring ensures the SR sees a clean addition event.\n // Cancel any pending rAF for this batchId to avoid duplicate nodes if two\n // announce() calls fire before the first rAF runs.\n if (hadExisting) {\n const pendingRaf = this.pendingRafByBatchId.get(options.batchId);\n if (pendingRaf !== undefined) {\n cancelAnimationFrame(pendingRaf);\n }\n const rafId = requestAnimationFrame(() => {\n this.pendingRafByBatchId.delete(options.batchId);\n appendNode();\n });\n this.pendingRafByBatchId.set(options.batchId, rafId);\n } else {\n appendNode();\n }\n }\n\n private removeNode(node: HTMLElement): void {\n const timer = this.cleanupTimers.get(node);\n if (timer) {\n clearTimeout(timer);\n this.cleanupTimers.delete(node);\n }\n node.remove();\n }\n\n private clearLog(log: HTMLDivElement | null): void {\n if (!log) return;\n // Cancel timers for all children in this log.\n Array.from(log.children).forEach((child) => {\n const timer = this.cleanupTimers.get(child as HTMLElement);\n if (timer) {\n clearTimeout(timer);\n this.cleanupTimers.delete(child as HTMLElement);\n }\n });\n log.innerHTML = '';\n }\n\n private flushPendingQueue(): void {\n const queued = this.pendingQueue.splice(0);\n queued.forEach(({ message, options }) => this.doAnnounce(message, options));\n }\n\n private isTestEnvironment(): boolean {\n try {\n // React 18+ test flag\n // @ts-expect-error – global test flag\n if (typeof IS_REACT_ACT_ENVIRONMENT === 'boolean' && IS_REACT_ACT_ENVIRONMENT) return true;\n // Jest / Vitest\n // @ts-expect-error – jest global may not be typed\n if (typeof jest !== 'undefined') return true;\n // @ts-expect-error – Vitest global\n if (typeof vitest !== 'undefined') return true;\n } catch {\n // ignore\n }\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public imperative API (module-level)\n// ---------------------------------------------------------------------------\n\n/**\n * Announce a message to screen readers.\n *\n * Works anywhere — React components, event handlers, thunks, vanilla JS.\n * The singleton live region is lazily created on first call.\n *\n * @example\n * announce('5 results found');\n * announce('Error: upload failed', { assertiveness: 'assertive' });\n * announce('3 items selected', { batchId: 'selection-count' });\n * announce('Invalid email', { assertiveness: 'assertive', delay: 300 });\n */\nexport function announce(message: string, options?: AnnounceOptions): void;\nexport function announce(message: string, assertiveness?: Assertiveness): void;\nexport function announce(\n message: string,\n optionsOrAssertiveness?: AnnounceOptions | Assertiveness\n): void {\n const options: AnnounceOptions =\n typeof optionsOrAssertiveness === 'string'\n ? { assertiveness: optionsOrAssertiveness }\n : optionsOrAssertiveness ?? {};\n\n getInstance().announce(message, options);\n}\n\n/**\n * Clear all pending announcements.\n * @param assertiveness — if omitted, clears both polite and assertive logs.\n */\nexport function clearAnnouncer(assertiveness?: Assertiveness): void {\n instance?.clear(assertiveness);\n}\n\n/**\n * Remove the live-region container from the DOM and reset the singleton.\n * Typically called when the host app unmounts (via LiveAnnouncerProvider).\n */\nexport function destroyAnnouncer(): void {\n instance?.destroy();\n}\n\n/**\n * Check whether the live-region container is currently in the DOM.\n */\nexport function isAnnouncerAttached(): boolean {\n return instance?.isAttached() ?? false;\n}\n"],"names":["SR_ONLY_CLASS","BATCH_ATTR","MODAL_ANNOUNCER_ATTR","instance","getInstance","LiveAnnouncer","constructor","container","assertiveLog","politeLog","cleanupTimers","Map","delayTimers","pendingRafByBatchId","ready","pendingQueue","document","createElement","dataset","liveAnnouncer","className","createLog","appendChild","body","prepend","isTestEnvironment","setTimeout","flushPendingQueue","SAFARI_INIT_DELAY","announce","message","options","resolved","assertiveness","timeout","DEFAULT_TIMEOUT","batchId","delay","pendingTimer","get","clearTimeout","delete","push","key","Date","now","Math","random","timer","doAnnounce","set","clear","clearLog","destroy","forEach","rafId","cancelAnimationFrame","remove","isAttached","isConnected","ariaLive","log","setAttribute","getLogForContext","modal","findVisibleModal","modalAnnouncer","querySelector","logs","querySelectorAll","modals","m","el","closest","getComputedStyle","visibility","hadExisting","existing","CSS","escape","removeNode","node","textContent","appendNode","pendingRaf","undefined","requestAnimationFrame","Array","from","children","child","innerHTML","queued","splice","IS_REACT_ACT_ENVIRONMENT","jest","vitest","optionsOrAssertiveness","clearAnnouncer","destroyAnnouncer"],"mappings":"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"}