analytica-frontend-lib 1.7.21 → 1.7.23

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 (384) hide show
  1. package/dist/AccessReportModal/index.js +3 -3
  2. package/dist/AccessReportModal/index.mjs +2 -2
  3. package/dist/ActivitiesHistory/index.js +49 -49
  4. package/dist/ActivitiesHistory/index.mjs +48 -48
  5. package/dist/ActivityCardQuestionBanks/index.js +49 -49
  6. package/dist/ActivityCardQuestionBanks/index.mjs +48 -48
  7. package/dist/ActivityCardQuestionPreview/index.js +49 -49
  8. package/dist/ActivityCardQuestionPreview/index.mjs +48 -48
  9. package/dist/ActivityCreate/ActivityCreateContent/index.js +49 -49
  10. package/dist/ActivityCreate/ActivityCreateContent/index.mjs +48 -48
  11. package/dist/ActivityCreate/index.js +49 -49
  12. package/dist/ActivityCreate/index.mjs +48 -48
  13. package/dist/ActivityDetails/index.js +49 -49
  14. package/dist/ActivityDetails/index.mjs +48 -48
  15. package/dist/ActivityFilters/index.js +49 -49
  16. package/dist/ActivityFilters/index.mjs +48 -48
  17. package/dist/ActivityPageLayout/index.js +49 -49
  18. package/dist/ActivityPageLayout/index.mjs +48 -48
  19. package/dist/ActivityPreview/index.js +49 -49
  20. package/dist/ActivityPreview/index.mjs +48 -48
  21. package/dist/AlertManager/index.js +49 -49
  22. package/dist/AlertManager/index.mjs +48 -48
  23. package/dist/AlertManagerView/index.js +3 -3
  24. package/dist/AlertManagerView/index.mjs +2 -2
  25. package/dist/AnnouncementModal/index.js +3 -3
  26. package/dist/AnnouncementModal/index.mjs +2 -2
  27. package/dist/AnnouncementsGate/index.js +4 -4
  28. package/dist/AnnouncementsGate/index.mjs +3 -3
  29. package/dist/AnswerKeyModal/index.js +3 -3
  30. package/dist/AnswerKeyModal/index.mjs +2 -2
  31. package/dist/AppHeader/index.js +6 -6
  32. package/dist/AppHeader/index.mjs +5 -5
  33. package/dist/CalendarCard/index.js +4 -4
  34. package/dist/CalendarCard/index.mjs +3 -3
  35. package/dist/Chat/index.js +3 -3
  36. package/dist/Chat/index.mjs +2 -2
  37. package/dist/CheckBox/index.d.ts.map +1 -1
  38. package/dist/CheckBox/index.js +2 -2
  39. package/dist/CheckBox/index.mjs +1 -1
  40. package/dist/CheckBoxGroup/index.js +49 -49
  41. package/dist/CheckBoxGroup/index.mjs +48 -48
  42. package/dist/ComparatorChart/ComparatorView/index.js +3 -3
  43. package/dist/ComparatorChart/ComparatorView/index.mjs +2 -2
  44. package/dist/CorrectActivityModal/index.js +7 -7
  45. package/dist/CorrectActivityModal/index.mjs +6 -6
  46. package/dist/DownloadButton/index.js +4 -4
  47. package/dist/DownloadButton/index.mjs +3 -3
  48. package/dist/DownloadModal/index.js +3 -3
  49. package/dist/DownloadModal/index.mjs +2 -2
  50. package/dist/DropdownMenu/index.js +3 -3
  51. package/dist/DropdownMenu/index.mjs +2 -2
  52. package/dist/EnemClassroomStartModal/index.d.ts.map +1 -1
  53. package/dist/EnemClassroomStartModal/index.js +7 -7
  54. package/dist/EnemClassroomStartModal/index.mjs +6 -6
  55. package/dist/EnemMoment/index.js +57 -57
  56. package/dist/EnemMoment/index.mjs +51 -51
  57. package/dist/EnemMomentStudentModal/index.js +50 -50
  58. package/dist/EnemMomentStudentModal/index.mjs +48 -48
  59. package/dist/ExamDetailsLayout/ExamDetailsPage/index.js +49 -49
  60. package/dist/ExamDetailsLayout/ExamDetailsPage/index.mjs +48 -48
  61. package/dist/ExamPageLayout/index.js +49 -49
  62. package/dist/ExamPageLayout/index.mjs +48 -48
  63. package/dist/Forum/index.js +5 -5
  64. package/dist/Forum/index.mjs +4 -4
  65. package/dist/KnowledgePerformanceCard/index.js +51 -51
  66. package/dist/KnowledgePerformanceCard/index.mjs +50 -50
  67. package/dist/LessonCardPreview/index.js +49 -49
  68. package/dist/LessonCardPreview/index.mjs +48 -48
  69. package/dist/LessonMediaSections/index.js +49 -49
  70. package/dist/LessonMediaSections/index.mjs +48 -48
  71. package/dist/LessonsCatalog/index.js +49 -49
  72. package/dist/LessonsCatalog/index.mjs +48 -48
  73. package/dist/Modal/index.d.ts.map +1 -1
  74. package/dist/Modal/index.js +2 -2
  75. package/dist/Modal/index.mjs +1 -1
  76. package/dist/MultiCheckSelect/index.js +5 -5
  77. package/dist/MultiCheckSelect/index.mjs +4 -4
  78. package/dist/MultiSearchSelect/index.js +3 -3
  79. package/dist/MultiSearchSelect/index.mjs +2 -2
  80. package/dist/MultipleChoice/index.js +3 -3
  81. package/dist/MultipleChoice/index.mjs +2 -2
  82. package/dist/NotificationCard/index.js +4 -4
  83. package/dist/NotificationCard/index.mjs +3 -3
  84. package/dist/PerformanceReportModal/index.js +3 -3
  85. package/dist/PerformanceReportModal/index.mjs +2 -2
  86. package/dist/PrintableUsersTable/index.js +49 -49
  87. package/dist/PrintableUsersTable/index.mjs +48 -48
  88. package/dist/ProgressModal/index.js +3 -3
  89. package/dist/ProgressModal/index.mjs +2 -2
  90. package/dist/QuestionsPerformanceCard/index.js +50 -50
  91. package/dist/QuestionsPerformanceCard/index.mjs +49 -49
  92. package/dist/Quiz/index.js +6 -6
  93. package/dist/Quiz/index.mjs +5 -5
  94. package/dist/RecommendedLessonCreate/index.js +49 -49
  95. package/dist/RecommendedLessonCreate/index.mjs +48 -48
  96. package/dist/RecommendedLessonDetails/index.js +7 -7
  97. package/dist/RecommendedLessonDetails/index.mjs +6 -6
  98. package/dist/RecommendedLessonsHistory/index.js +49 -49
  99. package/dist/RecommendedLessonsHistory/index.mjs +48 -48
  100. package/dist/ReportDetailModal/index.js +4 -4
  101. package/dist/ReportDetailModal/index.mjs +3 -3
  102. package/dist/RichEditor/index.js +5 -5
  103. package/dist/RichEditor/index.mjs +4 -4
  104. package/dist/{RichEditorCore-6MTYSSKS.mjs → RichEditorCore-2S5YUAOI.mjs} +5 -5
  105. package/dist/{RichEditorCore-KDARBEK4.js → RichEditorCore-N7A2445B.js} +16 -16
  106. package/dist/{RichEditorCore-KDARBEK4.js.map → RichEditorCore-N7A2445B.js.map} +1 -1
  107. package/dist/Search/index.js +4 -4
  108. package/dist/Search/index.mjs +3 -3
  109. package/dist/SendActivityModal/SendActivityModal.js +49 -49
  110. package/dist/SendActivityModal/SendActivityModal.mjs +48 -48
  111. package/dist/SendActivityModal/index.js +49 -49
  112. package/dist/SendActivityModal/index.mjs +48 -48
  113. package/dist/SendLessonModal/index.js +49 -49
  114. package/dist/SendLessonModal/index.mjs +48 -48
  115. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.js +49 -49
  116. package/dist/SimulatedPerformance/SimulatedPerformanceView/index.mjs +48 -48
  117. package/dist/SimulatedPerformance/useSimulatedPerformance/index.js +5 -5
  118. package/dist/SimulatedPerformance/useSimulatedPerformance/index.mjs +4 -4
  119. package/dist/SimulatedStudentSimulationsModal/index.js +6 -6
  120. package/dist/SimulatedStudentSimulationsModal/index.mjs +5 -5
  121. package/dist/SimulationsPage/index.js +49 -49
  122. package/dist/SimulationsPage/index.mjs +48 -48
  123. package/dist/StudentLessonProgressModal/index.js +3 -3
  124. package/dist/StudentLessonProgressModal/index.mjs +2 -2
  125. package/dist/StudentPerformanceDetailsModal/index.js +5 -5
  126. package/dist/StudentPerformanceDetailsModal/index.mjs +4 -4
  127. package/dist/Support/TicketModal/index.js +3 -3
  128. package/dist/Support/TicketModal/index.mjs +2 -2
  129. package/dist/Support/index.js +4 -4
  130. package/dist/Support/index.mjs +3 -3
  131. package/dist/SupportChatComposer/index.js +3 -3
  132. package/dist/SupportChatComposer/index.mjs +2 -2
  133. package/dist/SupportChatEvent/index.js +3 -3
  134. package/dist/SupportChatEvent/index.mjs +2 -2
  135. package/dist/SupportChatMessage/index.js +3 -3
  136. package/dist/SupportChatMessage/index.mjs +2 -2
  137. package/dist/SupportChatWidget/index.js +3 -3
  138. package/dist/SupportChatWidget/index.mjs +2 -2
  139. package/dist/SupportConcludeModal/index.js +4 -4
  140. package/dist/SupportConcludeModal/index.mjs +3 -3
  141. package/dist/SupportConversationHeader/index.js +4 -4
  142. package/dist/SupportConversationHeader/index.mjs +3 -3
  143. package/dist/SupportConversationMessages/index.js +5 -5
  144. package/dist/SupportConversationMessages/index.mjs +4 -4
  145. package/dist/SupportTicketConversation/index.js +12 -12
  146. package/dist/SupportTicketConversation/index.mjs +11 -11
  147. package/dist/TableProvider/index.js +49 -49
  148. package/dist/TableProvider/index.mjs +48 -48
  149. package/dist/UnifiedDraftModelPage/index.js +49 -49
  150. package/dist/UnifiedDraftModelPage/index.mjs +48 -48
  151. package/dist/UnifiedHistoryPage/index.js +49 -49
  152. package/dist/UnifiedHistoryPage/index.mjs +48 -48
  153. package/dist/VideoPlayer/index.js +5 -5
  154. package/dist/VideoPlayer/index.mjs +4 -4
  155. package/dist/Whiteboard/index.js +3 -3
  156. package/dist/Whiteboard/index.mjs +2 -2
  157. package/dist/{chunk-B6QOBHZM.mjs → chunk-24BEZK43.mjs} +2 -2
  158. package/dist/{chunk-CYSODZGX.js → chunk-27Y6V3GM.js} +169 -66
  159. package/dist/chunk-27Y6V3GM.js.map +1 -0
  160. package/dist/{chunk-OQTXTDSK.mjs → chunk-2OJ3QSLU.mjs} +3 -3
  161. package/dist/{chunk-VDUO65VK.mjs → chunk-3EGJJDIM.mjs} +2 -2
  162. package/dist/{chunk-VDUO65VK.mjs.map → chunk-3EGJJDIM.mjs.map} +1 -1
  163. package/dist/{chunk-4OUBPEVS.mjs → chunk-3HKLGNUE.mjs} +3 -3
  164. package/dist/{chunk-YA7XLH7D.js → chunk-3MFACEZ2.js} +3 -3
  165. package/dist/{chunk-YA7XLH7D.js.map → chunk-3MFACEZ2.js.map} +1 -1
  166. package/dist/{chunk-IHBWJM4C.mjs → chunk-3Q7RJKUP.mjs} +2 -2
  167. package/dist/{chunk-PY6WD6SB.mjs → chunk-3WRB4WCD.mjs} +4 -4
  168. package/dist/{chunk-4RHCBMMP.js → chunk-46VHAL4S.js} +3 -3
  169. package/dist/{chunk-4RHCBMMP.js.map → chunk-46VHAL4S.js.map} +1 -1
  170. package/dist/{chunk-POT3ALY3.js → chunk-5DINXU6W.js} +1 -1
  171. package/dist/{chunk-POT3ALY3.js.map → chunk-5DINXU6W.js.map} +1 -1
  172. package/dist/{chunk-L52NFNFK.mjs → chunk-5VC4BK65.mjs} +3 -3
  173. package/dist/{chunk-B67RLIUM.js → chunk-72D7G6YQ.js} +3 -3
  174. package/dist/{chunk-B67RLIUM.js.map → chunk-72D7G6YQ.js.map} +1 -1
  175. package/dist/{chunk-C65DG2RI.mjs → chunk-7FSBWUOM.mjs} +2 -2
  176. package/dist/{chunk-QWMFWYII.js → chunk-7IZNLARX.js} +3 -3
  177. package/dist/{chunk-QWMFWYII.js.map → chunk-7IZNLARX.js.map} +1 -1
  178. package/dist/{chunk-ZCBT2BUZ.js → chunk-7RFHXFFV.js} +13 -13
  179. package/dist/{chunk-ZCBT2BUZ.js.map → chunk-7RFHXFFV.js.map} +1 -1
  180. package/dist/{chunk-EMWX6IKP.js → chunk-7VJP5D26.js} +13 -13
  181. package/dist/{chunk-EMWX6IKP.js.map → chunk-7VJP5D26.js.map} +1 -1
  182. package/dist/{chunk-HCQJR5CE.mjs → chunk-7ZWEBNFB.mjs} +2 -2
  183. package/dist/{chunk-3RHNJUXU.mjs → chunk-ACSEJOW6.mjs} +2 -2
  184. package/dist/{chunk-WQZHUW7C.mjs → chunk-ANFHLSDB.mjs} +3 -3
  185. package/dist/{chunk-H6NQOLQY.mjs → chunk-ANFLWCVD.mjs} +2 -2
  186. package/dist/{chunk-BKBDQXZX.js → chunk-AOYPUZRO.js} +3 -3
  187. package/dist/{chunk-BKBDQXZX.js.map → chunk-AOYPUZRO.js.map} +1 -1
  188. package/dist/{chunk-5WS6IENQ.mjs → chunk-AU2HLQDJ.mjs} +2 -2
  189. package/dist/{chunk-P6FX3NT6.mjs → chunk-AZGDHAP5.mjs} +2 -2
  190. package/dist/{chunk-NCGO7FPB.js → chunk-AZKNYREH.js} +8 -8
  191. package/dist/{chunk-NCGO7FPB.js.map → chunk-AZKNYREH.js.map} +1 -1
  192. package/dist/{chunk-R6MTPYYW.js → chunk-AZTBNERQ.js} +3 -3
  193. package/dist/{chunk-R6MTPYYW.js.map → chunk-AZTBNERQ.js.map} +1 -1
  194. package/dist/{chunk-NPB2JTYC.mjs → chunk-BO3QVQ37.mjs} +4 -4
  195. package/dist/{chunk-2WI4Q2I2.mjs → chunk-BPMWRFSI.mjs} +4 -4
  196. package/dist/{chunk-TEW33GLR.mjs → chunk-BXN3VO6I.mjs} +2 -2
  197. package/dist/{chunk-7ZB3YHF2.mjs → chunk-C6LC5LD5.mjs} +1 -1
  198. package/dist/{chunk-7ZB3YHF2.mjs.map → chunk-C6LC5LD5.mjs.map} +1 -1
  199. package/dist/{chunk-5HONWYQA.js → chunk-C7ILTLT5.js} +3 -3
  200. package/dist/{chunk-5HONWYQA.js.map → chunk-C7ILTLT5.js.map} +1 -1
  201. package/dist/{chunk-OES54VU6.mjs → chunk-CFV2OZ3P.mjs} +116 -13
  202. package/dist/chunk-CFV2OZ3P.mjs.map +1 -0
  203. package/dist/{chunk-4OVIHZ3I.js → chunk-CGXWWPEA.js} +6 -6
  204. package/dist/{chunk-4OVIHZ3I.js.map → chunk-CGXWWPEA.js.map} +1 -1
  205. package/dist/{chunk-MNU45FM3.js → chunk-COT4KW3H.js} +2 -2
  206. package/dist/{chunk-MNU45FM3.js.map → chunk-COT4KW3H.js.map} +1 -1
  207. package/dist/{chunk-JDQUXG75.js → chunk-CVDNCU6L.js} +3 -3
  208. package/dist/{chunk-JDQUXG75.js.map → chunk-CVDNCU6L.js.map} +1 -1
  209. package/dist/{chunk-HBZHWSMZ.mjs → chunk-D46PJSQ7.mjs} +13 -13
  210. package/dist/{chunk-ARYIJ4RU.js → chunk-DDORKDQO.js} +3 -3
  211. package/dist/{chunk-ARYIJ4RU.js.map → chunk-DDORKDQO.js.map} +1 -1
  212. package/dist/{chunk-7YQTDJFA.js → chunk-DKXSR2V3.js} +3 -3
  213. package/dist/{chunk-7YQTDJFA.js.map → chunk-DKXSR2V3.js.map} +1 -1
  214. package/dist/{chunk-I6EAPPBL.mjs → chunk-DZMYIOKZ.mjs} +5 -5
  215. package/dist/{chunk-SKM7NFPY.js → chunk-EPXAFJVW.js} +6 -6
  216. package/dist/{chunk-SKM7NFPY.js.map → chunk-EPXAFJVW.js.map} +1 -1
  217. package/dist/{chunk-AYYMS23L.js → chunk-ESIQ2GB4.js} +7 -7
  218. package/dist/{chunk-AYYMS23L.js.map → chunk-ESIQ2GB4.js.map} +1 -1
  219. package/dist/{chunk-NU2WNYOD.js → chunk-EW7HGUFV.js} +87 -87
  220. package/dist/{chunk-NU2WNYOD.js.map → chunk-EW7HGUFV.js.map} +1 -1
  221. package/dist/{chunk-I4QGQMXI.mjs → chunk-EXYXZFNK.mjs} +2 -2
  222. package/dist/{chunk-RBJAO57A.js → chunk-FAAR4XC5.js} +3 -3
  223. package/dist/{chunk-RBJAO57A.js.map → chunk-FAAR4XC5.js.map} +1 -1
  224. package/dist/{chunk-N2VNE3ON.js → chunk-FDTDJDZI.js} +13 -13
  225. package/dist/{chunk-N2VNE3ON.js.map → chunk-FDTDJDZI.js.map} +1 -1
  226. package/dist/{chunk-SU5Q64TW.mjs → chunk-FVCTKS33.mjs} +6 -5
  227. package/dist/chunk-FVCTKS33.mjs.map +1 -0
  228. package/dist/{chunk-AVHZHWI3.js → chunk-GBIFSFWJ.js} +15 -15
  229. package/dist/{chunk-AVHZHWI3.js.map → chunk-GBIFSFWJ.js.map} +1 -1
  230. package/dist/{chunk-KSUPPAKH.js → chunk-GIQSHTB5.js} +9 -9
  231. package/dist/{chunk-KSUPPAKH.js.map → chunk-GIQSHTB5.js.map} +1 -1
  232. package/dist/{chunk-4QBKD4JN.js → chunk-GUUJYGXF.js} +3 -3
  233. package/dist/{chunk-4QBKD4JN.js.map → chunk-GUUJYGXF.js.map} +1 -1
  234. package/dist/{chunk-M4O46VDY.mjs → chunk-GX2PKLN2.mjs} +2 -2
  235. package/dist/{chunk-5BUTINIM.mjs → chunk-HLVLSWKD.mjs} +3 -3
  236. package/dist/{chunk-FU23WOTY.js → chunk-HSV5VZ4Z.js} +6 -6
  237. package/dist/{chunk-FU23WOTY.js.map → chunk-HSV5VZ4Z.js.map} +1 -1
  238. package/dist/{chunk-MMS2ELDK.mjs → chunk-IIQCS3A2.mjs} +2 -2
  239. package/dist/{chunk-NKWLCZUZ.mjs → chunk-JGGUMHM6.mjs} +2 -2
  240. package/dist/{chunk-HT2GREFH.js → chunk-JLD46DL7.js} +11 -11
  241. package/dist/{chunk-HT2GREFH.js.map → chunk-JLD46DL7.js.map} +1 -1
  242. package/dist/{chunk-NVL5TKWD.mjs → chunk-JYFLOQYT.mjs} +2 -2
  243. package/dist/{chunk-DWGDEUEW.mjs → chunk-KG563P7F.mjs} +5 -5
  244. package/dist/{chunk-TCIC5556.mjs → chunk-KH4TEDGD.mjs} +2 -2
  245. package/dist/{chunk-23PAYKWB.js → chunk-KM27ZDHZ.js} +2 -2
  246. package/dist/{chunk-23PAYKWB.js.map → chunk-KM27ZDHZ.js.map} +1 -1
  247. package/dist/{chunk-XZUMD3CB.mjs → chunk-LODDYDTP.mjs} +2 -2
  248. package/dist/{chunk-XZUMD3CB.mjs.map → chunk-LODDYDTP.mjs.map} +1 -1
  249. package/dist/{chunk-E6CGJBID.mjs → chunk-LUHKMY56.mjs} +2 -2
  250. package/dist/{chunk-TVLEUNKE.mjs → chunk-LZP5VZCO.mjs} +2 -2
  251. package/dist/{chunk-EHNEBXS7.mjs → chunk-M6DXZ2FM.mjs} +2 -2
  252. package/dist/{chunk-B4C5DWAP.mjs → chunk-MMMJDO75.mjs} +2 -2
  253. package/dist/{chunk-AFCWMJQO.js → chunk-MOUPNP53.js} +3 -3
  254. package/dist/{chunk-AFCWMJQO.js.map → chunk-MOUPNP53.js.map} +1 -1
  255. package/dist/{chunk-36KFIZ43.js → chunk-MYPUZ6EG.js} +3 -3
  256. package/dist/{chunk-36KFIZ43.js.map → chunk-MYPUZ6EG.js.map} +1 -1
  257. package/dist/{chunk-6JHIKIPA.js → chunk-NUGLTWEA.js} +23 -23
  258. package/dist/{chunk-6JHIKIPA.js.map → chunk-NUGLTWEA.js.map} +1 -1
  259. package/dist/{chunk-KWXOYHYK.mjs → chunk-P7MORXSE.mjs} +2 -2
  260. package/dist/{chunk-YG4IZ5Q3.mjs → chunk-Q2OSEROT.mjs} +2 -2
  261. package/dist/{chunk-L5SXG6T6.mjs → chunk-QQKOHKO2.mjs} +3 -3
  262. package/dist/{chunk-SADSJ2MV.js → chunk-QVGUYPXO.js} +3 -3
  263. package/dist/{chunk-SADSJ2MV.js.map → chunk-QVGUYPXO.js.map} +1 -1
  264. package/dist/{chunk-K45PTE7F.mjs → chunk-RBKQXT73.mjs} +4 -4
  265. package/dist/{chunk-JZFISWTP.js → chunk-RIN2IN7E.js} +2 -2
  266. package/dist/{chunk-JZFISWTP.js.map → chunk-RIN2IN7E.js.map} +1 -1
  267. package/dist/{chunk-BXLAOYAG.mjs → chunk-RIUVPTG7.mjs} +2 -2
  268. package/dist/{chunk-QVB54LBS.mjs → chunk-RNZGX6MX.mjs} +3 -3
  269. package/dist/{chunk-VSVLIRO2.js → chunk-RXEU77VN.js} +15 -15
  270. package/dist/{chunk-VSVLIRO2.js.map → chunk-RXEU77VN.js.map} +1 -1
  271. package/dist/{chunk-ZRIYCU2V.js → chunk-S2BZPPAI.js} +5 -5
  272. package/dist/{chunk-ZRIYCU2V.js.map → chunk-S2BZPPAI.js.map} +1 -1
  273. package/dist/{chunk-MES2BP4T.mjs → chunk-SGL5IUCV.mjs} +2 -2
  274. package/dist/{chunk-7LS5UQCQ.mjs → chunk-STKUPDHG.mjs} +2 -2
  275. package/dist/{chunk-PPYCCT2K.js → chunk-SWDVSCQJ.js} +9 -8
  276. package/dist/chunk-SWDVSCQJ.js.map +1 -0
  277. package/dist/{chunk-JCA76TCX.mjs → chunk-SZKLJOTN.mjs} +2 -2
  278. package/dist/{chunk-F5WR6XZZ.js → chunk-T73HPYF2.js} +3 -3
  279. package/dist/{chunk-F5WR6XZZ.js.map → chunk-T73HPYF2.js.map} +1 -1
  280. package/dist/{chunk-ZB6HDDE4.js → chunk-TQO37PVW.js} +14 -14
  281. package/dist/{chunk-ZB6HDDE4.js.map → chunk-TQO37PVW.js.map} +1 -1
  282. package/dist/{chunk-VKQ5AJDT.mjs → chunk-UENZQF2Y.mjs} +2 -2
  283. package/dist/{chunk-DCG7EUBC.js → chunk-UWLW5YVB.js} +6 -6
  284. package/dist/{chunk-DCG7EUBC.js.map → chunk-UWLW5YVB.js.map} +1 -1
  285. package/dist/{chunk-37AJ2GVO.mjs → chunk-UXJIZDXQ.mjs} +2 -2
  286. package/dist/{chunk-PLLPFCN3.js → chunk-UXMDPRAI.js} +14 -14
  287. package/dist/{chunk-PLLPFCN3.js.map → chunk-UXMDPRAI.js.map} +1 -1
  288. package/dist/{chunk-7BOTKRY6.mjs → chunk-V55DG5B4.mjs} +2 -2
  289. package/dist/{chunk-YRTIDZTN.js → chunk-VHEDMUAW.js} +4 -4
  290. package/dist/{chunk-YRTIDZTN.js.map → chunk-VHEDMUAW.js.map} +1 -1
  291. package/dist/{chunk-4RD55E4R.js → chunk-VWLWSO4J.js} +3 -3
  292. package/dist/{chunk-4RD55E4R.js.map → chunk-VWLWSO4J.js.map} +1 -1
  293. package/dist/{chunk-VJQIKFMG.mjs → chunk-W3CWEZDB.mjs} +3 -3
  294. package/dist/{chunk-JM6SLU7D.js → chunk-W7NNH4VY.js} +6 -6
  295. package/dist/{chunk-JM6SLU7D.js.map → chunk-W7NNH4VY.js.map} +1 -1
  296. package/dist/{chunk-VQ7MSYIH.js → chunk-WUN5NHZE.js} +4 -4
  297. package/dist/{chunk-VQ7MSYIH.js.map → chunk-WUN5NHZE.js.map} +1 -1
  298. package/dist/{chunk-AAU7ABJN.js → chunk-WWTRSQ3X.js} +3 -3
  299. package/dist/{chunk-AAU7ABJN.js.map → chunk-WWTRSQ3X.js.map} +1 -1
  300. package/dist/{chunk-BR6D2TGF.js → chunk-WZOA2PVJ.js} +3 -3
  301. package/dist/{chunk-BR6D2TGF.js.map → chunk-WZOA2PVJ.js.map} +1 -1
  302. package/dist/{chunk-TZKR5Z4J.mjs → chunk-XJF2F36N.mjs} +2 -2
  303. package/dist/{chunk-P6ACVNQK.mjs → chunk-YD2XBEKO.mjs} +2 -2
  304. package/dist/{chunk-LMXWIPGR.js → chunk-YWPLHFAK.js} +3 -3
  305. package/dist/{chunk-LMXWIPGR.js.map → chunk-YWPLHFAK.js.map} +1 -1
  306. package/dist/{chunk-DCEWWHV5.mjs → chunk-Z3HGYPH7.mjs} +2 -2
  307. package/dist/{chunk-Q3PKW6TH.js → chunk-ZCV3HWI2.js} +6 -6
  308. package/dist/{chunk-Q3PKW6TH.js.map → chunk-ZCV3HWI2.js.map} +1 -1
  309. package/dist/{chunk-OSA3YVDZ.mjs → chunk-ZF46DCAC.mjs} +2 -2
  310. package/dist/{chunk-5T6IDXSB.js → chunk-ZO2GA23I.js} +3 -3
  311. package/dist/{chunk-5T6IDXSB.js.map → chunk-ZO2GA23I.js.map} +1 -1
  312. package/dist/{chunk-ZN3763VZ.js → chunk-ZQLW4EJB.js} +9 -9
  313. package/dist/{chunk-ZN3763VZ.js.map → chunk-ZQLW4EJB.js.map} +1 -1
  314. package/dist/{chunk-CM5R4SPF.js → chunk-ZQQJTO6T.js} +3 -3
  315. package/dist/{chunk-CM5R4SPF.js.map → chunk-ZQQJTO6T.js.map} +1 -1
  316. package/dist/{chunk-SEL4QDCO.js → chunk-ZSXSAE3D.js} +3 -3
  317. package/dist/{chunk-SEL4QDCO.js.map → chunk-ZSXSAE3D.js.map} +1 -1
  318. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts +6 -1
  319. package/dist/components/SupportChatWidget/guestSupportChatAdapter.d.ts.map +1 -1
  320. package/dist/hooks/useAppContent/index.js +49 -49
  321. package/dist/hooks/useAppContent/index.mjs +48 -48
  322. package/dist/hooks/useSupportChat.d.ts +2 -0
  323. package/dist/hooks/useSupportChat.d.ts.map +1 -1
  324. package/dist/index.js +49 -49
  325. package/dist/index.mjs +48 -48
  326. package/dist/types/supportChat/index.d.ts +14 -0
  327. package/dist/types/supportChat/index.d.ts.map +1 -1
  328. package/dist/types/supportChat/index.js +2 -2
  329. package/dist/types/supportChat/index.mjs +1 -1
  330. package/dist/types/supportChat.d.ts +14 -0
  331. package/dist/types/supportChat.d.ts.map +1 -1
  332. package/package.json +1 -1
  333. package/dist/chunk-CYSODZGX.js.map +0 -1
  334. package/dist/chunk-OES54VU6.mjs.map +0 -1
  335. package/dist/chunk-PPYCCT2K.js.map +0 -1
  336. package/dist/chunk-SU5Q64TW.mjs.map +0 -1
  337. /package/dist/{RichEditorCore-6MTYSSKS.mjs.map → RichEditorCore-2S5YUAOI.mjs.map} +0 -0
  338. /package/dist/{chunk-B6QOBHZM.mjs.map → chunk-24BEZK43.mjs.map} +0 -0
  339. /package/dist/{chunk-OQTXTDSK.mjs.map → chunk-2OJ3QSLU.mjs.map} +0 -0
  340. /package/dist/{chunk-4OUBPEVS.mjs.map → chunk-3HKLGNUE.mjs.map} +0 -0
  341. /package/dist/{chunk-IHBWJM4C.mjs.map → chunk-3Q7RJKUP.mjs.map} +0 -0
  342. /package/dist/{chunk-PY6WD6SB.mjs.map → chunk-3WRB4WCD.mjs.map} +0 -0
  343. /package/dist/{chunk-L52NFNFK.mjs.map → chunk-5VC4BK65.mjs.map} +0 -0
  344. /package/dist/{chunk-C65DG2RI.mjs.map → chunk-7FSBWUOM.mjs.map} +0 -0
  345. /package/dist/{chunk-HCQJR5CE.mjs.map → chunk-7ZWEBNFB.mjs.map} +0 -0
  346. /package/dist/{chunk-3RHNJUXU.mjs.map → chunk-ACSEJOW6.mjs.map} +0 -0
  347. /package/dist/{chunk-WQZHUW7C.mjs.map → chunk-ANFHLSDB.mjs.map} +0 -0
  348. /package/dist/{chunk-H6NQOLQY.mjs.map → chunk-ANFLWCVD.mjs.map} +0 -0
  349. /package/dist/{chunk-5WS6IENQ.mjs.map → chunk-AU2HLQDJ.mjs.map} +0 -0
  350. /package/dist/{chunk-P6FX3NT6.mjs.map → chunk-AZGDHAP5.mjs.map} +0 -0
  351. /package/dist/{chunk-NPB2JTYC.mjs.map → chunk-BO3QVQ37.mjs.map} +0 -0
  352. /package/dist/{chunk-2WI4Q2I2.mjs.map → chunk-BPMWRFSI.mjs.map} +0 -0
  353. /package/dist/{chunk-TEW33GLR.mjs.map → chunk-BXN3VO6I.mjs.map} +0 -0
  354. /package/dist/{chunk-HBZHWSMZ.mjs.map → chunk-D46PJSQ7.mjs.map} +0 -0
  355. /package/dist/{chunk-I6EAPPBL.mjs.map → chunk-DZMYIOKZ.mjs.map} +0 -0
  356. /package/dist/{chunk-I4QGQMXI.mjs.map → chunk-EXYXZFNK.mjs.map} +0 -0
  357. /package/dist/{chunk-M4O46VDY.mjs.map → chunk-GX2PKLN2.mjs.map} +0 -0
  358. /package/dist/{chunk-5BUTINIM.mjs.map → chunk-HLVLSWKD.mjs.map} +0 -0
  359. /package/dist/{chunk-MMS2ELDK.mjs.map → chunk-IIQCS3A2.mjs.map} +0 -0
  360. /package/dist/{chunk-NKWLCZUZ.mjs.map → chunk-JGGUMHM6.mjs.map} +0 -0
  361. /package/dist/{chunk-NVL5TKWD.mjs.map → chunk-JYFLOQYT.mjs.map} +0 -0
  362. /package/dist/{chunk-DWGDEUEW.mjs.map → chunk-KG563P7F.mjs.map} +0 -0
  363. /package/dist/{chunk-TCIC5556.mjs.map → chunk-KH4TEDGD.mjs.map} +0 -0
  364. /package/dist/{chunk-E6CGJBID.mjs.map → chunk-LUHKMY56.mjs.map} +0 -0
  365. /package/dist/{chunk-TVLEUNKE.mjs.map → chunk-LZP5VZCO.mjs.map} +0 -0
  366. /package/dist/{chunk-EHNEBXS7.mjs.map → chunk-M6DXZ2FM.mjs.map} +0 -0
  367. /package/dist/{chunk-B4C5DWAP.mjs.map → chunk-MMMJDO75.mjs.map} +0 -0
  368. /package/dist/{chunk-KWXOYHYK.mjs.map → chunk-P7MORXSE.mjs.map} +0 -0
  369. /package/dist/{chunk-YG4IZ5Q3.mjs.map → chunk-Q2OSEROT.mjs.map} +0 -0
  370. /package/dist/{chunk-L5SXG6T6.mjs.map → chunk-QQKOHKO2.mjs.map} +0 -0
  371. /package/dist/{chunk-K45PTE7F.mjs.map → chunk-RBKQXT73.mjs.map} +0 -0
  372. /package/dist/{chunk-BXLAOYAG.mjs.map → chunk-RIUVPTG7.mjs.map} +0 -0
  373. /package/dist/{chunk-QVB54LBS.mjs.map → chunk-RNZGX6MX.mjs.map} +0 -0
  374. /package/dist/{chunk-MES2BP4T.mjs.map → chunk-SGL5IUCV.mjs.map} +0 -0
  375. /package/dist/{chunk-7LS5UQCQ.mjs.map → chunk-STKUPDHG.mjs.map} +0 -0
  376. /package/dist/{chunk-JCA76TCX.mjs.map → chunk-SZKLJOTN.mjs.map} +0 -0
  377. /package/dist/{chunk-VKQ5AJDT.mjs.map → chunk-UENZQF2Y.mjs.map} +0 -0
  378. /package/dist/{chunk-37AJ2GVO.mjs.map → chunk-UXJIZDXQ.mjs.map} +0 -0
  379. /package/dist/{chunk-7BOTKRY6.mjs.map → chunk-V55DG5B4.mjs.map} +0 -0
  380. /package/dist/{chunk-VJQIKFMG.mjs.map → chunk-W3CWEZDB.mjs.map} +0 -0
  381. /package/dist/{chunk-TZKR5Z4J.mjs.map → chunk-XJF2F36N.mjs.map} +0 -0
  382. /package/dist/{chunk-P6ACVNQK.mjs.map → chunk-YD2XBEKO.mjs.map} +0 -0
  383. /package/dist/{chunk-DCEWWHV5.mjs.map → chunk-Z3HGYPH7.mjs.map} +0 -0
  384. /package/dist/{chunk-OSA3YVDZ.mjs.map → chunk-ZF46DCAC.mjs.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-JM6SLU7D.js","../src/components/RichEditor/components/MathNode.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-JM6SLU7D.js","sourcesContent":[null,"import { InputRule, Node, mergeAttributes } from '@tiptap/core';\nimport { Plugin, PluginKey } from '@tiptap/pm/state';\nimport { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react';\nimport type { NodeViewProps } from '@tiptap/react';\nimport katex from 'katex';\nimport { useMemo } from 'react';\nimport {\n BLOCK_INPUT_RULE,\n INLINE_INPUT_RULE,\n MATH_DISPLAY_ATTR,\n MATH_SPAN_TYPE,\n isCurrencyAmount,\n looksLikeMath,\n} from '../../../utils/latexMath';\nimport { normalizeLineBreaksInHtml } from '../../../utils/htmlLineBreaks';\nimport { escapeHtmlText, processLatexInHtml } from './utils';\n\ninterface MathNodeViewProps {\n readonly node: NodeViewProps['node'];\n readonly editor: NodeViewProps['editor'];\n readonly getPos: NodeViewProps['getPos'];\n}\n\n/**\n * Classes applied to a display formula. They mirror the wrapper\n * `HtmlMathRenderer` uses for block math so the line break the author sees in\n * the editor lands in exactly the same place for the student.\n */\nconst DISPLAY_CLASS = 'block w-full text-center my-2.5';\nconst INLINE_CLASS = 'inline-math';\n\nfunction MathNodeView({ node, editor, getPos }: MathNodeViewProps) {\n // `node.attrs.latex` is not guaranteed to be a string: pasted/imported HTML\n // without a `data-latex` attribute makes parseHTML return `undefined`, which\n // bypasses the attribute's `default: ''`. Normalising here keeps both the\n // KaTeX render and the cursor math below working on a real string.\n const latex = typeof node.attrs.latex === 'string' ? node.attrs.latex : '';\n const display = node.attrs.display === true;\n\n const renderedHtml = useMemo(() => {\n try {\n return katex.renderToString(latex, {\n throwOnError: false,\n displayMode: display,\n });\n } catch {\n // `latex` comes from the `data-latex` of pasted/persisted spans and is\n // about to reach dangerouslySetInnerHTML, so it must be escaped — unlike\n // the KaTeX output above, which is markup KaTeX generates itself.\n return `<span class=\"text-error-600\">${escapeHtmlText(latex)}</span>`;\n }\n }, [latex, display]);\n\n // When clicked, convert back to editable text\n const handleClick = () => {\n const pos = getPos();\n // `typeof NaN === 'number'`, so an explicit finite check is required here —\n // a NaN position would flow into setTextSelection and make ProseMirror throw\n // \"Position NaN out of range\" (FRONTEND-BACKOFFICE-WEB-P).\n if (typeof pos !== 'number' || !Number.isFinite(pos)) return;\n\n const delimiter = display ? '$$' : '$';\n\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .insertContent(`${delimiter}${latex}${delimiter}`)\n .run();\n\n // Move cursor to the end of the inserted text (before the closing\n // delimiter). Clamp to the document bounds: the position is derived from\n // `pos`, which predates the edits above, so it can land past the end of the\n // resulting document — another way ProseMirror would throw \"out of range\".\n const docSize = editor.state.doc.content.size;\n const newPos = Math.min(\n Math.max(pos + latex.length + delimiter.length, 0),\n docSize\n );\n editor.commands.setTextSelection(newPos);\n };\n\n return (\n <NodeViewWrapper\n as=\"span\"\n className={`${display ? DISPLAY_CLASS : INLINE_CLASS} cursor-pointer hover:bg-primary-50 rounded px-0.5 transition-colors`}\n onClick={handleClick}\n title=\"Clique para editar\"\n >\n <span\n dangerouslySetInnerHTML={{ __html: renderedHtml }}\n className={display ? 'block' : 'inline'}\n />\n </NodeViewWrapper>\n );\n}\n\n/**\n * Reads the two characters that precede `from` in the document, so the currency\n * guard can tell `R$1,00` from a formula that merely starts with a digit.\n */\nconst textBeforeRange = (\n doc: { textBetween: (from: number, to: number, sep?: string) => string },\n from: number\n): string => doc.textBetween(Math.max(0, from - 2), Math.max(0, from), '');\n\nexport const MathNode = Node.create({\n name: 'mathInline',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: true,\n\n addAttributes() {\n // Both attributes are serialized explicitly by `renderHTML` below. Without\n // `renderHTML: () => ({})` TipTap ALSO emits them under their bare names\n // (`latex=\"...\"`, `display=\"false\"`), which leaks non-canonical attributes\n // into every saved question.\n return {\n latex: {\n default: '',\n renderHTML: () => ({}),\n },\n display: {\n default: false,\n renderHTML: () => ({}),\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: `span[data-type=\"${MATH_SPAN_TYPE}\"]`,\n getAttrs: (dom) => {\n // Fall back to '' instead of forwarding `undefined`: returning the\n // attribute explicitly overrides the `default: ''` declared above, so\n // HTML pasted without `data-latex` would otherwise produce a node\n // whose `latex` attr is undefined.\n return {\n latex: dom.dataset.latex ?? '',\n display: dom.dataset.displayMode === 'true',\n };\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n // The display flag is only emitted when set: keeping the serialized markup\n // minimal means inline formulas produce byte-identical HTML to before.\n const displayAttributes = node.attrs.display\n ? { [MATH_DISPLAY_ATTR]: 'true' }\n : {};\n\n return [\n 'span',\n mergeAttributes(HTMLAttributes, {\n 'data-type': MATH_SPAN_TYPE,\n ...displayAttributes,\n 'data-latex': node.attrs.latex,\n }),\n ];\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(MathNodeView);\n },\n\n /**\n * Converts `$formula$` / `$$formula$$` the moment the closing delimiter is\n * typed. Previously the only trigger was the Space shortcut below, which is\n * why authors had to type a space after the last `$` and why a formula that\n * ended a sentence stayed raw text forever.\n */\n addInputRules() {\n const { type } = this;\n\n const buildRule = (find: RegExp, display: boolean) =>\n new InputRule({\n find,\n handler: ({ state, range, match }) => {\n const latex = match[1];\n if (!latex?.trim()) return;\n\n // Inline math is the ambiguous case: `R$1,00 e de R$0,50` closes a\n // perfectly valid `$...$` pair across two prices.\n if (!display) {\n const before = textBeforeRange(state.doc, range.from);\n if (isCurrencyAmount(before, latex) || !looksLikeMath(latex)) {\n return;\n }\n }\n\n state.tr.replaceRangeWith(\n range.from,\n range.to,\n type.create({ latex, display })\n );\n },\n });\n\n // `$$` first: the inline rule must never claim the inner `$formula$` of a\n // display block.\n return [\n buildRule(BLOCK_INPUT_RULE, true),\n buildRule(INLINE_INPUT_RULE, false),\n ];\n },\n\n /**\n * Pasted content never runs through input rules, so a formula copied from a\n * document or from the KaTeX playground used to land as raw text. Both\n * clipboard flavours are converted here.\n */\n addProseMirrorPlugins() {\n const { editor } = this;\n\n return [\n new Plugin({\n key: new PluginKey('mathInlinePaste'),\n props: {\n transformPastedHTML: (html) => processLatexInHtml(html),\n handlePaste: (_view, event) => {\n const clipboard = event.clipboardData;\n // HTML payloads are already covered by transformPastedHTML above.\n if (!clipboard || clipboard.getData('text/html')) return false;\n\n const text = clipboard.getData('text/plain');\n if (\n !text ||\n !(text.includes('$') || text.includes(String.raw`\\begin{`))\n ) {\n return false;\n }\n\n const html = processLatexInHtml(\n normalizeLineBreaksInHtml(escapeHtmlText(text), { inline: true })\n );\n if (!html.includes(`data-type=\"${MATH_SPAN_TYPE}\"`)) return false;\n\n editor.commands.insertContent(html);\n return true;\n },\n },\n }),\n ];\n },\n\n addKeyboardShortcuts() {\n return {\n // Kept for authors used to the old flow (and for `$...$` typed before a\n // paste re-flowed the text): space after a closing delimiter converts.\n Space: ({ editor }) => {\n const { state } = editor;\n const { selection } = state;\n const { $from } = selection;\n\n // Get text before cursor\n const textBefore = $from.parent.textBetween(\n Math.max(0, $from.parentOffset - 200),\n $from.parentOffset,\n '\\n'\n );\n\n // `$$...$$` is tested first so a display block is not split in half.\n const blockMatch = BLOCK_INPUT_RULE.exec(textBefore);\n const inlineMatch = blockMatch\n ? null\n : INLINE_INPUT_RULE.exec(textBefore);\n const match = blockMatch ?? inlineMatch;\n if (!match?.[1]?.trim()) return false;\n\n const latex = match[1];\n const display = blockMatch !== null;\n const matchStart = $from.pos - match[0].length;\n\n if (!display) {\n const before = textBeforeRange(state.doc, matchStart);\n if (isCurrencyAmount(before, latex) || !looksLikeMath(latex)) {\n return false;\n }\n }\n\n // Replace the delimited text with a math node, then add a space\n editor\n .chain()\n .deleteRange({ from: matchStart, to: $from.pos })\n .insertContent([\n { type: 'mathInline', attrs: { latex, display } },\n { type: 'text', text: ' ' },\n ])\n .run();\n\n return true;\n },\n\n // When backspace is pressed on a math node, convert it back to text for editing\n Backspace: ({ editor }) => {\n const { selection } = editor.state;\n const { $from } = selection;\n\n // Check if we're right after a math node\n const nodeBefore = $from.nodeBefore;\n if (nodeBefore?.type.name === 'mathInline') {\n const pos = $from.pos - nodeBefore.nodeSize;\n const latex = nodeBefore.attrs.latex;\n const delimiter = nodeBefore.attrs.display ? '$$' : '$';\n\n // Convert back to text without the closing delimiter, so the user can\n // continue editing\n editor\n .chain()\n .deleteRange({ from: pos, to: $from.pos })\n .insertContent(`${delimiter}${latex}`)\n .run();\n\n return true;\n }\n\n return false;\n },\n };\n },\n});\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-W7NNH4VY.js","../src/components/RichEditor/components/MathNode.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-W7NNH4VY.js","sourcesContent":[null,"import { InputRule, Node, mergeAttributes } from '@tiptap/core';\nimport { Plugin, PluginKey } from '@tiptap/pm/state';\nimport { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react';\nimport type { NodeViewProps } from '@tiptap/react';\nimport katex from 'katex';\nimport { useMemo } from 'react';\nimport {\n BLOCK_INPUT_RULE,\n INLINE_INPUT_RULE,\n MATH_DISPLAY_ATTR,\n MATH_SPAN_TYPE,\n isCurrencyAmount,\n looksLikeMath,\n} from '../../../utils/latexMath';\nimport { normalizeLineBreaksInHtml } from '../../../utils/htmlLineBreaks';\nimport { escapeHtmlText, processLatexInHtml } from './utils';\n\ninterface MathNodeViewProps {\n readonly node: NodeViewProps['node'];\n readonly editor: NodeViewProps['editor'];\n readonly getPos: NodeViewProps['getPos'];\n}\n\n/**\n * Classes applied to a display formula. They mirror the wrapper\n * `HtmlMathRenderer` uses for block math so the line break the author sees in\n * the editor lands in exactly the same place for the student.\n */\nconst DISPLAY_CLASS = 'block w-full text-center my-2.5';\nconst INLINE_CLASS = 'inline-math';\n\nfunction MathNodeView({ node, editor, getPos }: MathNodeViewProps) {\n // `node.attrs.latex` is not guaranteed to be a string: pasted/imported HTML\n // without a `data-latex` attribute makes parseHTML return `undefined`, which\n // bypasses the attribute's `default: ''`. Normalising here keeps both the\n // KaTeX render and the cursor math below working on a real string.\n const latex = typeof node.attrs.latex === 'string' ? node.attrs.latex : '';\n const display = node.attrs.display === true;\n\n const renderedHtml = useMemo(() => {\n try {\n return katex.renderToString(latex, {\n throwOnError: false,\n displayMode: display,\n });\n } catch {\n // `latex` comes from the `data-latex` of pasted/persisted spans and is\n // about to reach dangerouslySetInnerHTML, so it must be escaped — unlike\n // the KaTeX output above, which is markup KaTeX generates itself.\n return `<span class=\"text-error-600\">${escapeHtmlText(latex)}</span>`;\n }\n }, [latex, display]);\n\n // When clicked, convert back to editable text\n const handleClick = () => {\n const pos = getPos();\n // `typeof NaN === 'number'`, so an explicit finite check is required here —\n // a NaN position would flow into setTextSelection and make ProseMirror throw\n // \"Position NaN out of range\" (FRONTEND-BACKOFFICE-WEB-P).\n if (typeof pos !== 'number' || !Number.isFinite(pos)) return;\n\n const delimiter = display ? '$$' : '$';\n\n editor\n .chain()\n .focus()\n .deleteRange({ from: pos, to: pos + node.nodeSize })\n .insertContent(`${delimiter}${latex}${delimiter}`)\n .run();\n\n // Move cursor to the end of the inserted text (before the closing\n // delimiter). Clamp to the document bounds: the position is derived from\n // `pos`, which predates the edits above, so it can land past the end of the\n // resulting document — another way ProseMirror would throw \"out of range\".\n const docSize = editor.state.doc.content.size;\n const newPos = Math.min(\n Math.max(pos + latex.length + delimiter.length, 0),\n docSize\n );\n editor.commands.setTextSelection(newPos);\n };\n\n return (\n <NodeViewWrapper\n as=\"span\"\n className={`${display ? DISPLAY_CLASS : INLINE_CLASS} cursor-pointer hover:bg-primary-50 rounded px-0.5 transition-colors`}\n onClick={handleClick}\n title=\"Clique para editar\"\n >\n <span\n dangerouslySetInnerHTML={{ __html: renderedHtml }}\n className={display ? 'block' : 'inline'}\n />\n </NodeViewWrapper>\n );\n}\n\n/**\n * Reads the two characters that precede `from` in the document, so the currency\n * guard can tell `R$1,00` from a formula that merely starts with a digit.\n */\nconst textBeforeRange = (\n doc: { textBetween: (from: number, to: number, sep?: string) => string },\n from: number\n): string => doc.textBetween(Math.max(0, from - 2), Math.max(0, from), '');\n\nexport const MathNode = Node.create({\n name: 'mathInline',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: true,\n\n addAttributes() {\n // Both attributes are serialized explicitly by `renderHTML` below. Without\n // `renderHTML: () => ({})` TipTap ALSO emits them under their bare names\n // (`latex=\"...\"`, `display=\"false\"`), which leaks non-canonical attributes\n // into every saved question.\n return {\n latex: {\n default: '',\n renderHTML: () => ({}),\n },\n display: {\n default: false,\n renderHTML: () => ({}),\n },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: `span[data-type=\"${MATH_SPAN_TYPE}\"]`,\n getAttrs: (dom) => {\n // Fall back to '' instead of forwarding `undefined`: returning the\n // attribute explicitly overrides the `default: ''` declared above, so\n // HTML pasted without `data-latex` would otherwise produce a node\n // whose `latex` attr is undefined.\n return {\n latex: dom.dataset.latex ?? '',\n display: dom.dataset.displayMode === 'true',\n };\n },\n },\n ];\n },\n\n renderHTML({ node, HTMLAttributes }) {\n // The display flag is only emitted when set: keeping the serialized markup\n // minimal means inline formulas produce byte-identical HTML to before.\n const displayAttributes = node.attrs.display\n ? { [MATH_DISPLAY_ATTR]: 'true' }\n : {};\n\n return [\n 'span',\n mergeAttributes(HTMLAttributes, {\n 'data-type': MATH_SPAN_TYPE,\n ...displayAttributes,\n 'data-latex': node.attrs.latex,\n }),\n ];\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(MathNodeView);\n },\n\n /**\n * Converts `$formula$` / `$$formula$$` the moment the closing delimiter is\n * typed. Previously the only trigger was the Space shortcut below, which is\n * why authors had to type a space after the last `$` and why a formula that\n * ended a sentence stayed raw text forever.\n */\n addInputRules() {\n const { type } = this;\n\n const buildRule = (find: RegExp, display: boolean) =>\n new InputRule({\n find,\n handler: ({ state, range, match }) => {\n const latex = match[1];\n if (!latex?.trim()) return;\n\n // Inline math is the ambiguous case: `R$1,00 e de R$0,50` closes a\n // perfectly valid `$...$` pair across two prices.\n if (!display) {\n const before = textBeforeRange(state.doc, range.from);\n if (isCurrencyAmount(before, latex) || !looksLikeMath(latex)) {\n return;\n }\n }\n\n state.tr.replaceRangeWith(\n range.from,\n range.to,\n type.create({ latex, display })\n );\n },\n });\n\n // `$$` first: the inline rule must never claim the inner `$formula$` of a\n // display block.\n return [\n buildRule(BLOCK_INPUT_RULE, true),\n buildRule(INLINE_INPUT_RULE, false),\n ];\n },\n\n /**\n * Pasted content never runs through input rules, so a formula copied from a\n * document or from the KaTeX playground used to land as raw text. Both\n * clipboard flavours are converted here.\n */\n addProseMirrorPlugins() {\n const { editor } = this;\n\n return [\n new Plugin({\n key: new PluginKey('mathInlinePaste'),\n props: {\n transformPastedHTML: (html) => processLatexInHtml(html),\n handlePaste: (_view, event) => {\n const clipboard = event.clipboardData;\n // HTML payloads are already covered by transformPastedHTML above.\n if (!clipboard || clipboard.getData('text/html')) return false;\n\n const text = clipboard.getData('text/plain');\n if (\n !text ||\n !(text.includes('$') || text.includes(String.raw`\\begin{`))\n ) {\n return false;\n }\n\n const html = processLatexInHtml(\n normalizeLineBreaksInHtml(escapeHtmlText(text), { inline: true })\n );\n if (!html.includes(`data-type=\"${MATH_SPAN_TYPE}\"`)) return false;\n\n editor.commands.insertContent(html);\n return true;\n },\n },\n }),\n ];\n },\n\n addKeyboardShortcuts() {\n return {\n // Kept for authors used to the old flow (and for `$...$` typed before a\n // paste re-flowed the text): space after a closing delimiter converts.\n Space: ({ editor }) => {\n const { state } = editor;\n const { selection } = state;\n const { $from } = selection;\n\n // Get text before cursor\n const textBefore = $from.parent.textBetween(\n Math.max(0, $from.parentOffset - 200),\n $from.parentOffset,\n '\\n'\n );\n\n // `$$...$$` is tested first so a display block is not split in half.\n const blockMatch = BLOCK_INPUT_RULE.exec(textBefore);\n const inlineMatch = blockMatch\n ? null\n : INLINE_INPUT_RULE.exec(textBefore);\n const match = blockMatch ?? inlineMatch;\n if (!match?.[1]?.trim()) return false;\n\n const latex = match[1];\n const display = blockMatch !== null;\n const matchStart = $from.pos - match[0].length;\n\n if (!display) {\n const before = textBeforeRange(state.doc, matchStart);\n if (isCurrencyAmount(before, latex) || !looksLikeMath(latex)) {\n return false;\n }\n }\n\n // Replace the delimited text with a math node, then add a space\n editor\n .chain()\n .deleteRange({ from: matchStart, to: $from.pos })\n .insertContent([\n { type: 'mathInline', attrs: { latex, display } },\n { type: 'text', text: ' ' },\n ])\n .run();\n\n return true;\n },\n\n // When backspace is pressed on a math node, convert it back to text for editing\n Backspace: ({ editor }) => {\n const { selection } = editor.state;\n const { $from } = selection;\n\n // Check if we're right after a math node\n const nodeBefore = $from.nodeBefore;\n if (nodeBefore?.type.name === 'mathInline') {\n const pos = $from.pos - nodeBefore.nodeSize;\n const latex = nodeBefore.attrs.latex;\n const delimiter = nodeBefore.attrs.display ? '$$' : '$';\n\n // Convert back to text without the closing delimiter, so the user can\n // continue editing\n editor\n .chain()\n .deleteRange({ from: pos, to: $from.pos })\n .insertContent(`${delimiter}${latex}`)\n .run();\n\n return true;\n }\n\n return false;\n },\n };\n },\n});\n"]}
@@ -17,7 +17,7 @@ var _chunkJVDEOFXDjs = require('./chunk-JVDEOFXD.js');
17
17
  var _chunkWWXDXIV7js = require('./chunk-WWXDXIV7.js');
18
18
 
19
19
 
20
- var _chunk23PAYKWBjs = require('./chunk-23PAYKWB.js');
20
+ var _chunkKM27ZDHZjs = require('./chunk-KM27ZDHZ.js');
21
21
 
22
22
 
23
23
  var _chunkXN2HPVVHjs = require('./chunk-XN2HPVVH.js');
@@ -136,7 +136,7 @@ var TicketModal = ({
136
136
  }, [isOpen, fetchAnswers]);
137
137
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
138
138
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
139
- _chunk23PAYKWBjs.Modal_default,
139
+ _chunkKM27ZDHZjs.Modal_default,
140
140
  {
141
141
  isOpen,
142
142
  onClose,
@@ -392,7 +392,7 @@ var TicketModal = ({
392
392
  }
393
393
  ),
394
394
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
395
- _chunk23PAYKWBjs.Modal_default,
395
+ _chunkKM27ZDHZjs.Modal_default,
396
396
  {
397
397
  isOpen: showCloseConfirmation,
398
398
  onClose: () => setShowCloseConfirmation(false),
@@ -434,4 +434,4 @@ var TicketModal = ({
434
434
 
435
435
 
436
436
  exports.getCategoryIcon = getCategoryIcon; exports.TicketModal = TicketModal;
437
- //# sourceMappingURL=chunk-VQ7MSYIH.js.map
437
+ //# sourceMappingURL=chunk-WUN5NHZE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-VQ7MSYIH.js","../src/components/Support/components/TicketModal.tsx","../src/components/Support/utils/supportUtils.tsx"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-VQ7MSYIH.js","sourcesContent":[null,"import { useState, useEffect, useCallback } from 'react';\nimport dayjs from 'dayjs';\nimport 'dayjs/locale/pt-br';\nimport Badge from '../../Badge/Badge';\nimport Button from '../../Button/Button';\nimport Divider from '../../Divider/Divider';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport TextArea from '../../TextArea/TextArea';\nimport { SkeletonText } from '../../Skeleton/Skeleton';\nimport {\n SupportTicket,\n SupportStatus,\n SupportAnswerAPI,\n GetSupportAnswersResponse,\n SubmitSupportAnswerRequest,\n SubmitSupportAnswerResponse,\n getStatusBadgeAction,\n getStatusText,\n getCategoryText,\n SupportApiClient,\n} from '../../../types/support';\nimport { getCategoryIcon } from '../utils/supportUtils';\n\ndayjs.locale('pt-br');\n\n// Skeleton component for answer loading\nconst AnswerSkeleton = () => (\n <div className=\"bg-background p-4 space-y-6 rounded-xl\">\n {/* Date skeleton */}\n <div className=\"flex items-center space-x-6\">\n <SkeletonText width=\"80px\" height={16} />\n <SkeletonText width=\"200px\" height={16} />\n </div>\n\n <Divider />\n\n {/* Answer content skeleton */}\n <div className=\"flex items-start space-x-6\">\n <SkeletonText width=\"80px\" height={16} />\n <div className=\"flex-1 space-y-2\">\n <SkeletonText width=\"100%\" height={16} />\n <SkeletonText width=\"80%\" height={16} />\n <SkeletonText width=\"60%\" height={16} />\n </div>\n </div>\n </div>\n);\n\nexport interface TicketModalProps {\n ticket: SupportTicket;\n isOpen: boolean;\n onClose: () => void;\n onTicketClose?: (ticketId: string) => void;\n /** API client instance for making requests */\n apiClient: SupportApiClient;\n /** Current user ID */\n userId?: string;\n}\n\nexport const TicketModal = ({\n ticket,\n isOpen,\n onClose,\n onTicketClose,\n apiClient,\n userId,\n}: TicketModalProps) => {\n const [showCloseConfirmation, setShowCloseConfirmation] = useState(false);\n const [responseText, setResponseText] = useState('');\n const [answers, setAnswers] = useState<SupportAnswerAPI[]>([]);\n const [isSubmittingAnswer, setIsSubmittingAnswer] = useState(false);\n const [isLoadingAnswers, setIsLoadingAnswers] = useState(false);\n\n const handleCloseTicket = () => {\n onTicketClose?.(ticket.id);\n setShowCloseConfirmation(false);\n onClose();\n };\n\n // Fetch support answers\n const fetchAnswers = useCallback(async () => {\n if (!ticket.id || ticket.status !== SupportStatus.RESPONDIDO) return;\n\n setIsLoadingAnswers(true);\n try {\n const response = await apiClient.get<GetSupportAnswersResponse>(\n `/support/answer/${ticket.id}`\n );\n setAnswers(response.data.data || []);\n } catch (error) {\n console.error('Erro ao buscar respostas:', error);\n setAnswers([]);\n } finally {\n setIsLoadingAnswers(false);\n }\n }, [ticket.id, ticket.status, apiClient]);\n\n // Submit user answer\n const handleSubmitAnswer = async () => {\n if (!responseText.trim() || !userId || !ticket.id) {\n return;\n }\n\n setIsSubmittingAnswer(true);\n try {\n const requestData: SubmitSupportAnswerRequest = {\n userId: userId,\n supportId: ticket.id,\n answer: responseText.trim(),\n };\n\n await apiClient.post<SubmitSupportAnswerResponse>(\n '/support/answer',\n requestData\n );\n\n // Clear response text\n setResponseText('');\n\n // Refresh answers list\n await fetchAnswers();\n } catch (error) {\n console.error('Erro ao enviar resposta:', error);\n } finally {\n setIsSubmittingAnswer(false);\n }\n };\n\n const canCloseTicket = ticket.status !== SupportStatus.ENCERRADO;\n\n // Limpar o texto e carregar respostas quando o modal for aberto\n useEffect(() => {\n if (isOpen) {\n setResponseText('');\n (async () => {\n await fetchAnswers();\n })().catch((error) => {\n console.error('Erro ao carregar respostas:', error);\n });\n } else {\n setAnswers([]);\n }\n }, [isOpen, fetchAnswers]);\n\n return (\n <>\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={`Pedido: ${ticket.title}`}\n size=\"lg\"\n hideCloseButton={false}\n closeOnEscape={true}\n data-testid=\"ticket-modal\"\n >\n <div className=\"flex flex-col h-full max-h-[80vh]\">\n {/* Header com botão Encerrar Pedido */}\n <div className=\"flex justify-between items-center mb-3\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n Detalhes\n </Text>\n {canCloseTicket && (\n <Button\n variant=\"outline\"\n size=\"small\"\n action=\"negative\"\n onClick={() => setShowCloseConfirmation(true)}\n >\n Encerrar Pedido\n </Button>\n )}\n </div>\n\n {/* Conteúdo com scroll */}\n <div className=\"flex-1 overflow-y-auto pr-2 space-y-6\">\n <div className=\"bg-background p-4 space-y-6 rounded-xl\">\n {/* ID */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n ID\n </Text>\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {ticket.id}\n </Text>\n </div>\n\n {/* Aberto em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Aberto em\n </Text>\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {dayjs(ticket.createdAt).format('DD MMMM YYYY, [às] HH[h]')}\n </Text>\n </div>\n\n {/* Status */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Status\n </Text>\n <Badge\n variant=\"solid\"\n size=\"small\"\n action={getStatusBadgeAction(ticket.status)}\n className=\"w-fit\"\n >\n {getStatusText(ticket.status)}\n </Badge>\n </div>\n\n {/* Tipo */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Tipo\n </Text>\n <Badge\n variant=\"solid\"\n size=\"small\"\n action=\"muted\"\n className=\"w-fit\"\n >\n {getCategoryIcon(ticket.category)}\n {getCategoryText(ticket.category)}\n </Badge>\n </div>\n\n <Divider />\n\n {/* Descrição */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Descrição\n </Text>\n {ticket.description && (\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {ticket.description}\n </Text>\n )}\n </div>\n </div>\n\n {/* Seção de resposta (quando há respostas do suporte ou carregando) */}\n {ticket.status === SupportStatus.RESPONDIDO && isLoadingAnswers && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta de Suporte Técnico\n </Text>\n <AnswerSkeleton />\n </>\n )}\n\n {/* Seção de resposta (quando há respostas do suporte) */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId !== userId) && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta de Suporte Técnico\n </Text>\n\n {answers\n .filter((answer) => answer.userId !== userId)\n .sort(\n (a, b) =>\n new Date(b.createdAt).getTime() -\n new Date(a.createdAt).getTime()\n )\n .slice(0, 1)\n .map((answer) => (\n <div\n key={answer.id}\n className=\"bg-background p-4 space-y-6 rounded-xl\"\n >\n {/* Recebido em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Recebido\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {dayjs(answer.createdAt).format(\n 'DD MMMM YYYY, [às] HH[h]'\n )}\n </Text>\n </div>\n\n <Divider />\n\n {/* Resposta */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Resposta\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {answer.answer}\n </Text>\n </div>\n </div>\n ))}\n </>\n )}\n\n {/* Seção de resposta do usuário */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId === userId) && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta enviada\n </Text>\n\n {answers\n .filter((answer) => answer.userId === userId)\n .sort(\n (a, b) =>\n new Date(b.createdAt).getTime() -\n new Date(a.createdAt).getTime()\n )\n .slice(0, 1)\n .map((answer) => (\n <div\n key={answer.id}\n className=\"bg-background p-4 space-y-6 rounded-xl\"\n >\n {/* Enviada em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Enviada\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {dayjs(answer.createdAt).format(\n 'DD MMMM YYYY, [às] HH[h]'\n )}\n </Text>\n </div>\n\n <Divider />\n\n {/* Resposta */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Resposta\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {answer.answer}\n </Text>\n </div>\n </div>\n ))}\n </>\n )}\n\n {/* Seção Responder */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId !== userId) && (\n <>\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950 my-6\">\n Responder\n </Text>\n\n <div className=\"space-y-4\">\n <TextArea\n placeholder=\"Detalhe o problema aqui.\"\n rows={4}\n className=\"w-full\"\n value={responseText}\n onChange={(e) => setResponseText(e.target.value)}\n />\n\n {responseText.trim().length > 0 && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"solid\"\n size=\"medium\"\n onClick={handleSubmitAnswer}\n disabled={isSubmittingAnswer}\n >\n {isSubmittingAnswer ? 'Enviando...' : 'Enviar'}\n </Button>\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n </Modal>\n\n {/* Modal de confirmação de encerramento */}\n <Modal\n isOpen={showCloseConfirmation}\n onClose={() => setShowCloseConfirmation(false)}\n title=\"Encerrar pedido?\"\n size=\"md\"\n hideCloseButton={false}\n closeOnEscape={true}\n data-testid=\"close-ticket-modal\"\n >\n <div className=\"space-y-6\">\n <Text size=\"sm\" weight=\"normal\" className=\"text-text-700\">\n Ao encerrar este pedido, ele será fechado e não poderá mais ser\n atualizado.\n </Text>\n\n <div className=\"flex gap-3 justify-end\">\n <Button\n variant=\"outline\"\n size=\"medium\"\n onClick={() => setShowCloseConfirmation(false)}\n >\n Cancelar\n </Button>\n <Button\n variant=\"solid\"\n size=\"medium\"\n action=\"negative\"\n onClick={handleCloseTicket}\n >\n Encerrar\n </Button>\n </div>\n </div>\n </Modal>\n </>\n );\n};\n","import { KeyIcon, BugIcon, InfoIcon } from '@phosphor-icons/react';\nimport React from 'react';\nimport { SupportCategory } from '../../../types/support';\n\nexport const getCategoryIcon = (\n category: SupportCategory | null,\n size = 16\n): React.ReactNode => {\n if (!category) return null;\n switch (category) {\n case SupportCategory.ACESSO:\n return <KeyIcon size={size} />;\n case SupportCategory.TECNICO:\n return <BugIcon size={size} />;\n case SupportCategory.OUTROS:\n return <InfoIcon size={size} />;\n default:\n return <InfoIcon size={size} />;\n }\n};\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WUN5NHZE.js","../src/components/Support/components/TicketModal.tsx","../src/components/Support/utils/supportUtils.tsx"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WUN5NHZE.js","sourcesContent":[null,"import { useState, useEffect, useCallback } from 'react';\nimport dayjs from 'dayjs';\nimport 'dayjs/locale/pt-br';\nimport Badge from '../../Badge/Badge';\nimport Button from '../../Button/Button';\nimport Divider from '../../Divider/Divider';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport TextArea from '../../TextArea/TextArea';\nimport { SkeletonText } from '../../Skeleton/Skeleton';\nimport {\n SupportTicket,\n SupportStatus,\n SupportAnswerAPI,\n GetSupportAnswersResponse,\n SubmitSupportAnswerRequest,\n SubmitSupportAnswerResponse,\n getStatusBadgeAction,\n getStatusText,\n getCategoryText,\n SupportApiClient,\n} from '../../../types/support';\nimport { getCategoryIcon } from '../utils/supportUtils';\n\ndayjs.locale('pt-br');\n\n// Skeleton component for answer loading\nconst AnswerSkeleton = () => (\n <div className=\"bg-background p-4 space-y-6 rounded-xl\">\n {/* Date skeleton */}\n <div className=\"flex items-center space-x-6\">\n <SkeletonText width=\"80px\" height={16} />\n <SkeletonText width=\"200px\" height={16} />\n </div>\n\n <Divider />\n\n {/* Answer content skeleton */}\n <div className=\"flex items-start space-x-6\">\n <SkeletonText width=\"80px\" height={16} />\n <div className=\"flex-1 space-y-2\">\n <SkeletonText width=\"100%\" height={16} />\n <SkeletonText width=\"80%\" height={16} />\n <SkeletonText width=\"60%\" height={16} />\n </div>\n </div>\n </div>\n);\n\nexport interface TicketModalProps {\n ticket: SupportTicket;\n isOpen: boolean;\n onClose: () => void;\n onTicketClose?: (ticketId: string) => void;\n /** API client instance for making requests */\n apiClient: SupportApiClient;\n /** Current user ID */\n userId?: string;\n}\n\nexport const TicketModal = ({\n ticket,\n isOpen,\n onClose,\n onTicketClose,\n apiClient,\n userId,\n}: TicketModalProps) => {\n const [showCloseConfirmation, setShowCloseConfirmation] = useState(false);\n const [responseText, setResponseText] = useState('');\n const [answers, setAnswers] = useState<SupportAnswerAPI[]>([]);\n const [isSubmittingAnswer, setIsSubmittingAnswer] = useState(false);\n const [isLoadingAnswers, setIsLoadingAnswers] = useState(false);\n\n const handleCloseTicket = () => {\n onTicketClose?.(ticket.id);\n setShowCloseConfirmation(false);\n onClose();\n };\n\n // Fetch support answers\n const fetchAnswers = useCallback(async () => {\n if (!ticket.id || ticket.status !== SupportStatus.RESPONDIDO) return;\n\n setIsLoadingAnswers(true);\n try {\n const response = await apiClient.get<GetSupportAnswersResponse>(\n `/support/answer/${ticket.id}`\n );\n setAnswers(response.data.data || []);\n } catch (error) {\n console.error('Erro ao buscar respostas:', error);\n setAnswers([]);\n } finally {\n setIsLoadingAnswers(false);\n }\n }, [ticket.id, ticket.status, apiClient]);\n\n // Submit user answer\n const handleSubmitAnswer = async () => {\n if (!responseText.trim() || !userId || !ticket.id) {\n return;\n }\n\n setIsSubmittingAnswer(true);\n try {\n const requestData: SubmitSupportAnswerRequest = {\n userId: userId,\n supportId: ticket.id,\n answer: responseText.trim(),\n };\n\n await apiClient.post<SubmitSupportAnswerResponse>(\n '/support/answer',\n requestData\n );\n\n // Clear response text\n setResponseText('');\n\n // Refresh answers list\n await fetchAnswers();\n } catch (error) {\n console.error('Erro ao enviar resposta:', error);\n } finally {\n setIsSubmittingAnswer(false);\n }\n };\n\n const canCloseTicket = ticket.status !== SupportStatus.ENCERRADO;\n\n // Limpar o texto e carregar respostas quando o modal for aberto\n useEffect(() => {\n if (isOpen) {\n setResponseText('');\n (async () => {\n await fetchAnswers();\n })().catch((error) => {\n console.error('Erro ao carregar respostas:', error);\n });\n } else {\n setAnswers([]);\n }\n }, [isOpen, fetchAnswers]);\n\n return (\n <>\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={`Pedido: ${ticket.title}`}\n size=\"lg\"\n hideCloseButton={false}\n closeOnEscape={true}\n data-testid=\"ticket-modal\"\n >\n <div className=\"flex flex-col h-full max-h-[80vh]\">\n {/* Header com botão Encerrar Pedido */}\n <div className=\"flex justify-between items-center mb-3\">\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950\">\n Detalhes\n </Text>\n {canCloseTicket && (\n <Button\n variant=\"outline\"\n size=\"small\"\n action=\"negative\"\n onClick={() => setShowCloseConfirmation(true)}\n >\n Encerrar Pedido\n </Button>\n )}\n </div>\n\n {/* Conteúdo com scroll */}\n <div className=\"flex-1 overflow-y-auto pr-2 space-y-6\">\n <div className=\"bg-background p-4 space-y-6 rounded-xl\">\n {/* ID */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n ID\n </Text>\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {ticket.id}\n </Text>\n </div>\n\n {/* Aberto em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Aberto em\n </Text>\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {dayjs(ticket.createdAt).format('DD MMMM YYYY, [às] HH[h]')}\n </Text>\n </div>\n\n {/* Status */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Status\n </Text>\n <Badge\n variant=\"solid\"\n size=\"small\"\n action={getStatusBadgeAction(ticket.status)}\n className=\"w-fit\"\n >\n {getStatusText(ticket.status)}\n </Badge>\n </div>\n\n {/* Tipo */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Tipo\n </Text>\n <Badge\n variant=\"solid\"\n size=\"small\"\n action=\"muted\"\n className=\"w-fit\"\n >\n {getCategoryIcon(ticket.category)}\n {getCategoryText(ticket.category)}\n </Badge>\n </div>\n\n <Divider />\n\n {/* Descrição */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Descrição\n </Text>\n {ticket.description && (\n <Text size=\"md\" weight=\"normal\" className=\"text-text-600\">\n {ticket.description}\n </Text>\n )}\n </div>\n </div>\n\n {/* Seção de resposta (quando há respostas do suporte ou carregando) */}\n {ticket.status === SupportStatus.RESPONDIDO && isLoadingAnswers && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta de Suporte Técnico\n </Text>\n <AnswerSkeleton />\n </>\n )}\n\n {/* Seção de resposta (quando há respostas do suporte) */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId !== userId) && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta de Suporte Técnico\n </Text>\n\n {answers\n .filter((answer) => answer.userId !== userId)\n .sort(\n (a, b) =>\n new Date(b.createdAt).getTime() -\n new Date(a.createdAt).getTime()\n )\n .slice(0, 1)\n .map((answer) => (\n <div\n key={answer.id}\n className=\"bg-background p-4 space-y-6 rounded-xl\"\n >\n {/* Recebido em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Recebido\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {dayjs(answer.createdAt).format(\n 'DD MMMM YYYY, [às] HH[h]'\n )}\n </Text>\n </div>\n\n <Divider />\n\n {/* Resposta */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Resposta\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {answer.answer}\n </Text>\n </div>\n </div>\n ))}\n </>\n )}\n\n {/* Seção de resposta do usuário */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId === userId) && (\n <>\n <Text size=\"md\" weight=\"bold\" className=\"text-text-950 my-6\">\n Resposta enviada\n </Text>\n\n {answers\n .filter((answer) => answer.userId === userId)\n .sort(\n (a, b) =>\n new Date(b.createdAt).getTime() -\n new Date(a.createdAt).getTime()\n )\n .slice(0, 1)\n .map((answer) => (\n <div\n key={answer.id}\n className=\"bg-background p-4 space-y-6 rounded-xl\"\n >\n {/* Enviada em */}\n <div className=\"flex items-center space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Enviada\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {dayjs(answer.createdAt).format(\n 'DD MMMM YYYY, [às] HH[h]'\n )}\n </Text>\n </div>\n\n <Divider />\n\n {/* Resposta */}\n <div className=\"flex items-start space-x-6\">\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"text-text-700 w-20\"\n >\n Resposta\n </Text>\n <Text\n size=\"md\"\n weight=\"normal\"\n className=\"text-text-600\"\n >\n {answer.answer}\n </Text>\n </div>\n </div>\n ))}\n </>\n )}\n\n {/* Seção Responder */}\n {!isLoadingAnswers &&\n answers.some((answer) => answer.userId !== userId) && (\n <>\n <Text size=\"lg\" weight=\"bold\" className=\"text-text-950 my-6\">\n Responder\n </Text>\n\n <div className=\"space-y-4\">\n <TextArea\n placeholder=\"Detalhe o problema aqui.\"\n rows={4}\n className=\"w-full\"\n value={responseText}\n onChange={(e) => setResponseText(e.target.value)}\n />\n\n {responseText.trim().length > 0 && (\n <div className=\"flex justify-end\">\n <Button\n variant=\"solid\"\n size=\"medium\"\n onClick={handleSubmitAnswer}\n disabled={isSubmittingAnswer}\n >\n {isSubmittingAnswer ? 'Enviando...' : 'Enviar'}\n </Button>\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n </Modal>\n\n {/* Modal de confirmação de encerramento */}\n <Modal\n isOpen={showCloseConfirmation}\n onClose={() => setShowCloseConfirmation(false)}\n title=\"Encerrar pedido?\"\n size=\"md\"\n hideCloseButton={false}\n closeOnEscape={true}\n data-testid=\"close-ticket-modal\"\n >\n <div className=\"space-y-6\">\n <Text size=\"sm\" weight=\"normal\" className=\"text-text-700\">\n Ao encerrar este pedido, ele será fechado e não poderá mais ser\n atualizado.\n </Text>\n\n <div className=\"flex gap-3 justify-end\">\n <Button\n variant=\"outline\"\n size=\"medium\"\n onClick={() => setShowCloseConfirmation(false)}\n >\n Cancelar\n </Button>\n <Button\n variant=\"solid\"\n size=\"medium\"\n action=\"negative\"\n onClick={handleCloseTicket}\n >\n Encerrar\n </Button>\n </div>\n </div>\n </Modal>\n </>\n );\n};\n","import { KeyIcon, BugIcon, InfoIcon } from '@phosphor-icons/react';\nimport React from 'react';\nimport { SupportCategory } from '../../../types/support';\n\nexport const getCategoryIcon = (\n category: SupportCategory | null,\n size = 16\n): React.ReactNode => {\n if (!category) return null;\n switch (category) {\n case SupportCategory.ACESSO:\n return <KeyIcon size={size} />;\n case SupportCategory.TECNICO:\n return <BugIcon size={size} />;\n case SupportCategory.OUTROS:\n return <InfoIcon size={size} />;\n default:\n return <InfoIcon size={size} />;\n }\n};\n"]}
@@ -3,7 +3,7 @@
3
3
  var _chunkP7W2VN4Kjs = require('./chunk-P7W2VN4K.js');
4
4
 
5
5
 
6
- var _chunkR6MTPYYWjs = require('./chunk-R6MTPYYW.js');
6
+ var _chunkAZTBNERQjs = require('./chunk-AZTBNERQ.js');
7
7
 
8
8
  // src/components/AnnouncementModal/AnnouncementsGate.tsx
9
9
  var _react = require('react');
@@ -16,7 +16,7 @@ var AnnouncementsGate = ({ apiClient, userId }) => {
16
16
  const { current, pending, dismissCurrent } = useAnnouncements(userId);
17
17
  if (!current) return null;
18
18
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
19
- _chunkR6MTPYYWjs.AnnouncementModal_default,
19
+ _chunkAZTBNERQjs.AnnouncementModal_default,
20
20
  {
21
21
  announcement: current,
22
22
  isOpen: true,
@@ -30,4 +30,4 @@ var AnnouncementsGate_default = AnnouncementsGate;
30
30
 
31
31
 
32
32
  exports.AnnouncementsGate_default = AnnouncementsGate_default;
33
- //# sourceMappingURL=chunk-AAU7ABJN.js.map
33
+ //# sourceMappingURL=chunk-WWTRSQ3X.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-AAU7ABJN.js","../src/components/AnnouncementModal/AnnouncementsGate.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACPA,8BAAwB;AAgDpB,+CAAA;AAbJ,IAAM,kBAAA,EAAoB,CAAC,EAAE,SAAA,EAAW,OAAO,CAAA,EAAA,GAA8B;AAG3E,EAAA,MAAM,iBAAA,EAAmB,4BAAA;AAAA,IACvB,CAAA,EAAA,GAAM,qDAAA,SAAgC,CAAA;AAAA,IACtC,CAAC,SAAS;AAAA,EACZ,CAAA;AAEA,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAS,eAAe,EAAA,EAAI,gBAAA,CAAiB,MAAM,CAAA;AAEpE,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS,OAAO,IAAA;AAErB,EAAA,uBACE,6BAAA;AAAA,IAAC,0CAAA;AAAA,IAAA;AAAA,MACC,YAAA,EAAc,OAAA;AAAA,MACd,MAAA,EAAM,IAAA;AAAA,MACN,OAAA,EAAS,cAAA;AAAA,MACT,cAAA,EAAgB,OAAA,CAAQ,OAAA,EAAS;AAAA,IAAA;AAAA,EACnC,CAAA;AAEJ,CAAA;AAEA,IAAO,0BAAA,EAAQ,iBAAA;AD7Bf;AACA;AACE;AACF,8DAAC","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-AAU7ABJN.js","sourcesContent":[null,"import { useMemo } from 'react';\nimport AnnouncementModal from './AnnouncementModal';\nimport { createUseAnnouncements } from '../../hooks/useAnnouncements';\nimport type { AnnouncementsApiClient } from '../../types/announcements';\n\n/**\n * Props for {@link AnnouncementsGate}\n */\nexport type AnnouncementsGateProps = {\n /** Authenticated API client of the host app */\n apiClient: AnnouncementsApiClient;\n /**\n * Id of the logged-in user. Scopes the \"already seen\" storage, so two people\n * sharing a browser do not hide each other's announcements. Pass null/undefined\n * while unauthenticated and nothing is fetched.\n */\n userId?: string | null;\n};\n\n/**\n * Drop-in announcements gate for the authenticated app shell.\n *\n * Mount it once inside the logged-in layout and it takes care of everything:\n * fetching the announcements for this user's institution and profile, skipping\n * the ones already dismissed (localStorage), and showing the rest one after\n * another until the queue is empty.\n *\n * Identical in the student, teacher and manager apps — the only difference is\n * the API client each one injects.\n *\n * @example\n * ```tsx\n * <AnnouncementsGate apiClient={api} userId={user?.id} />\n * ```\n */\nconst AnnouncementsGate = ({ apiClient, userId }: AnnouncementsGateProps) => {\n // The hook factory closes over the client, so it must not be recreated on\n // every render — that would reset the fetch guard and refetch in a loop.\n const useAnnouncements = useMemo(\n () => createUseAnnouncements(apiClient),\n [apiClient]\n );\n\n const { current, pending, dismissCurrent } = useAnnouncements(userId);\n\n if (!current) return null;\n\n return (\n <AnnouncementModal\n announcement={current}\n isOpen\n onClose={dismissCurrent}\n remainingCount={pending.length - 1}\n />\n );\n};\n\nexport default AnnouncementsGate;\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WWTRSQ3X.js","../src/components/AnnouncementModal/AnnouncementsGate.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,sDAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACPA,8BAAwB;AAgDpB,+CAAA;AAbJ,IAAM,kBAAA,EAAoB,CAAC,EAAE,SAAA,EAAW,OAAO,CAAA,EAAA,GAA8B;AAG3E,EAAA,MAAM,iBAAA,EAAmB,4BAAA;AAAA,IACvB,CAAA,EAAA,GAAM,qDAAA,SAAgC,CAAA;AAAA,IACtC,CAAC,SAAS;AAAA,EACZ,CAAA;AAEA,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAS,eAAe,EAAA,EAAI,gBAAA,CAAiB,MAAM,CAAA;AAEpE,EAAA,GAAA,CAAI,CAAC,OAAA,EAAS,OAAO,IAAA;AAErB,EAAA,uBACE,6BAAA;AAAA,IAAC,0CAAA;AAAA,IAAA;AAAA,MACC,YAAA,EAAc,OAAA;AAAA,MACd,MAAA,EAAM,IAAA;AAAA,MACN,OAAA,EAAS,cAAA;AAAA,MACT,cAAA,EAAgB,OAAA,CAAQ,OAAA,EAAS;AAAA,IAAA;AAAA,EACnC,CAAA;AAEJ,CAAA;AAEA,IAAO,0BAAA,EAAQ,iBAAA;AD7Bf;AACA;AACE;AACF,8DAAC","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WWTRSQ3X.js","sourcesContent":[null,"import { useMemo } from 'react';\nimport AnnouncementModal from './AnnouncementModal';\nimport { createUseAnnouncements } from '../../hooks/useAnnouncements';\nimport type { AnnouncementsApiClient } from '../../types/announcements';\n\n/**\n * Props for {@link AnnouncementsGate}\n */\nexport type AnnouncementsGateProps = {\n /** Authenticated API client of the host app */\n apiClient: AnnouncementsApiClient;\n /**\n * Id of the logged-in user. Scopes the \"already seen\" storage, so two people\n * sharing a browser do not hide each other's announcements. Pass null/undefined\n * while unauthenticated and nothing is fetched.\n */\n userId?: string | null;\n};\n\n/**\n * Drop-in announcements gate for the authenticated app shell.\n *\n * Mount it once inside the logged-in layout and it takes care of everything:\n * fetching the announcements for this user's institution and profile, skipping\n * the ones already dismissed (localStorage), and showing the rest one after\n * another until the queue is empty.\n *\n * Identical in the student, teacher and manager apps — the only difference is\n * the API client each one injects.\n *\n * @example\n * ```tsx\n * <AnnouncementsGate apiClient={api} userId={user?.id} />\n * ```\n */\nconst AnnouncementsGate = ({ apiClient, userId }: AnnouncementsGateProps) => {\n // The hook factory closes over the client, so it must not be recreated on\n // every render — that would reset the fetch guard and refetch in a loop.\n const useAnnouncements = useMemo(\n () => createUseAnnouncements(apiClient),\n [apiClient]\n );\n\n const { current, pending, dismissCurrent } = useAnnouncements(userId);\n\n if (!current) return null;\n\n return (\n <AnnouncementModal\n announcement={current}\n isOpen\n onClose={dismissCurrent}\n remainingCount={pending.length - 1}\n />\n );\n};\n\nexport default AnnouncementsGate;\n"]}
@@ -6,7 +6,7 @@ var _chunkP5M6Z5VGjs = require('./chunk-P5M6Z5VG.js');
6
6
  var _chunkVVO3W2SMjs = require('./chunk-VVO3W2SM.js');
7
7
 
8
8
 
9
- var _chunkMNU45FM3js = require('./chunk-MNU45FM3.js');
9
+ var _chunkCOT4KW3Hjs = require('./chunk-COT4KW3H.js');
10
10
 
11
11
 
12
12
  var _chunkXN2HPVVHjs = require('./chunk-XN2HPVVH.js');
@@ -154,7 +154,7 @@ function OptionRow({
154
154
  ),
155
155
  children: [
156
156
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
157
- _chunkMNU45FM3js.CheckBox_default,
157
+ _chunkCOT4KW3Hjs.CheckBox_default,
158
158
  {
159
159
  checked: selected,
160
160
  readOnly: true,
@@ -717,4 +717,4 @@ var MultiSearchSelect_default = MultiSearchSelect;
717
717
 
718
718
 
719
719
  exports.MultiSearchSelect = MultiSearchSelect; exports.MultiSearchSelect_default = MultiSearchSelect_default;
720
- //# sourceMappingURL=chunk-BR6D2TGF.js.map
720
+ //# sourceMappingURL=chunk-WZOA2PVJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-BR6D2TGF.js","../src/components/MultiSearchSelect/MultiSearchSelect.tsx","../src/components/MultiSearchSelect/constants.ts","../src/components/MultiSearchSelect/OptionList.tsx","../src/components/MultiSearchSelect/TriggerContent.tsx","../src/components/MultiSearchSelect/useDropdownPosition.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-BR6D2TGF.js","sourcesContent":[null,"import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { CaretDownIcon } from '@phosphor-icons/react/dist/csr/CaretDown';\nimport { MagnifyingGlassIcon } from '@phosphor-icons/react/dist/csr/MagnifyingGlass';\nimport Input from '../Input/Input';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport {\n LABEL_SIZE_CLASSES,\n OPEN_KEYS,\n PADDING_CLASSES,\n SIZE_CLASSES,\n VARIANT_CLASSES,\n} from './constants';\nimport { OptionList } from './OptionList';\nimport { TriggerContent } from './TriggerContent';\nimport { useDropdownPosition } from './useDropdownPosition';\nimport { useDebounce } from '../../hooks/useDebounce';\nimport type { MultiSearchSelectOption, MultiSearchSelectProps } from './types';\n\n/**\n * Searchable multi-select: the sibling of `SearchSelect` for picking several\n * values at once. Same sizing, borders, portalled dropdown and keyboard model,\n * with removable chips in the trigger and a checkbox per option.\n *\n * Filtering is local by default — pass an already loaded option list. For a\n * source too large to load at once, supply `onSearch` + `pagination` +\n * `onLoadMore` and set `filterLocally={false}`: the query then round-trips to\n * the server and the list grows by infinite scroll, exactly like `SearchSelect`.\n *\n * Follows the W3C ARIA combobox pattern (`combobox` -> `listbox` -> `option`),\n * the same one `SearchSelect` uses. No native element covers it: `select\n * multiple` and `datalist` support neither removable chips, nor an inline\n * search field, nor a checkbox per row.\n *\n * @example\n * ```tsx\n * <MultiSearchSelect\n * label=\"Turmas\"\n * values={classIds}\n * onValuesChange={setClassIds}\n * options={classes.map((c) => ({ value: c.id, label: c.name }))}\n * unknownValueLabel=\"Turma não encontrada\"\n * />\n * ```\n */\nexport function MultiSearchSelect({\n values,\n onValuesChange,\n options,\n label,\n placeholder = 'Selecione...',\n searchPlaceholder = 'Buscar...',\n emptyText = 'Nenhum resultado encontrado',\n loadingText = 'Carregando...',\n unknownValueLabel = 'Item não encontrado',\n helperText,\n errorMessage,\n disabled = false,\n loading = false,\n size = 'small',\n variant = 'outlined',\n className,\n id,\n maxVisibleChips = 3,\n onSearch,\n searchDebounce = 300,\n pagination,\n onLoadMore,\n loadingMore = false,\n filterLocally = true,\n}: Readonly<MultiSearchSelectProps>) {\n const [open, setOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n\n const triggerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const isFetchingMoreRef = useRef(false);\n\n const generatedId = useId();\n const selectId = id ?? `multi-search-select-${generatedId}`;\n const listboxId = `${selectId}-listbox`;\n const helperId = `${selectId}-helper`;\n const errorId = `${selectId}-error`;\n const labelId = `${selectId}-label`;\n\n const { triggerRect, measureTrigger, dropdownStyles } = useDropdownPosition({\n open,\n triggerRef,\n });\n\n const debouncedSearch = useDebounce(searchQuery, searchDebounce);\n\n useEffect(() => {\n if (onSearch) onSearch(debouncedSearch);\n }, [debouncedSearch, onSearch]);\n\n const filteredOptions = useMemo(\n () => (filterLocally ? filterOptions(options, searchQuery) : options),\n [options, searchQuery, filterLocally]\n );\n\n // Reset the in-flight guard once the caller reports the page landed.\n useEffect(() => {\n if (!loadingMore) isFetchingMoreRef.current = false;\n }, [loadingMore]);\n\n const handleScroll = useCallback(() => {\n if (\n !listRef.current ||\n !pagination?.hasNext ||\n loadingMore ||\n isFetchingMoreRef.current ||\n !onLoadMore\n )\n return;\n\n const { scrollTop, scrollHeight, clientHeight } = listRef.current;\n const scrollThreshold = 50;\n if (scrollHeight - scrollTop - clientHeight >= scrollThreshold) return;\n\n isFetchingMoreRef.current = true;\n const result = onLoadMore();\n // A promise resolves the guard itself; a void return leans on `loadingMore`.\n if (result && typeof result.then === 'function') {\n result.finally(() => {\n isFetchingMoreRef.current = false;\n });\n }\n }, [pagination?.hasNext, loadingMore, onLoadMore]);\n\n const selectedOptions = useMemo(\n () => resolveSelectedOptions(values, options, unknownValueLabel),\n [values, options, unknownValueLabel]\n );\n\n const interactive = !disabled && !loading;\n\n const closeDropdown = useCallback((restoreFocus = false) => {\n setOpen(false);\n setSearchQuery('');\n if (restoreFocus) triggerRef.current?.focus();\n }, []);\n\n const openDropdown = useCallback(() => {\n measureTrigger();\n setSearchQuery('');\n setHighlightedIndex(-1);\n setOpen(true);\n }, [measureTrigger]);\n\n const toggleOpen = useCallback(() => {\n if (!interactive) return;\n if (open) {\n closeDropdown();\n return;\n }\n openDropdown();\n }, [interactive, open, closeDropdown, openDropdown]);\n\n /**\n * Toggling deliberately keeps the dropdown and the search query alive: picks\n * usually come in runs of adjacent options.\n */\n const toggleValue = useCallback(\n (optionValue: string) => {\n const alreadySelected = values.includes(optionValue);\n const next = alreadySelected\n ? values.filter((value) => value !== optionValue)\n : [...values, optionValue];\n onValuesChange(next);\n },\n [values, onValuesChange]\n );\n\n const removeValue = useCallback(\n (optionValue: string) => {\n onValuesChange(values.filter((value) => value !== optionValue));\n },\n [values, onValuesChange]\n );\n\n useFocusOnOpen(open, searchInputRef);\n useCloseOnOutsideClick(open, closeDropdown, triggerRef, contentRef);\n useScrollHighlightIntoView(highlightedIndex, listRef);\n useClampHighlight(\n filteredOptions.length,\n highlightedIndex,\n setHighlightedIndex\n );\n\n const moveHighlight = useCallback(\n (direction: 'next' | 'prev') => {\n setHighlightedIndex((current) =>\n findNextEnabledIndex(filteredOptions, current, direction)\n );\n },\n [filteredOptions]\n );\n\n const toggleHighlighted = useCallback(() => {\n const option = filteredOptions[highlightedIndex];\n if (!option || option.disabled) return;\n toggleValue(option.value);\n }, [filteredOptions, highlightedIndex, toggleValue]);\n\n /** An empty query turns Backspace into \"drop the last chip\". */\n const canRemoveLastChip = searchQuery === '' && values.length > 0;\n\n const removeLastValue = useCallback(() => {\n onValuesChange(values.slice(0, -1));\n }, [values, onValuesChange]);\n\n /**\n * Dismisses the dropdown without letting Escape reach the document.\n *\n * Modals in the design system close on a document-level Escape listener, so a\n * bubbling key would discard the whole surrounding form.\n *\n * It is bound to every element that can hold focus while the dropdown is open\n * — the trigger, the search field and the listbox, which the option rows sit\n * inside — rather than to the panel wrapper, which carries no role.\n */\n const handleEscape = useCallback(\n (event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Escape') return;\n event.preventDefault();\n event.stopPropagation();\n closeDropdown(true);\n },\n [closeDropdown]\n );\n\n const handleSearchKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n moveHighlight('next');\n return;\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n moveHighlight('prev');\n return;\n }\n if (event.key === 'Enter') {\n event.preventDefault();\n toggleHighlighted();\n return;\n }\n if (event.key === 'Backspace' && canRemoveLastChip) {\n event.preventDefault();\n removeLastValue();\n return;\n }\n handleEscape(event);\n },\n [\n moveHighlight,\n toggleHighlighted,\n removeLastValue,\n canRemoveLastChip,\n handleEscape,\n ]\n );\n\n const handleTriggerKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (!interactive) return;\n\n if (OPEN_KEYS.includes(event.key) && !open) {\n event.preventDefault();\n openDropdown();\n return;\n }\n\n handleEscape(event);\n },\n [interactive, open, openDropdown, handleEscape]\n );\n\n const describedById = resolveDescribedBy({\n errorMessage,\n errorId,\n helperText,\n helperId,\n });\n\n const activeDescendant =\n highlightedIndex >= 0\n ? `${listboxId}-option-${highlightedIndex}`\n : undefined;\n\n const hasDescription = Boolean(helperText) || Boolean(errorMessage);\n\n const dropdown = open && triggerRect && (\n <div\n ref={contentRef}\n data-multi-search-select-id={selectId}\n style={dropdownStyles}\n className=\"bg-secondary rounded-md border border-border-100 shadow-lg overflow-hidden flex flex-col\"\n >\n <div className=\"p-2 border-b border-border-100\">\n <Input\n ref={searchInputRef}\n type=\"text\"\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-activedescendant={activeDescendant}\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n onKeyDown={handleSearchKeyDown}\n placeholder={searchPlaceholder}\n size=\"small\"\n variant=\"outlined\"\n iconRight={<MagnifyingGlassIcon size={16} />}\n containerClassName=\"w-full\"\n />\n </div>\n\n <div // NOSONAR — ARIA combobox pattern, no native tag fits (see JSDoc)\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable\n aria-label={label ?? 'Opções'}\n tabIndex={-1}\n onKeyDown={handleEscape}\n onScroll={handleScroll}\n className=\"flex-1 overflow-y-auto\"\n >\n <OptionList\n options={filteredOptions}\n selectedValues={values}\n highlightedIndex={highlightedIndex}\n listboxId={listboxId}\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n onToggle={toggleValue}\n loadingMore={loadingMore}\n pagination={pagination}\n localFilterActive={filterLocally && searchQuery !== ''}\n />\n </div>\n </div>\n );\n\n return (\n <div className={cn('w-full', className)}>\n {label && (\n <label\n id={labelId}\n htmlFor={selectId}\n className={cn(\n 'block font-bold text-text-900 mb-1.5',\n LABEL_SIZE_CLASSES[size]\n )}\n >\n {label}\n </label>\n )}\n\n {/*\n `htmlFor` alone would not name this trigger: a `label` only associates\n with labelable elements, which a `div` is not. `aria-labelledby` is what\n gives it an accessible name.\n */}\n <div\n ref={triggerRef}\n id={selectId}\n role=\"combobox\"\n tabIndex={interactive ? 0 : -1}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? listboxId : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-invalid={!!errorMessage}\n aria-describedby={describedById}\n aria-disabled={!interactive}\n onClick={toggleOpen}\n onKeyDown={handleTriggerKeyDown}\n className={cn(\n 'flex w-full items-center justify-between gap-2 border-border-300 bg-background',\n SIZE_CLASSES[size],\n PADDING_CLASSES[size],\n VARIANT_CLASSES[variant],\n errorMessage && 'border-indicator-error',\n interactive\n ? 'cursor-pointer hover:bg-background-50 text-text-700'\n : 'cursor-not-allowed opacity-50 text-text-400'\n )}\n >\n <div className=\"min-w-0 flex-1\">\n <TriggerContent\n selectedOptions={selectedOptions}\n placeholder={placeholder}\n loading={loading}\n loadingText={loadingText}\n disabled={disabled}\n maxVisibleChips={maxVisibleChips}\n size={size}\n onRemove={removeValue}\n />\n </div>\n <CaretDownIcon\n className={cn(\n 'h-4 w-4 opacity-50 transition-transform shrink-0',\n open && 'rotate-180'\n )}\n />\n </div>\n\n {hasDescription && (\n <div className=\"mt-1.5\">\n {helperText && !errorMessage && (\n <Text id={helperId} size=\"sm\" className=\"text-text-500\">\n {helperText}\n </Text>\n )}\n {errorMessage && (\n <Text id={errorId} size=\"sm\" className=\"text-indicator-error\">\n {errorMessage}\n </Text>\n )}\n </div>\n )}\n\n {typeof document !== 'undefined' && createPortal(dropdown, document.body)}\n </div>\n );\n}\n\n/** Case-insensitive local filter over the option captions. */\nfunction filterOptions(\n options: MultiSearchSelectOption[],\n searchQuery: string\n): MultiSearchSelectOption[] {\n if (!searchQuery) return options;\n const query = searchQuery.toLowerCase();\n return options.filter((option) => option.label.toLowerCase().includes(query));\n}\n\n/**\n * Maps every selected value to a renderable option.\n *\n * Values are never filtered against `options`: while the list is still loading,\n * or after an entry is deleted elsewhere, dropping the value would silently\n * discard a real binding. It falls back to a placeholder caption instead, so\n * the value stays visible and removable.\n */\nfunction resolveSelectedOptions(\n values: string[],\n options: MultiSearchSelectOption[],\n unknownValueLabel: string\n): MultiSearchSelectOption[] {\n return values.map(\n (value) =>\n options.find((option) => option.value === value) ?? {\n value,\n label: unknownValueLabel,\n }\n );\n}\n\n/**\n * Walks the option list in one direction and returns the first enabled index,\n * wrapping around. Returns -1 when every option is disabled or the list is\n * empty.\n */\nfunction findNextEnabledIndex(\n options: MultiSearchSelectOption[],\n currentIndex: number,\n direction: 'next' | 'prev'\n): number {\n const total = options.length;\n if (total === 0) return -1;\n\n const step = direction === 'next' ? 1 : -1;\n const unsetStart = direction === 'next' ? -1 : total;\n const startIndex = currentIndex >= 0 ? currentIndex : unsetStart;\n let index = startIndex;\n\n for (let attempt = 0; attempt < total; attempt++) {\n index = (index + step + total) % total;\n if (!options[index].disabled) return index;\n }\n\n return -1;\n}\n\ninterface DescribedByParams {\n errorMessage?: string;\n errorId: string;\n helperText?: string;\n helperId: string;\n}\n\n/** The error takes precedence over the hint as the field's description. */\nfunction resolveDescribedBy({\n errorMessage,\n errorId,\n helperText,\n helperId,\n}: DescribedByParams): string | undefined {\n if (errorMessage) return errorId;\n if (helperText) return helperId;\n return undefined;\n}\n\n/** Moves focus into the search field as soon as the dropdown opens. */\nfunction useFocusOnOpen(\n open: boolean,\n searchInputRef: RefObject<HTMLInputElement | null>\n) {\n useEffect(() => {\n if (!open) return;\n const timer = setTimeout(() => searchInputRef.current?.focus(), 0);\n return () => clearTimeout(timer);\n }, [open, searchInputRef]);\n}\n\n/**\n * Dismisses the dropdown on a click outside it.\n *\n * The trigger and the dropdown are checked separately because the dropdown is\n * portalled and therefore not a DOM descendant of the trigger.\n */\nfunction useCloseOnOutsideClick(\n open: boolean,\n close: () => void,\n triggerRef: RefObject<HTMLElement | null>,\n contentRef: RefObject<HTMLElement | null>\n) {\n useEffect(() => {\n if (!open) return;\n\n const handleMouseDown = (event: MouseEvent) => {\n const target = event.target as Node;\n const insideTrigger = triggerRef.current?.contains(target);\n const insideContent = contentRef.current?.contains(target);\n if (insideTrigger || insideContent) return;\n close();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [open, close, triggerRef, contentRef]);\n}\n\n/**\n * Pulls the highlight back into range when a search narrows the option list,\n * so `aria-activedescendant` never points at a row that no longer exists.\n */\nfunction useClampHighlight(\n optionCount: number,\n highlightedIndex: number,\n setHighlightedIndex: (index: number) => void\n) {\n useEffect(() => {\n if (highlightedIndex < optionCount) return;\n setHighlightedIndex(Math.max(optionCount - 1, -1));\n }, [optionCount, highlightedIndex, setHighlightedIndex]);\n}\n\n/** Keeps the keyboard-highlighted row visible in a long list. */\nfunction useScrollHighlightIntoView(\n highlightedIndex: number,\n listRef: RefObject<HTMLDivElement | null>\n) {\n useEffect(() => {\n if (highlightedIndex < 0 || !listRef.current) return;\n const rows = listRef.current.querySelectorAll('[data-option]');\n const row = rows[highlightedIndex] as HTMLElement | undefined;\n row?.scrollIntoView({ block: 'nearest' });\n }, [highlightedIndex, listRef]);\n}\n\nexport type {\n MultiSearchSelectOption,\n MultiSearchSelectPagination,\n MultiSearchSelectProps,\n MultiSearchSelectSize,\n MultiSearchSelectVariant,\n} from './types';\n\nexport default MultiSearchSelect;\n","import type { MultiSearchSelectSize, MultiSearchSelectVariant } from './types';\n\n/**\n * Trigger height per size.\n *\n * Uses `min-h-*` where `SearchSelect` uses a fixed `h-*`: chips wrap onto more\n * than one line once several values are selected.\n */\nexport const SIZE_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'text-sm min-h-8',\n medium: 'text-md min-h-9',\n large: 'text-lg min-h-10',\n};\n\nexport const PADDING_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'px-3 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n};\n\nexport const VARIANT_CLASSES: Record<MultiSearchSelectVariant, string> = {\n outlined: 'border-2 rounded-lg focus:border-primary-950',\n underlined: 'border-b-2 focus:border-primary-950',\n rounded: 'border-2 rounded-full focus:border-primary-950',\n};\n\n/** `Text` size that matches the trigger font for each select size. */\nexport const TRIGGER_TEXT_SIZES: Record<\n MultiSearchSelectSize,\n 'sm' | 'md' | 'lg'\n> = {\n small: 'sm',\n medium: 'md',\n large: 'lg',\n};\n\nexport const LABEL_SIZE_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n};\n\n/** Keys that open the dropdown while the trigger has focus. */\nexport const OPEN_KEYS = ['Enter', ' ', 'ArrowDown'];\n\n/** Matches the dropdown's `max-h`, used to decide whether it flips above. */\nexport const DROPDOWN_MAX_HEIGHT = 300;\n\n/**\n * Floor for the dropdown height, enough for the search field plus a couple of\n * rows. Guards against a trigger scrolled past a viewport edge, where the\n * measured space turns negative.\n */\nexport const DROPDOWN_MIN_HEIGHT = 120;\n\n/** Distance between the trigger and the dropdown. */\nexport const DROPDOWN_GAP = 4;\n","import type { KeyboardEvent } from 'react';\nimport Text from '../Text/Text';\nimport CheckBox from '../CheckBox/CheckBox';\nimport { cn } from '../../utils/utils';\nimport { SpinnerGapIcon } from '@phosphor-icons/react/dist/csr/SpinnerGap';\nimport type {\n MultiSearchSelectOption,\n MultiSearchSelectPagination,\n} from './types';\n\ninterface OptionListProps {\n options: MultiSearchSelectOption[];\n selectedValues: string[];\n highlightedIndex: number;\n listboxId: string;\n loading: boolean;\n loadingText: string;\n emptyText: string;\n onToggle: (value: string) => void;\n /** True while the next page is in flight; renders a footer spinner. */\n loadingMore?: boolean;\n /** Page state of the option source; renders a \"N de M\" footer. */\n pagination?: MultiSearchSelectPagination;\n /** True while a local filter is narrowing the list, which hides the footer. */\n localFilterActive?: boolean;\n}\n\n/**\n * The dropdown body: a loading state, an empty state, or the option rows.\n */\nexport function OptionList({\n options,\n selectedValues,\n highlightedIndex,\n listboxId,\n loading,\n loadingText,\n emptyText,\n onToggle,\n loadingMore = false,\n pagination,\n localFilterActive = false,\n}: Readonly<OptionListProps>) {\n if (loading) {\n return (\n <div className=\"flex items-center justify-center gap-2 p-4 text-text-500\">\n <SpinnerGapIcon size={18} className=\"animate-spin\" />\n <Text size=\"sm\" className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n );\n }\n\n if (options.length === 0) {\n return (\n <Text size=\"sm\" className=\"p-4 text-center text-text-500\">\n {emptyText}\n </Text>\n );\n }\n\n // The \"N de M\" footer counts the loaded options against the server total, so\n // it is meaningless while a local filter is showing a subset of them.\n const showPaginationFooter =\n Boolean(pagination) &&\n !loadingMore &&\n (pagination?.total ?? 0) > 0 &&\n !localFilterActive;\n\n return (\n <>\n {options.map((option, index) => (\n <OptionRow\n key={option.value}\n option={option}\n optionId={`${listboxId}-option-${index}`}\n selected={selectedValues.includes(option.value)}\n highlighted={index === highlightedIndex}\n onToggle={onToggle}\n />\n ))}\n\n {loadingMore && (\n <div className=\"flex items-center justify-center gap-2 p-3 text-text-500 border-t border-border-100\">\n <SpinnerGapIcon size={16} className=\"animate-spin\" />\n <Text size=\"xs\" className=\"text-text-500\">\n Carregando mais...\n </Text>\n </div>\n )}\n\n {showPaginationFooter && (\n <Text\n size=\"xs\"\n className=\"px-3 py-2 text-text-400 border-t border-border-100 text-center\"\n >\n {options.length} de {pagination?.total} itens\n </Text>\n )}\n </>\n );\n}\n\ninterface OptionRowProps {\n option: MultiSearchSelectOption;\n optionId: string;\n selected: boolean;\n highlighted: boolean;\n onToggle: (value: string) => void;\n}\n\n/**\n * One selectable row of the ARIA combobox listbox.\n *\n * A native `option` is not usable here: it renders no checkbox and accepts no\n * arbitrary markup, and it only lives inside `select`/`datalist`, which this\n * pattern deliberately replaces.\n *\n * The checkbox is purely presentational: the row owns the interaction, so\n * letting the checkbox handle its own would toggle the value twice.\n *\n * A click moves focus onto the row, so it also answers Enter and Space to stay\n * operable from the keyboard. Arrowing through the list is driven from the\n * search field, which keeps focus while the dropdown is open.\n */\nfunction OptionRow({\n option,\n optionId,\n selected,\n highlighted,\n onToggle,\n}: Readonly<OptionRowProps>) {\n const toggle = () => {\n if (option.disabled) return;\n onToggle(option.value);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n toggle();\n };\n\n return (\n <div // NOSONAR — ARIA combobox pattern, no native tag fits (see JSDoc)\n id={optionId}\n data-option\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled}\n tabIndex={-1}\n onClick={toggle}\n onKeyDown={handleKeyDown}\n className={cn(\n 'relative flex items-center gap-2 px-3 py-2.5 cursor-pointer transition-colors text-sm',\n selected && 'bg-primary-50',\n highlighted && !selected && 'bg-background-50',\n option.disabled && 'opacity-50 cursor-not-allowed pointer-events-none',\n !option.disabled &&\n !selected &&\n !highlighted &&\n 'hover:bg-background-50'\n )}\n >\n <CheckBox\n checked={selected}\n readOnly\n tabIndex={-1}\n size=\"small\"\n className=\"pointer-events-none\"\n />\n <Text size=\"sm\" className=\"flex-1 text-text-700\">\n {option.label}\n </Text>\n </div>\n );\n}\n","import Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { SpinnerGapIcon } from '@phosphor-icons/react/dist/csr/SpinnerGap';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport { TRIGGER_TEXT_SIZES } from './constants';\nimport type { MultiSearchSelectOption, MultiSearchSelectSize } from './types';\n\ninterface TriggerContentProps {\n selectedOptions: MultiSearchSelectOption[];\n placeholder: string;\n loading: boolean;\n loadingText: string;\n disabled: boolean;\n maxVisibleChips: number;\n size: MultiSearchSelectSize;\n onRemove: (value: string) => void;\n}\n\n/**\n * Renders what the closed trigger shows: a loading indicator, the placeholder,\n * or one removable chip per selected value.\n */\nexport function TriggerContent({\n selectedOptions,\n placeholder,\n loading,\n loadingText,\n disabled,\n maxVisibleChips,\n size,\n onRemove,\n}: Readonly<TriggerContentProps>) {\n const textSize = TRIGGER_TEXT_SIZES[size];\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2\">\n <SpinnerGapIcon size={14} className=\"animate-spin\" />\n <Text as=\"span\" size={textSize} className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n );\n }\n\n if (selectedOptions.length === 0) {\n return (\n <Text as=\"span\" size={textSize} className=\"block truncate text-text-500\">\n {placeholder}\n </Text>\n );\n }\n\n const visible = selectedOptions.slice(0, maxVisibleChips);\n const hiddenCount = selectedOptions.length - visible.length;\n\n return (\n <div className=\"flex flex-wrap items-center gap-1 py-0.5\">\n {visible.map((option) => (\n <ValueChip\n key={option.value}\n option={option}\n disabled={disabled}\n onRemove={onRemove}\n />\n ))}\n {hiddenCount > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"medium\"\n className=\"inline-flex items-center rounded-full border border-border-200 bg-background-50 px-2 py-0.5 text-text-700\"\n >\n +{hiddenCount}\n </Text>\n )}\n </div>\n );\n}\n\ninterface ValueChipProps {\n option: MultiSearchSelectOption;\n disabled: boolean;\n onRemove: (value: string) => void;\n}\n\n/**\n * A single selected value.\n *\n * Both the click and the keydown are stopped from bubbling so that removing a\n * value never reaches the trigger and toggles the dropdown open.\n */\nfunction ValueChip({ option, disabled, onRemove }: Readonly<ValueChipProps>) {\n return (\n <span\n data-chip={option.value}\n className=\"inline-flex max-w-[160px] items-center gap-1 rounded-full border border-primary-300 bg-primary-100 px-2 py-0.5 text-primary-900\"\n >\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"medium\"\n color=\"text-primary-900\"\n className=\"truncate\"\n >\n {option.label}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n className=\"shrink-0 cursor-pointer hover:text-indicator-error disabled:cursor-not-allowed\"\n onClick={(event) => {\n event.stopPropagation();\n onRemove(option.value);\n }}\n onKeyDown={(event) => event.stopPropagation()}\n >\n <XIcon size={12} weight=\"bold\" />\n </Button>\n </span>\n );\n}\n","import {\n useCallback,\n useEffect,\n useState,\n type CSSProperties,\n type RefObject,\n} from 'react';\nimport {\n DROPDOWN_GAP,\n DROPDOWN_MAX_HEIGHT,\n DROPDOWN_MIN_HEIGHT,\n} from './constants';\n\ninterface UseDropdownPositionParams {\n open: boolean;\n triggerRef: RefObject<HTMLElement | null>;\n}\n\ninterface UseDropdownPosition {\n /** Null until the trigger has been measured; the dropdown waits for it. */\n triggerRect: DOMRect | null;\n /** Re-measures the trigger, called when the dropdown is about to open. */\n measureTrigger: () => void;\n /** Fixed-position styles that keep the dropdown glued to the trigger. */\n dropdownStyles: CSSProperties;\n}\n\n/**\n * Positions a portalled dropdown against its trigger.\n *\n * The dropdown renders into `document.body`, so it cannot rely on the trigger's\n * layout. It is measured on open and re-measured on scroll and resize, and it\n * flips above the trigger when there is not enough room below.\n */\nexport function useDropdownPosition({\n open,\n triggerRef,\n}: UseDropdownPositionParams): UseDropdownPosition {\n const [triggerRect, setTriggerRect] = useState<DOMRect | null>(null);\n\n const measureTrigger = useCallback(() => {\n if (!triggerRef.current) return;\n setTriggerRect(triggerRef.current.getBoundingClientRect());\n }, [triggerRef]);\n\n /**\n * Scroll is listened to in the capture phase, so every nested scroll\n * container feeds this handler. Measuring once per frame is enough to keep\n * the panel glued to the trigger and avoids a layout read per event.\n */\n useEffect(() => {\n if (!open) return;\n\n let frame = 0;\n\n const handleUpdate = () => {\n if (frame) return;\n frame = requestAnimationFrame(() => {\n frame = 0;\n measureTrigger();\n });\n };\n\n window.addEventListener('scroll', handleUpdate, true);\n window.addEventListener('resize', handleUpdate);\n\n return () => {\n if (frame) cancelAnimationFrame(frame);\n window.removeEventListener('scroll', handleUpdate, true);\n window.removeEventListener('resize', handleUpdate);\n };\n }, [open, measureTrigger]);\n\n const dropdownStyles = buildDropdownStyles(triggerRect);\n\n return { triggerRect, measureTrigger, dropdownStyles };\n}\n\n/**\n * Turns a trigger rect into fixed-position styles, flipping the dropdown above\n * the trigger when the space below is too tight and the space above is larger.\n *\n * The available space is clamped to `DROPDOWN_MIN_HEIGHT`: a trigger scrolled\n * past either viewport edge yields a negative measurement, which would produce\n * an invalid `maxHeight` and collapse the dropdown.\n */\nfunction buildDropdownStyles(triggerRect: DOMRect | null): CSSProperties {\n if (!triggerRect) return {};\n\n const viewportHeight = window.innerHeight;\n const spaceBelow = viewportHeight - triggerRect.bottom - DROPDOWN_GAP;\n const spaceAbove = triggerRect.top - DROPDOWN_GAP;\n const openAbove = spaceBelow < DROPDOWN_MAX_HEIGHT && spaceAbove > spaceBelow;\n const availableSpace = openAbove ? spaceAbove : spaceBelow;\n\n const placement: CSSProperties = openAbove\n ? { bottom: viewportHeight - triggerRect.top + DROPDOWN_GAP }\n : { top: triggerRect.bottom + DROPDOWN_GAP };\n\n return {\n position: 'fixed',\n left: triggerRect.left,\n width: triggerRect.width,\n zIndex: 9999,\n maxHeight: clampDropdownHeight(availableSpace),\n ...placement,\n };\n}\n\n/** Keeps the dropdown between a usable floor and its documented ceiling. */\nfunction clampDropdownHeight(availableSpace: number): number {\n const usableSpace = Math.max(availableSpace, DROPDOWN_MIN_HEIGHT);\n return Math.min(usableSpace, DROPDOWN_MAX_HEIGHT);\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WZOA2PVJ.js","../src/components/MultiSearchSelect/MultiSearchSelect.tsx","../src/components/MultiSearchSelect/constants.ts","../src/components/MultiSearchSelect/OptionList.tsx","../src/components/MultiSearchSelect/TriggerContent.tsx","../src/components/MultiSearchSelect/useDropdownPosition.ts"],"names":["jsx"],"mappings":"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","file":"/home/runner/work/analytica-frontend-lib/analytica-frontend-lib/dist/chunk-WZOA2PVJ.js","sourcesContent":[null,"import {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { CaretDownIcon } from '@phosphor-icons/react/dist/csr/CaretDown';\nimport { MagnifyingGlassIcon } from '@phosphor-icons/react/dist/csr/MagnifyingGlass';\nimport Input from '../Input/Input';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport {\n LABEL_SIZE_CLASSES,\n OPEN_KEYS,\n PADDING_CLASSES,\n SIZE_CLASSES,\n VARIANT_CLASSES,\n} from './constants';\nimport { OptionList } from './OptionList';\nimport { TriggerContent } from './TriggerContent';\nimport { useDropdownPosition } from './useDropdownPosition';\nimport { useDebounce } from '../../hooks/useDebounce';\nimport type { MultiSearchSelectOption, MultiSearchSelectProps } from './types';\n\n/**\n * Searchable multi-select: the sibling of `SearchSelect` for picking several\n * values at once. Same sizing, borders, portalled dropdown and keyboard model,\n * with removable chips in the trigger and a checkbox per option.\n *\n * Filtering is local by default — pass an already loaded option list. For a\n * source too large to load at once, supply `onSearch` + `pagination` +\n * `onLoadMore` and set `filterLocally={false}`: the query then round-trips to\n * the server and the list grows by infinite scroll, exactly like `SearchSelect`.\n *\n * Follows the W3C ARIA combobox pattern (`combobox` -> `listbox` -> `option`),\n * the same one `SearchSelect` uses. No native element covers it: `select\n * multiple` and `datalist` support neither removable chips, nor an inline\n * search field, nor a checkbox per row.\n *\n * @example\n * ```tsx\n * <MultiSearchSelect\n * label=\"Turmas\"\n * values={classIds}\n * onValuesChange={setClassIds}\n * options={classes.map((c) => ({ value: c.id, label: c.name }))}\n * unknownValueLabel=\"Turma não encontrada\"\n * />\n * ```\n */\nexport function MultiSearchSelect({\n values,\n onValuesChange,\n options,\n label,\n placeholder = 'Selecione...',\n searchPlaceholder = 'Buscar...',\n emptyText = 'Nenhum resultado encontrado',\n loadingText = 'Carregando...',\n unknownValueLabel = 'Item não encontrado',\n helperText,\n errorMessage,\n disabled = false,\n loading = false,\n size = 'small',\n variant = 'outlined',\n className,\n id,\n maxVisibleChips = 3,\n onSearch,\n searchDebounce = 300,\n pagination,\n onLoadMore,\n loadingMore = false,\n filterLocally = true,\n}: Readonly<MultiSearchSelectProps>) {\n const [open, setOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n\n const triggerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const isFetchingMoreRef = useRef(false);\n\n const generatedId = useId();\n const selectId = id ?? `multi-search-select-${generatedId}`;\n const listboxId = `${selectId}-listbox`;\n const helperId = `${selectId}-helper`;\n const errorId = `${selectId}-error`;\n const labelId = `${selectId}-label`;\n\n const { triggerRect, measureTrigger, dropdownStyles } = useDropdownPosition({\n open,\n triggerRef,\n });\n\n const debouncedSearch = useDebounce(searchQuery, searchDebounce);\n\n useEffect(() => {\n if (onSearch) onSearch(debouncedSearch);\n }, [debouncedSearch, onSearch]);\n\n const filteredOptions = useMemo(\n () => (filterLocally ? filterOptions(options, searchQuery) : options),\n [options, searchQuery, filterLocally]\n );\n\n // Reset the in-flight guard once the caller reports the page landed.\n useEffect(() => {\n if (!loadingMore) isFetchingMoreRef.current = false;\n }, [loadingMore]);\n\n const handleScroll = useCallback(() => {\n if (\n !listRef.current ||\n !pagination?.hasNext ||\n loadingMore ||\n isFetchingMoreRef.current ||\n !onLoadMore\n )\n return;\n\n const { scrollTop, scrollHeight, clientHeight } = listRef.current;\n const scrollThreshold = 50;\n if (scrollHeight - scrollTop - clientHeight >= scrollThreshold) return;\n\n isFetchingMoreRef.current = true;\n const result = onLoadMore();\n // A promise resolves the guard itself; a void return leans on `loadingMore`.\n if (result && typeof result.then === 'function') {\n result.finally(() => {\n isFetchingMoreRef.current = false;\n });\n }\n }, [pagination?.hasNext, loadingMore, onLoadMore]);\n\n const selectedOptions = useMemo(\n () => resolveSelectedOptions(values, options, unknownValueLabel),\n [values, options, unknownValueLabel]\n );\n\n const interactive = !disabled && !loading;\n\n const closeDropdown = useCallback((restoreFocus = false) => {\n setOpen(false);\n setSearchQuery('');\n if (restoreFocus) triggerRef.current?.focus();\n }, []);\n\n const openDropdown = useCallback(() => {\n measureTrigger();\n setSearchQuery('');\n setHighlightedIndex(-1);\n setOpen(true);\n }, [measureTrigger]);\n\n const toggleOpen = useCallback(() => {\n if (!interactive) return;\n if (open) {\n closeDropdown();\n return;\n }\n openDropdown();\n }, [interactive, open, closeDropdown, openDropdown]);\n\n /**\n * Toggling deliberately keeps the dropdown and the search query alive: picks\n * usually come in runs of adjacent options.\n */\n const toggleValue = useCallback(\n (optionValue: string) => {\n const alreadySelected = values.includes(optionValue);\n const next = alreadySelected\n ? values.filter((value) => value !== optionValue)\n : [...values, optionValue];\n onValuesChange(next);\n },\n [values, onValuesChange]\n );\n\n const removeValue = useCallback(\n (optionValue: string) => {\n onValuesChange(values.filter((value) => value !== optionValue));\n },\n [values, onValuesChange]\n );\n\n useFocusOnOpen(open, searchInputRef);\n useCloseOnOutsideClick(open, closeDropdown, triggerRef, contentRef);\n useScrollHighlightIntoView(highlightedIndex, listRef);\n useClampHighlight(\n filteredOptions.length,\n highlightedIndex,\n setHighlightedIndex\n );\n\n const moveHighlight = useCallback(\n (direction: 'next' | 'prev') => {\n setHighlightedIndex((current) =>\n findNextEnabledIndex(filteredOptions, current, direction)\n );\n },\n [filteredOptions]\n );\n\n const toggleHighlighted = useCallback(() => {\n const option = filteredOptions[highlightedIndex];\n if (!option || option.disabled) return;\n toggleValue(option.value);\n }, [filteredOptions, highlightedIndex, toggleValue]);\n\n /** An empty query turns Backspace into \"drop the last chip\". */\n const canRemoveLastChip = searchQuery === '' && values.length > 0;\n\n const removeLastValue = useCallback(() => {\n onValuesChange(values.slice(0, -1));\n }, [values, onValuesChange]);\n\n /**\n * Dismisses the dropdown without letting Escape reach the document.\n *\n * Modals in the design system close on a document-level Escape listener, so a\n * bubbling key would discard the whole surrounding form.\n *\n * It is bound to every element that can hold focus while the dropdown is open\n * — the trigger, the search field and the listbox, which the option rows sit\n * inside — rather than to the panel wrapper, which carries no role.\n */\n const handleEscape = useCallback(\n (event: KeyboardEvent<HTMLElement>) => {\n if (event.key !== 'Escape') return;\n event.preventDefault();\n event.stopPropagation();\n closeDropdown(true);\n },\n [closeDropdown]\n );\n\n const handleSearchKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n moveHighlight('next');\n return;\n }\n if (event.key === 'ArrowUp') {\n event.preventDefault();\n moveHighlight('prev');\n return;\n }\n if (event.key === 'Enter') {\n event.preventDefault();\n toggleHighlighted();\n return;\n }\n if (event.key === 'Backspace' && canRemoveLastChip) {\n event.preventDefault();\n removeLastValue();\n return;\n }\n handleEscape(event);\n },\n [\n moveHighlight,\n toggleHighlighted,\n removeLastValue,\n canRemoveLastChip,\n handleEscape,\n ]\n );\n\n const handleTriggerKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (!interactive) return;\n\n if (OPEN_KEYS.includes(event.key) && !open) {\n event.preventDefault();\n openDropdown();\n return;\n }\n\n handleEscape(event);\n },\n [interactive, open, openDropdown, handleEscape]\n );\n\n const describedById = resolveDescribedBy({\n errorMessage,\n errorId,\n helperText,\n helperId,\n });\n\n const activeDescendant =\n highlightedIndex >= 0\n ? `${listboxId}-option-${highlightedIndex}`\n : undefined;\n\n const hasDescription = Boolean(helperText) || Boolean(errorMessage);\n\n const dropdown = open && triggerRect && (\n <div\n ref={contentRef}\n data-multi-search-select-id={selectId}\n style={dropdownStyles}\n className=\"bg-secondary rounded-md border border-border-100 shadow-lg overflow-hidden flex flex-col\"\n >\n <div className=\"p-2 border-b border-border-100\">\n <Input\n ref={searchInputRef}\n type=\"text\"\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-activedescendant={activeDescendant}\n value={searchQuery}\n onChange={(event) => setSearchQuery(event.target.value)}\n onKeyDown={handleSearchKeyDown}\n placeholder={searchPlaceholder}\n size=\"small\"\n variant=\"outlined\"\n iconRight={<MagnifyingGlassIcon size={16} />}\n containerClassName=\"w-full\"\n />\n </div>\n\n <div // NOSONAR — ARIA combobox pattern, no native tag fits (see JSDoc)\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable\n aria-label={label ?? 'Opções'}\n tabIndex={-1}\n onKeyDown={handleEscape}\n onScroll={handleScroll}\n className=\"flex-1 overflow-y-auto\"\n >\n <OptionList\n options={filteredOptions}\n selectedValues={values}\n highlightedIndex={highlightedIndex}\n listboxId={listboxId}\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n onToggle={toggleValue}\n loadingMore={loadingMore}\n pagination={pagination}\n localFilterActive={filterLocally && searchQuery !== ''}\n />\n </div>\n </div>\n );\n\n return (\n <div className={cn('w-full', className)}>\n {label && (\n <label\n id={labelId}\n htmlFor={selectId}\n className={cn(\n 'block font-bold text-text-900 mb-1.5',\n LABEL_SIZE_CLASSES[size]\n )}\n >\n {label}\n </label>\n )}\n\n {/*\n `htmlFor` alone would not name this trigger: a `label` only associates\n with labelable elements, which a `div` is not. `aria-labelledby` is what\n gives it an accessible name.\n */}\n <div\n ref={triggerRef}\n id={selectId}\n role=\"combobox\"\n tabIndex={interactive ? 0 : -1}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? listboxId : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-invalid={!!errorMessage}\n aria-describedby={describedById}\n aria-disabled={!interactive}\n onClick={toggleOpen}\n onKeyDown={handleTriggerKeyDown}\n className={cn(\n 'flex w-full items-center justify-between gap-2 border-border-300 bg-background',\n SIZE_CLASSES[size],\n PADDING_CLASSES[size],\n VARIANT_CLASSES[variant],\n errorMessage && 'border-indicator-error',\n interactive\n ? 'cursor-pointer hover:bg-background-50 text-text-700'\n : 'cursor-not-allowed opacity-50 text-text-400'\n )}\n >\n <div className=\"min-w-0 flex-1\">\n <TriggerContent\n selectedOptions={selectedOptions}\n placeholder={placeholder}\n loading={loading}\n loadingText={loadingText}\n disabled={disabled}\n maxVisibleChips={maxVisibleChips}\n size={size}\n onRemove={removeValue}\n />\n </div>\n <CaretDownIcon\n className={cn(\n 'h-4 w-4 opacity-50 transition-transform shrink-0',\n open && 'rotate-180'\n )}\n />\n </div>\n\n {hasDescription && (\n <div className=\"mt-1.5\">\n {helperText && !errorMessage && (\n <Text id={helperId} size=\"sm\" className=\"text-text-500\">\n {helperText}\n </Text>\n )}\n {errorMessage && (\n <Text id={errorId} size=\"sm\" className=\"text-indicator-error\">\n {errorMessage}\n </Text>\n )}\n </div>\n )}\n\n {typeof document !== 'undefined' && createPortal(dropdown, document.body)}\n </div>\n );\n}\n\n/** Case-insensitive local filter over the option captions. */\nfunction filterOptions(\n options: MultiSearchSelectOption[],\n searchQuery: string\n): MultiSearchSelectOption[] {\n if (!searchQuery) return options;\n const query = searchQuery.toLowerCase();\n return options.filter((option) => option.label.toLowerCase().includes(query));\n}\n\n/**\n * Maps every selected value to a renderable option.\n *\n * Values are never filtered against `options`: while the list is still loading,\n * or after an entry is deleted elsewhere, dropping the value would silently\n * discard a real binding. It falls back to a placeholder caption instead, so\n * the value stays visible and removable.\n */\nfunction resolveSelectedOptions(\n values: string[],\n options: MultiSearchSelectOption[],\n unknownValueLabel: string\n): MultiSearchSelectOption[] {\n return values.map(\n (value) =>\n options.find((option) => option.value === value) ?? {\n value,\n label: unknownValueLabel,\n }\n );\n}\n\n/**\n * Walks the option list in one direction and returns the first enabled index,\n * wrapping around. Returns -1 when every option is disabled or the list is\n * empty.\n */\nfunction findNextEnabledIndex(\n options: MultiSearchSelectOption[],\n currentIndex: number,\n direction: 'next' | 'prev'\n): number {\n const total = options.length;\n if (total === 0) return -1;\n\n const step = direction === 'next' ? 1 : -1;\n const unsetStart = direction === 'next' ? -1 : total;\n const startIndex = currentIndex >= 0 ? currentIndex : unsetStart;\n let index = startIndex;\n\n for (let attempt = 0; attempt < total; attempt++) {\n index = (index + step + total) % total;\n if (!options[index].disabled) return index;\n }\n\n return -1;\n}\n\ninterface DescribedByParams {\n errorMessage?: string;\n errorId: string;\n helperText?: string;\n helperId: string;\n}\n\n/** The error takes precedence over the hint as the field's description. */\nfunction resolveDescribedBy({\n errorMessage,\n errorId,\n helperText,\n helperId,\n}: DescribedByParams): string | undefined {\n if (errorMessage) return errorId;\n if (helperText) return helperId;\n return undefined;\n}\n\n/** Moves focus into the search field as soon as the dropdown opens. */\nfunction useFocusOnOpen(\n open: boolean,\n searchInputRef: RefObject<HTMLInputElement | null>\n) {\n useEffect(() => {\n if (!open) return;\n const timer = setTimeout(() => searchInputRef.current?.focus(), 0);\n return () => clearTimeout(timer);\n }, [open, searchInputRef]);\n}\n\n/**\n * Dismisses the dropdown on a click outside it.\n *\n * The trigger and the dropdown are checked separately because the dropdown is\n * portalled and therefore not a DOM descendant of the trigger.\n */\nfunction useCloseOnOutsideClick(\n open: boolean,\n close: () => void,\n triggerRef: RefObject<HTMLElement | null>,\n contentRef: RefObject<HTMLElement | null>\n) {\n useEffect(() => {\n if (!open) return;\n\n const handleMouseDown = (event: MouseEvent) => {\n const target = event.target as Node;\n const insideTrigger = triggerRef.current?.contains(target);\n const insideContent = contentRef.current?.contains(target);\n if (insideTrigger || insideContent) return;\n close();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [open, close, triggerRef, contentRef]);\n}\n\n/**\n * Pulls the highlight back into range when a search narrows the option list,\n * so `aria-activedescendant` never points at a row that no longer exists.\n */\nfunction useClampHighlight(\n optionCount: number,\n highlightedIndex: number,\n setHighlightedIndex: (index: number) => void\n) {\n useEffect(() => {\n if (highlightedIndex < optionCount) return;\n setHighlightedIndex(Math.max(optionCount - 1, -1));\n }, [optionCount, highlightedIndex, setHighlightedIndex]);\n}\n\n/** Keeps the keyboard-highlighted row visible in a long list. */\nfunction useScrollHighlightIntoView(\n highlightedIndex: number,\n listRef: RefObject<HTMLDivElement | null>\n) {\n useEffect(() => {\n if (highlightedIndex < 0 || !listRef.current) return;\n const rows = listRef.current.querySelectorAll('[data-option]');\n const row = rows[highlightedIndex] as HTMLElement | undefined;\n row?.scrollIntoView({ block: 'nearest' });\n }, [highlightedIndex, listRef]);\n}\n\nexport type {\n MultiSearchSelectOption,\n MultiSearchSelectPagination,\n MultiSearchSelectProps,\n MultiSearchSelectSize,\n MultiSearchSelectVariant,\n} from './types';\n\nexport default MultiSearchSelect;\n","import type { MultiSearchSelectSize, MultiSearchSelectVariant } from './types';\n\n/**\n * Trigger height per size.\n *\n * Uses `min-h-*` where `SearchSelect` uses a fixed `h-*`: chips wrap onto more\n * than one line once several values are selected.\n */\nexport const SIZE_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'text-sm min-h-8',\n medium: 'text-md min-h-9',\n large: 'text-lg min-h-10',\n};\n\nexport const PADDING_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'px-3 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n};\n\nexport const VARIANT_CLASSES: Record<MultiSearchSelectVariant, string> = {\n outlined: 'border-2 rounded-lg focus:border-primary-950',\n underlined: 'border-b-2 focus:border-primary-950',\n rounded: 'border-2 rounded-full focus:border-primary-950',\n};\n\n/** `Text` size that matches the trigger font for each select size. */\nexport const TRIGGER_TEXT_SIZES: Record<\n MultiSearchSelectSize,\n 'sm' | 'md' | 'lg'\n> = {\n small: 'sm',\n medium: 'md',\n large: 'lg',\n};\n\nexport const LABEL_SIZE_CLASSES: Record<MultiSearchSelectSize, string> = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n};\n\n/** Keys that open the dropdown while the trigger has focus. */\nexport const OPEN_KEYS = ['Enter', ' ', 'ArrowDown'];\n\n/** Matches the dropdown's `max-h`, used to decide whether it flips above. */\nexport const DROPDOWN_MAX_HEIGHT = 300;\n\n/**\n * Floor for the dropdown height, enough for the search field plus a couple of\n * rows. Guards against a trigger scrolled past a viewport edge, where the\n * measured space turns negative.\n */\nexport const DROPDOWN_MIN_HEIGHT = 120;\n\n/** Distance between the trigger and the dropdown. */\nexport const DROPDOWN_GAP = 4;\n","import type { KeyboardEvent } from 'react';\nimport Text from '../Text/Text';\nimport CheckBox from '../CheckBox/CheckBox';\nimport { cn } from '../../utils/utils';\nimport { SpinnerGapIcon } from '@phosphor-icons/react/dist/csr/SpinnerGap';\nimport type {\n MultiSearchSelectOption,\n MultiSearchSelectPagination,\n} from './types';\n\ninterface OptionListProps {\n options: MultiSearchSelectOption[];\n selectedValues: string[];\n highlightedIndex: number;\n listboxId: string;\n loading: boolean;\n loadingText: string;\n emptyText: string;\n onToggle: (value: string) => void;\n /** True while the next page is in flight; renders a footer spinner. */\n loadingMore?: boolean;\n /** Page state of the option source; renders a \"N de M\" footer. */\n pagination?: MultiSearchSelectPagination;\n /** True while a local filter is narrowing the list, which hides the footer. */\n localFilterActive?: boolean;\n}\n\n/**\n * The dropdown body: a loading state, an empty state, or the option rows.\n */\nexport function OptionList({\n options,\n selectedValues,\n highlightedIndex,\n listboxId,\n loading,\n loadingText,\n emptyText,\n onToggle,\n loadingMore = false,\n pagination,\n localFilterActive = false,\n}: Readonly<OptionListProps>) {\n if (loading) {\n return (\n <div className=\"flex items-center justify-center gap-2 p-4 text-text-500\">\n <SpinnerGapIcon size={18} className=\"animate-spin\" />\n <Text size=\"sm\" className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n );\n }\n\n if (options.length === 0) {\n return (\n <Text size=\"sm\" className=\"p-4 text-center text-text-500\">\n {emptyText}\n </Text>\n );\n }\n\n // The \"N de M\" footer counts the loaded options against the server total, so\n // it is meaningless while a local filter is showing a subset of them.\n const showPaginationFooter =\n Boolean(pagination) &&\n !loadingMore &&\n (pagination?.total ?? 0) > 0 &&\n !localFilterActive;\n\n return (\n <>\n {options.map((option, index) => (\n <OptionRow\n key={option.value}\n option={option}\n optionId={`${listboxId}-option-${index}`}\n selected={selectedValues.includes(option.value)}\n highlighted={index === highlightedIndex}\n onToggle={onToggle}\n />\n ))}\n\n {loadingMore && (\n <div className=\"flex items-center justify-center gap-2 p-3 text-text-500 border-t border-border-100\">\n <SpinnerGapIcon size={16} className=\"animate-spin\" />\n <Text size=\"xs\" className=\"text-text-500\">\n Carregando mais...\n </Text>\n </div>\n )}\n\n {showPaginationFooter && (\n <Text\n size=\"xs\"\n className=\"px-3 py-2 text-text-400 border-t border-border-100 text-center\"\n >\n {options.length} de {pagination?.total} itens\n </Text>\n )}\n </>\n );\n}\n\ninterface OptionRowProps {\n option: MultiSearchSelectOption;\n optionId: string;\n selected: boolean;\n highlighted: boolean;\n onToggle: (value: string) => void;\n}\n\n/**\n * One selectable row of the ARIA combobox listbox.\n *\n * A native `option` is not usable here: it renders no checkbox and accepts no\n * arbitrary markup, and it only lives inside `select`/`datalist`, which this\n * pattern deliberately replaces.\n *\n * The checkbox is purely presentational: the row owns the interaction, so\n * letting the checkbox handle its own would toggle the value twice.\n *\n * A click moves focus onto the row, so it also answers Enter and Space to stay\n * operable from the keyboard. Arrowing through the list is driven from the\n * search field, which keeps focus while the dropdown is open.\n */\nfunction OptionRow({\n option,\n optionId,\n selected,\n highlighted,\n onToggle,\n}: Readonly<OptionRowProps>) {\n const toggle = () => {\n if (option.disabled) return;\n onToggle(option.value);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n toggle();\n };\n\n return (\n <div // NOSONAR — ARIA combobox pattern, no native tag fits (see JSDoc)\n id={optionId}\n data-option\n role=\"option\"\n aria-selected={selected}\n aria-disabled={option.disabled}\n tabIndex={-1}\n onClick={toggle}\n onKeyDown={handleKeyDown}\n className={cn(\n 'relative flex items-center gap-2 px-3 py-2.5 cursor-pointer transition-colors text-sm',\n selected && 'bg-primary-50',\n highlighted && !selected && 'bg-background-50',\n option.disabled && 'opacity-50 cursor-not-allowed pointer-events-none',\n !option.disabled &&\n !selected &&\n !highlighted &&\n 'hover:bg-background-50'\n )}\n >\n <CheckBox\n checked={selected}\n readOnly\n tabIndex={-1}\n size=\"small\"\n className=\"pointer-events-none\"\n />\n <Text size=\"sm\" className=\"flex-1 text-text-700\">\n {option.label}\n </Text>\n </div>\n );\n}\n","import Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport { SpinnerGapIcon } from '@phosphor-icons/react/dist/csr/SpinnerGap';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\nimport { TRIGGER_TEXT_SIZES } from './constants';\nimport type { MultiSearchSelectOption, MultiSearchSelectSize } from './types';\n\ninterface TriggerContentProps {\n selectedOptions: MultiSearchSelectOption[];\n placeholder: string;\n loading: boolean;\n loadingText: string;\n disabled: boolean;\n maxVisibleChips: number;\n size: MultiSearchSelectSize;\n onRemove: (value: string) => void;\n}\n\n/**\n * Renders what the closed trigger shows: a loading indicator, the placeholder,\n * or one removable chip per selected value.\n */\nexport function TriggerContent({\n selectedOptions,\n placeholder,\n loading,\n loadingText,\n disabled,\n maxVisibleChips,\n size,\n onRemove,\n}: Readonly<TriggerContentProps>) {\n const textSize = TRIGGER_TEXT_SIZES[size];\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2\">\n <SpinnerGapIcon size={14} className=\"animate-spin\" />\n <Text as=\"span\" size={textSize} className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n );\n }\n\n if (selectedOptions.length === 0) {\n return (\n <Text as=\"span\" size={textSize} className=\"block truncate text-text-500\">\n {placeholder}\n </Text>\n );\n }\n\n const visible = selectedOptions.slice(0, maxVisibleChips);\n const hiddenCount = selectedOptions.length - visible.length;\n\n return (\n <div className=\"flex flex-wrap items-center gap-1 py-0.5\">\n {visible.map((option) => (\n <ValueChip\n key={option.value}\n option={option}\n disabled={disabled}\n onRemove={onRemove}\n />\n ))}\n {hiddenCount > 0 && (\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"medium\"\n className=\"inline-flex items-center rounded-full border border-border-200 bg-background-50 px-2 py-0.5 text-text-700\"\n >\n +{hiddenCount}\n </Text>\n )}\n </div>\n );\n}\n\ninterface ValueChipProps {\n option: MultiSearchSelectOption;\n disabled: boolean;\n onRemove: (value: string) => void;\n}\n\n/**\n * A single selected value.\n *\n * Both the click and the keydown are stopped from bubbling so that removing a\n * value never reaches the trigger and toggles the dropdown open.\n */\nfunction ValueChip({ option, disabled, onRemove }: Readonly<ValueChipProps>) {\n return (\n <span\n data-chip={option.value}\n className=\"inline-flex max-w-[160px] items-center gap-1 rounded-full border border-primary-300 bg-primary-100 px-2 py-0.5 text-primary-900\"\n >\n <Text\n as=\"span\"\n size=\"xs\"\n weight=\"medium\"\n color=\"text-primary-900\"\n className=\"truncate\"\n >\n {option.label}\n </Text>\n <Button\n variant=\"raw\"\n aria-label={`Remover ${option.label}`}\n disabled={disabled}\n className=\"shrink-0 cursor-pointer hover:text-indicator-error disabled:cursor-not-allowed\"\n onClick={(event) => {\n event.stopPropagation();\n onRemove(option.value);\n }}\n onKeyDown={(event) => event.stopPropagation()}\n >\n <XIcon size={12} weight=\"bold\" />\n </Button>\n </span>\n );\n}\n","import {\n useCallback,\n useEffect,\n useState,\n type CSSProperties,\n type RefObject,\n} from 'react';\nimport {\n DROPDOWN_GAP,\n DROPDOWN_MAX_HEIGHT,\n DROPDOWN_MIN_HEIGHT,\n} from './constants';\n\ninterface UseDropdownPositionParams {\n open: boolean;\n triggerRef: RefObject<HTMLElement | null>;\n}\n\ninterface UseDropdownPosition {\n /** Null until the trigger has been measured; the dropdown waits for it. */\n triggerRect: DOMRect | null;\n /** Re-measures the trigger, called when the dropdown is about to open. */\n measureTrigger: () => void;\n /** Fixed-position styles that keep the dropdown glued to the trigger. */\n dropdownStyles: CSSProperties;\n}\n\n/**\n * Positions a portalled dropdown against its trigger.\n *\n * The dropdown renders into `document.body`, so it cannot rely on the trigger's\n * layout. It is measured on open and re-measured on scroll and resize, and it\n * flips above the trigger when there is not enough room below.\n */\nexport function useDropdownPosition({\n open,\n triggerRef,\n}: UseDropdownPositionParams): UseDropdownPosition {\n const [triggerRect, setTriggerRect] = useState<DOMRect | null>(null);\n\n const measureTrigger = useCallback(() => {\n if (!triggerRef.current) return;\n setTriggerRect(triggerRef.current.getBoundingClientRect());\n }, [triggerRef]);\n\n /**\n * Scroll is listened to in the capture phase, so every nested scroll\n * container feeds this handler. Measuring once per frame is enough to keep\n * the panel glued to the trigger and avoids a layout read per event.\n */\n useEffect(() => {\n if (!open) return;\n\n let frame = 0;\n\n const handleUpdate = () => {\n if (frame) return;\n frame = requestAnimationFrame(() => {\n frame = 0;\n measureTrigger();\n });\n };\n\n window.addEventListener('scroll', handleUpdate, true);\n window.addEventListener('resize', handleUpdate);\n\n return () => {\n if (frame) cancelAnimationFrame(frame);\n window.removeEventListener('scroll', handleUpdate, true);\n window.removeEventListener('resize', handleUpdate);\n };\n }, [open, measureTrigger]);\n\n const dropdownStyles = buildDropdownStyles(triggerRect);\n\n return { triggerRect, measureTrigger, dropdownStyles };\n}\n\n/**\n * Turns a trigger rect into fixed-position styles, flipping the dropdown above\n * the trigger when the space below is too tight and the space above is larger.\n *\n * The available space is clamped to `DROPDOWN_MIN_HEIGHT`: a trigger scrolled\n * past either viewport edge yields a negative measurement, which would produce\n * an invalid `maxHeight` and collapse the dropdown.\n */\nfunction buildDropdownStyles(triggerRect: DOMRect | null): CSSProperties {\n if (!triggerRect) return {};\n\n const viewportHeight = window.innerHeight;\n const spaceBelow = viewportHeight - triggerRect.bottom - DROPDOWN_GAP;\n const spaceAbove = triggerRect.top - DROPDOWN_GAP;\n const openAbove = spaceBelow < DROPDOWN_MAX_HEIGHT && spaceAbove > spaceBelow;\n const availableSpace = openAbove ? spaceAbove : spaceBelow;\n\n const placement: CSSProperties = openAbove\n ? { bottom: viewportHeight - triggerRect.top + DROPDOWN_GAP }\n : { top: triggerRect.bottom + DROPDOWN_GAP };\n\n return {\n position: 'fixed',\n left: triggerRect.left,\n width: triggerRect.width,\n zIndex: 9999,\n maxHeight: clampDropdownHeight(availableSpace),\n ...placement,\n };\n}\n\n/** Keeps the dropdown between a usable floor and its documented ceiling. */\nfunction clampDropdownHeight(availableSpace: number): number {\n const usableSpace = Math.max(availableSpace, DROPDOWN_MIN_HEIGHT);\n return Math.min(usableSpace, DROPDOWN_MAX_HEIGHT);\n}\n"]}
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-ZRBIE5ZM.mjs";
14
14
  import {
15
15
  Modal_default
16
- } from "./chunk-XZUMD3CB.mjs";
16
+ } from "./chunk-LODDYDTP.mjs";
17
17
  import {
18
18
  Button_default
19
19
  } from "./chunk-ZQZBOTZJ.mjs";
@@ -791,4 +791,4 @@ export {
791
791
  Chat,
792
792
  Chat_default
793
793
  };
794
- //# sourceMappingURL=chunk-TZKR5Z4J.mjs.map
794
+ //# sourceMappingURL=chunk-XJF2F36N.mjs.map
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-N26PFH64.mjs";
11
11
  import {
12
12
  CheckBox_default
13
- } from "./chunk-VDUO65VK.mjs";
13
+ } from "./chunk-3EGJJDIM.mjs";
14
14
  import {
15
15
  Text_default
16
16
  } from "./chunk-IMCIR6TJ.mjs";
@@ -341,4 +341,4 @@ export {
341
341
  CheckboxList_default,
342
342
  MultipleChoiceList
343
343
  };
344
- //# sourceMappingURL=chunk-P6ACVNQK.mjs.map
344
+ //# sourceMappingURL=chunk-YD2XBEKO.mjs.map
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkQWMFWYIIjs = require('./chunk-QWMFWYII.js');
3
+ var _chunk7IZNLARXjs = require('./chunk-7IZNLARX.js');
4
4
 
5
5
 
6
6
  var _chunkCAQ2Q3ONjs = require('./chunk-CAQ2Q3ON.js');
@@ -235,7 +235,7 @@ var DownloadButton = ({
235
235
  }
236
236
  ),
237
237
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
238
- _chunkQWMFWYIIjs.ProgressModal_default,
238
+ _chunk7IZNLARXjs.ProgressModal_default,
239
239
  {
240
240
  isOpen: isDownloading,
241
241
  onClose: () => {
@@ -251,4 +251,4 @@ var DownloadButton_default = DownloadButton;
251
251
 
252
252
 
253
253
  exports.DownloadButton_default = DownloadButton_default;
254
- //# sourceMappingURL=chunk-LMXWIPGR.js.map
254
+ //# sourceMappingURL=chunk-YWPLHFAK.js.map