tempest-react-sdk 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. package/README.md +14 -1
  2. package/bin/lib/alias/collect.mjs +94 -0
  3. package/bin/lib/alias/collect.test.mjs +95 -0
  4. package/bin/lib/alias/discover.mjs +77 -0
  5. package/bin/lib/alias/discover.test.mjs +189 -0
  6. package/bin/lib/alias/index.mjs +88 -0
  7. package/bin/lib/alias/index.test.mjs +138 -0
  8. package/bin/lib/alias/rewrite-css.mjs +76 -0
  9. package/bin/lib/alias/rewrite-css.test.mjs +85 -0
  10. package/bin/lib/alias/rewrite.mjs +141 -0
  11. package/bin/lib/alias/rewrite.test.mjs +148 -0
  12. package/bin/lib/alias/specifier.mjs +49 -0
  13. package/bin/lib/alias/specifier.test.mjs +85 -0
  14. package/bin/lib/alias/tsconfig.mjs +116 -0
  15. package/bin/lib/alias/typescript.mjs +23 -0
  16. package/bin/lib/doctor/lucide.mjs +74 -0
  17. package/bin/lib/doctor/lucide.test.mjs +107 -0
  18. package/bin/lib/icons/generate.mjs +156 -0
  19. package/bin/lib/icons/generate.test.mjs +125 -0
  20. package/bin/tempest.mjs +174 -13
  21. package/dist/audio/use-audio.cjs +1 -1
  22. package/dist/audio/use-audio.cjs.map +1 -1
  23. package/dist/audio/use-audio.js +12 -12
  24. package/dist/audio/use-audio.js.map +1 -1
  25. package/dist/charts/palette.cjs +1 -1
  26. package/dist/charts/palette.cjs.map +1 -1
  27. package/dist/charts/palette.js +2 -2
  28. package/dist/charts/palette.js.map +1 -1
  29. package/dist/charts.d.ts +5 -3
  30. package/dist/components/ClickOutside/ClickOutside.cjs +1 -1
  31. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  32. package/dist/components/ClickOutside/ClickOutside.js +3 -1
  33. package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
  34. package/dist/components/DataTable/DataTable.cjs +1 -1
  35. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  36. package/dist/components/DataTable/DataTable.js +21 -23
  37. package/dist/components/DataTable/DataTable.js.map +1 -1
  38. package/dist/components/Kanban/Kanban.cjs +2 -0
  39. package/dist/components/Kanban/Kanban.cjs.map +1 -0
  40. package/dist/components/Kanban/Kanban.js +102 -0
  41. package/dist/components/Kanban/Kanban.js.map +1 -0
  42. package/dist/components/Kanban/Kanban.module.cjs +2 -0
  43. package/dist/components/Kanban/Kanban.module.cjs.map +1 -0
  44. package/dist/components/Kanban/Kanban.module.js +19 -0
  45. package/dist/components/Kanban/Kanban.module.js.map +1 -0
  46. package/dist/components/NotificationCenter/NotificationCenter.cjs +2 -0
  47. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -0
  48. package/dist/components/NotificationCenter/NotificationCenter.js +83 -0
  49. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -0
  50. package/dist/components/NotificationCenter/NotificationCenter.module.cjs +2 -0
  51. package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -0
  52. package/dist/components/NotificationCenter/NotificationCenter.module.js +21 -0
  53. package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -0
  54. package/dist/components/NotificationCenter/use-notification-inbox.cjs +2 -0
  55. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -0
  56. package/dist/components/NotificationCenter/use-notification-inbox.js +63 -0
  57. package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -0
  58. package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
  59. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  60. package/dist/components/RefreshIndicator/RefreshIndicator.js +17 -17
  61. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  62. package/dist/components/Tooltip/Tooltip.cjs +1 -1
  63. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  64. package/dist/components/Tooltip/Tooltip.js +26 -20
  65. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  66. package/dist/components/VirtualTable/VirtualTable.cjs +2 -0
  67. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -0
  68. package/dist/components/VirtualTable/VirtualTable.js +116 -0
  69. package/dist/components/VirtualTable/VirtualTable.js.map +1 -0
  70. package/dist/components/VirtualTable/VirtualTable.module.cjs +2 -0
  71. package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -0
  72. package/dist/components/VirtualTable/VirtualTable.module.js +20 -0
  73. package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -0
  74. package/dist/components/Wizard/Wizard.cjs +1 -1
  75. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  76. package/dist/components/Wizard/Wizard.js +53 -53
  77. package/dist/components/Wizard/Wizard.js.map +1 -1
  78. package/dist/geo/use-position-tracker.cjs +1 -1
  79. package/dist/geo/use-position-tracker.cjs.map +1 -1
  80. package/dist/geo/use-position-tracker.js +3 -1
  81. package/dist/geo/use-position-tracker.js.map +1 -1
  82. package/dist/hooks/use-async.cjs +1 -1
  83. package/dist/hooks/use-async.cjs.map +1 -1
  84. package/dist/hooks/use-async.js +3 -1
  85. package/dist/hooks/use-async.js.map +1 -1
  86. package/dist/hooks/use-click-outside.cjs +1 -1
  87. package/dist/hooks/use-click-outside.cjs.map +1 -1
  88. package/dist/hooks/use-click-outside.js +3 -1
  89. package/dist/hooks/use-click-outside.js.map +1 -1
  90. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  91. package/dist/hooks/use-deep-memo.js.map +1 -1
  92. package/dist/hooks/use-event-listener.cjs +1 -1
  93. package/dist/hooks/use-event-listener.cjs.map +1 -1
  94. package/dist/hooks/use-event-listener.js +3 -1
  95. package/dist/hooks/use-event-listener.js.map +1 -1
  96. package/dist/hooks/use-interval.cjs +1 -1
  97. package/dist/hooks/use-interval.cjs.map +1 -1
  98. package/dist/hooks/use-interval.js +3 -1
  99. package/dist/hooks/use-interval.js.map +1 -1
  100. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  101. package/dist/hooks/use-is-first-render.js.map +1 -1
  102. package/dist/hooks/use-long-press-handlers.cjs +1 -1
  103. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  104. package/dist/hooks/use-long-press-handlers.js +25 -23
  105. package/dist/hooks/use-long-press-handlers.js.map +1 -1
  106. package/dist/hooks/use-long-press.cjs +1 -1
  107. package/dist/hooks/use-long-press.cjs.map +1 -1
  108. package/dist/hooks/use-long-press.js +3 -1
  109. package/dist/hooks/use-long-press.js.map +1 -1
  110. package/dist/hooks/use-online.cjs +1 -1
  111. package/dist/hooks/use-online.cjs.map +1 -1
  112. package/dist/hooks/use-online.js +6 -4
  113. package/dist/hooks/use-online.js.map +1 -1
  114. package/dist/hooks/use-previous.cjs.map +1 -1
  115. package/dist/hooks/use-previous.js.map +1 -1
  116. package/dist/hooks/use-service-worker-update.cjs +1 -1
  117. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  118. package/dist/hooks/use-service-worker-update.js +3 -1
  119. package/dist/hooks/use-service-worker-update.js.map +1 -1
  120. package/dist/hooks/use-sortable.cjs +2 -0
  121. package/dist/hooks/use-sortable.cjs.map +1 -0
  122. package/dist/hooks/use-sortable.js +169 -0
  123. package/dist/hooks/use-sortable.js.map +1 -0
  124. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  125. package/dist/hooks/use-stable-callback.js.map +1 -1
  126. package/dist/hooks/use-timeout.cjs +1 -1
  127. package/dist/hooks/use-timeout.cjs.map +1 -1
  128. package/dist/hooks/use-timeout.js +3 -1
  129. package/dist/hooks/use-timeout.js.map +1 -1
  130. package/dist/icons/Icon.cjs +2 -0
  131. package/dist/icons/Icon.cjs.map +1 -0
  132. package/dist/icons/Icon.js +24 -0
  133. package/dist/icons/Icon.js.map +1 -0
  134. package/dist/icons/IconProvider.cjs +2 -0
  135. package/dist/icons/IconProvider.cjs.map +1 -0
  136. package/dist/icons/IconProvider.js +24 -0
  137. package/dist/icons/IconProvider.js.map +1 -0
  138. package/dist/icons/generated/aliases.cjs +2 -0
  139. package/dist/icons/generated/aliases.cjs.map +1 -0
  140. package/dist/icons/generated/aliases.js +255 -0
  141. package/dist/icons/generated/aliases.js.map +1 -0
  142. package/dist/icons/generated/icon-names.cjs +2 -0
  143. package/dist/icons/generated/icon-names.cjs.map +1 -0
  144. package/dist/icons/generated/icon-names.js +6 -0
  145. package/dist/icons/generated/icon-names.js.map +1 -0
  146. package/dist/icons/generated/loaders.cjs +2 -0
  147. package/dist/icons/generated/loaders.cjs.map +1 -0
  148. package/dist/icons/generated/loaders.js +32 -0
  149. package/dist/icons/generated/loaders.js.map +1 -0
  150. package/dist/icons/generated/shard-a.cjs +2 -0
  151. package/dist/icons/generated/shard-a.cjs.map +1 -0
  152. package/dist/icons/generated/shard-a.js +114 -0
  153. package/dist/icons/generated/shard-a.js.map +1 -0
  154. package/dist/icons/generated/shard-b.cjs +2 -0
  155. package/dist/icons/generated/shard-b.cjs.map +1 -0
  156. package/dist/icons/generated/shard-b.js +156 -0
  157. package/dist/icons/generated/shard-b.js.map +1 -0
  158. package/dist/icons/generated/shard-c.cjs +2 -0
  159. package/dist/icons/generated/shard-c.cjs.map +1 -0
  160. package/dist/icons/generated/shard-c.js +269 -0
  161. package/dist/icons/generated/shard-c.js.map +1 -0
  162. package/dist/icons/generated/shard-d.cjs +2 -0
  163. package/dist/icons/generated/shard-d.cjs.map +1 -0
  164. package/dist/icons/generated/shard-d.js +62 -0
  165. package/dist/icons/generated/shard-d.js.map +1 -0
  166. package/dist/icons/generated/shard-e.cjs +2 -0
  167. package/dist/icons/generated/shard-e.cjs.map +1 -0
  168. package/dist/icons/generated/shard-e.js +32 -0
  169. package/dist/icons/generated/shard-e.js.map +1 -0
  170. package/dist/icons/generated/shard-f.cjs +2 -0
  171. package/dist/icons/generated/shard-f.cjs.map +1 -0
  172. package/dist/icons/generated/shard-f.js +139 -0
  173. package/dist/icons/generated/shard-f.js.map +1 -0
  174. package/dist/icons/generated/shard-g.cjs +2 -0
  175. package/dist/icons/generated/shard-g.cjs.map +1 -0
  176. package/dist/icons/generated/shard-g.js +61 -0
  177. package/dist/icons/generated/shard-g.js.map +1 -0
  178. package/dist/icons/generated/shard-h.cjs +2 -0
  179. package/dist/icons/generated/shard-h.cjs.map +1 -0
  180. package/dist/icons/generated/shard-h.js +63 -0
  181. package/dist/icons/generated/shard-h.js.map +1 -0
  182. package/dist/icons/generated/shard-i.cjs +2 -0
  183. package/dist/icons/generated/shard-i.cjs.map +1 -0
  184. package/dist/icons/generated/shard-i.js +30 -0
  185. package/dist/icons/generated/shard-i.js.map +1 -0
  186. package/dist/icons/generated/shard-j.cjs +2 -0
  187. package/dist/icons/generated/shard-j.cjs.map +1 -0
  188. package/dist/icons/generated/shard-j.js +10 -0
  189. package/dist/icons/generated/shard-j.js.map +1 -0
  190. package/dist/icons/generated/shard-k.cjs +2 -0
  191. package/dist/icons/generated/shard-k.cjs.map +1 -0
  192. package/dist/icons/generated/shard-k.js +16 -0
  193. package/dist/icons/generated/shard-k.js.map +1 -0
  194. package/dist/icons/generated/shard-l.cjs +2 -0
  195. package/dist/icons/generated/shard-l.cjs.map +1 -0
  196. package/dist/icons/generated/shard-l.js +88 -0
  197. package/dist/icons/generated/shard-l.js.map +1 -0
  198. package/dist/icons/generated/shard-m.cjs +2 -0
  199. package/dist/icons/generated/shard-m.cjs.map +1 -0
  200. package/dist/icons/generated/shard-m.js +140 -0
  201. package/dist/icons/generated/shard-m.js.map +1 -0
  202. package/dist/icons/generated/shard-n.cjs +2 -0
  203. package/dist/icons/generated/shard-n.cjs.map +1 -0
  204. package/dist/icons/generated/shard-n.js +24 -0
  205. package/dist/icons/generated/shard-n.js.map +1 -0
  206. package/dist/icons/generated/shard-o.cjs +2 -0
  207. package/dist/icons/generated/shard-o.cjs.map +1 -0
  208. package/dist/icons/generated/shard-o.js +17 -0
  209. package/dist/icons/generated/shard-o.js.map +1 -0
  210. package/dist/icons/generated/shard-p.cjs +2 -0
  211. package/dist/icons/generated/shard-p.cjs.map +1 -0
  212. package/dist/icons/generated/shard-p.js +115 -0
  213. package/dist/icons/generated/shard-p.js.map +1 -0
  214. package/dist/icons/generated/shard-q.cjs +2 -0
  215. package/dist/icons/generated/shard-q.cjs.map +1 -0
  216. package/dist/icons/generated/shard-q.js +10 -0
  217. package/dist/icons/generated/shard-q.js.map +1 -0
  218. package/dist/icons/generated/shard-r.cjs +2 -0
  219. package/dist/icons/generated/shard-r.cjs.map +1 -0
  220. package/dist/icons/generated/shard-r.js +77 -0
  221. package/dist/icons/generated/shard-r.js.map +1 -0
  222. package/dist/icons/generated/shard-s.cjs +2 -0
  223. package/dist/icons/generated/shard-s.cjs.map +1 -0
  224. package/dist/icons/generated/shard-s.js +255 -0
  225. package/dist/icons/generated/shard-s.js.map +1 -0
  226. package/dist/icons/generated/shard-t.cjs +2 -0
  227. package/dist/icons/generated/shard-t.cjs.map +1 -0
  228. package/dist/icons/generated/shard-t.js +109 -0
  229. package/dist/icons/generated/shard-t.js.map +1 -0
  230. package/dist/icons/generated/shard-u.cjs +2 -0
  231. package/dist/icons/generated/shard-u.cjs.map +1 -0
  232. package/dist/icons/generated/shard-u.js +49 -0
  233. package/dist/icons/generated/shard-u.js.map +1 -0
  234. package/dist/icons/generated/shard-v.cjs +2 -0
  235. package/dist/icons/generated/shard-v.cjs.map +1 -0
  236. package/dist/icons/generated/shard-v.js +30 -0
  237. package/dist/icons/generated/shard-v.js.map +1 -0
  238. package/dist/icons/generated/shard-w.cjs +2 -0
  239. package/dist/icons/generated/shard-w.cjs.map +1 -0
  240. package/dist/icons/generated/shard-w.js +48 -0
  241. package/dist/icons/generated/shard-w.js.map +1 -0
  242. package/dist/icons/generated/shard-x.cjs +2 -0
  243. package/dist/icons/generated/shard-x.cjs.map +1 -0
  244. package/dist/icons/generated/shard-x.js +10 -0
  245. package/dist/icons/generated/shard-x.js.map +1 -0
  246. package/dist/icons/generated/shard-z.cjs +2 -0
  247. package/dist/icons/generated/shard-z.cjs.map +1 -0
  248. package/dist/icons/generated/shard-z.js +25 -0
  249. package/dist/icons/generated/shard-z.js.map +1 -0
  250. package/dist/icons/icon-context.cjs +2 -0
  251. package/dist/icons/icon-context.cjs.map +1 -0
  252. package/dist/icons/icon-context.js +13 -0
  253. package/dist/icons/icon-context.js.map +1 -0
  254. package/dist/icons/is-icon-name.cjs +2 -0
  255. package/dist/icons/is-icon-name.cjs.map +1 -0
  256. package/dist/icons/is-icon-name.js +10 -0
  257. package/dist/icons/is-icon-name.js.map +1 -0
  258. package/dist/icons/shard-cache.cjs +2 -0
  259. package/dist/icons/shard-cache.cjs.map +1 -0
  260. package/dist/icons/shard-cache.js +41 -0
  261. package/dist/icons/shard-cache.js.map +1 -0
  262. package/dist/icons/use-icon.cjs +2 -0
  263. package/dist/icons/use-icon.cjs.map +1 -0
  264. package/dist/icons/use-icon.js +20 -0
  265. package/dist/icons/use-icon.js.map +1 -0
  266. package/dist/icons-virtual.d.ts +22 -0
  267. package/dist/icons.cjs +1 -0
  268. package/dist/icons.d.ts +220 -0
  269. package/dist/icons.js +9 -0
  270. package/dist/offline/use-offline-sync.cjs +1 -1
  271. package/dist/offline/use-offline-sync.cjs.map +1 -1
  272. package/dist/offline/use-offline-sync.js +3 -1
  273. package/dist/offline/use-offline-sync.js.map +1 -1
  274. package/dist/push/use-push-subscription.cjs +1 -1
  275. package/dist/push/use-push-subscription.cjs.map +1 -1
  276. package/dist/push/use-push-subscription.js +3 -1
  277. package/dist/push/use-push-subscription.js.map +1 -1
  278. package/dist/sse/use-event-stream.cjs +1 -1
  279. package/dist/sse/use-event-stream.cjs.map +1 -1
  280. package/dist/sse/use-event-stream.js +3 -1
  281. package/dist/sse/use-event-stream.js.map +1 -1
  282. package/dist/styles.css +1 -1
  283. package/dist/tempest-react-sdk.cjs +1 -1
  284. package/dist/tempest-react-sdk.d.ts +585 -3
  285. package/dist/tempest-react-sdk.js +189 -183
  286. package/dist/theme/ThemeProvider.cjs +1 -1
  287. package/dist/theme/ThemeProvider.cjs.map +1 -1
  288. package/dist/theme/ThemeProvider.js +6 -2
  289. package/dist/theme/ThemeProvider.js.map +1 -1
  290. package/dist/theme/color.cjs +1 -1
  291. package/dist/theme/color.cjs.map +1 -1
  292. package/dist/theme/color.js +77 -46
  293. package/dist/theme/color.js.map +1 -1
  294. package/dist/theme/create-theme.cjs +1 -1
  295. package/dist/theme/create-theme.cjs.map +1 -1
  296. package/dist/theme/create-theme.js +9 -3
  297. package/dist/theme/create-theme.js.map +1 -1
  298. package/dist/utils/compare-values.cjs +2 -0
  299. package/dist/utils/compare-values.cjs.map +1 -0
  300. package/dist/utils/compare-values.js +8 -0
  301. package/dist/utils/compare-values.js.map +1 -0
  302. package/dist/vision/use-camera-stream.cjs +1 -1
  303. package/dist/vision/use-camera-stream.cjs.map +1 -1
  304. package/dist/vision/use-camera-stream.js +3 -1
  305. package/dist/vision/use-camera-stream.js.map +1 -1
  306. package/dist/vite/create-vite-config.cjs +1 -1
  307. package/dist/vite/create-vite-config.cjs.map +1 -1
  308. package/dist/vite/create-vite-config.js +30 -22
  309. package/dist/vite/create-vite-config.js.map +1 -1
  310. package/dist/vite/tempest-icons.cjs +9 -0
  311. package/dist/vite/tempest-icons.cjs.map +1 -0
  312. package/dist/vite/tempest-icons.js +99 -0
  313. package/dist/vite/tempest-icons.js.map +1 -0
  314. package/dist/vite.cjs +1 -1
  315. package/dist/vite.d.ts +79 -0
  316. package/dist/vite.js +6 -5
  317. package/dist/ws/use-web-socket.cjs +1 -1
  318. package/dist/ws/use-web-socket.cjs.map +1 -1
  319. package/dist/ws/use-web-socket.js +3 -1
  320. package/dist/ws/use-web-socket.js.map +1 -1
  321. package/package.json +12 -3
  322. package/template/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ //#region src/components/NotificationCenter/NotificationCenter.module.css
2
+ var e = "tempest_panel_FdsVm", t = "tempest_header_hEQNb", n = "tempest_title_UJyzl", r = "tempest_headerAction_A0T8e", i = "tempest_list_Q23Lz", a = "tempest_item_e-qev", o = "tempest_unread_5bHa-", s = "tempest_icon_W0eML", c = "tempest_body_m3hfl", l = "tempest_trigger_y7SuR", u = "tempest_itemTitle_o7OXC", d = "tempest_itemBody_KL7uZ", f = "tempest_time_NLUQU", p = "tempest_dismiss_74KRm", m = {
3
+ panel: e,
4
+ header: t,
5
+ title: n,
6
+ headerAction: r,
7
+ list: i,
8
+ item: a,
9
+ unread: o,
10
+ icon: s,
11
+ body: c,
12
+ trigger: l,
13
+ itemTitle: u,
14
+ itemBody: d,
15
+ time: f,
16
+ dismiss: p
17
+ };
18
+ //#endregion
19
+ export { c as body, m as default, p as dismiss, t as header, r as headerAction, s as icon, a as item, d as itemBody, u as itemTitle, i as list, e as panel, f as time, n as title, l as trigger, o as unread };
20
+
21
+ //# sourceMappingURL=NotificationCenter.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationCenter.module.js","names":[],"sources":["../../../src/components/NotificationCenter/NotificationCenter.module.css"],"sourcesContent":[".panel {\n display: flex;\n flex-direction: column;\n min-width: 0;\n background-color: var(--tempest-bg);\n color: var(--tempest-text);\n font-family: var(--tempest-font-sans);\n}\n\n.header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.title {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n margin: 0;\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-semibold);\n}\n\n.headerAction {\n padding: 0;\n border: none;\n background: none;\n color: var(--tempest-primary);\n font: inherit;\n font-size: var(--tempest-text-xs);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.headerAction:hover {\n text-decoration: underline;\n}\n\n.headerAction:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n overflow-y: auto;\n}\n\n.item {\n display: flex;\n align-items: flex-start;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-bottom: 1px solid var(--tempest-border);\n}\n\n.item:last-child {\n border-bottom: none;\n}\n\n/*\n * Unread is marked by a left bar plus a tinted background, never by colour alone\n * — the distinction has to survive a monochrome display and colour blindness.\n * `aria-current` on the row carries the same information non-visually.\n */\n.item.unread {\n background-color: var(--tempest-surface);\n box-shadow: inset 3px 0 0 0 var(--tempest-primary);\n}\n\n.icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n width: 2rem;\n height: 2rem;\n border-radius: var(--tempest-radius-full);\n background-color: var(--tempest-surface-2, var(--tempest-surface));\n color: var(--tempest-text-muted);\n}\n\n.body {\n display: flex;\n flex: 1;\n min-width: 0;\n flex-direction: column;\n gap: var(--tempest-space-1);\n}\n\n/*\n * The whole title/body block is one button so the hit target matches what the\n * user perceives as \"the notification\", instead of just its title text.\n */\n.trigger {\n display: flex;\n flex-direction: column;\n gap: var(--tempest-space-1);\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.trigger:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.itemTitle {\n font-size: var(--tempest-text-sm);\n font-weight: var(--tempest-weight-medium);\n line-height: var(--tempest-leading-snug);\n}\n\n.itemBody {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-normal);\n}\n\n.time {\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-xs);\n}\n\n.dismiss {\n flex-shrink: 0;\n width: 1.5rem;\n height: 1.5rem;\n padding: 0;\n border: none;\n border-radius: var(--tempest-radius-full);\n background: none;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-base);\n line-height: 1;\n cursor: pointer;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .dismiss:hover {\n background-color: var(--tempest-surface);\n color: var(--tempest-text);\n }\n}\n\n.dismiss:focus-visible {\n outline: 2px solid var(--tempest-primary);\n outline-offset: 2px;\n}\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ let e=require("react");function t(e,t){let n=typeof e.title==`string`?e.title:null;return n?{id:typeof e.id==`string`?e.id:`sw-${e.tag??t}-${n}`,title:n,body:typeof e.body==`string`?e.body:void 0,receivedAt:typeof e.receivedAt==`number`?e.receivedAt:new Date().getTime(),url:typeof e.url==`string`?e.url:void 0,data:e.data??void 0,read:!1}:null}function n(n={}){let{initialItems:r=[],listenToServiceWorker:i=!0,messageType:a=`tempest:notification`,limit:o=100,onChange:s}=n,[c,l]=(0,e.useState)(()=>[...r].sort((e,t)=>t.receivedAt-e.receivedAt).slice(0,o)),u=(0,e.useCallback)(e=>{l(t=>{let n=e(t);return s?.(n),n})},[s]),d=(0,e.useCallback)(e=>{u(t=>[e,...t.filter(t=>t.id!==e.id)].sort((e,t)=>t.receivedAt-e.receivedAt).slice(0,o))},[u,o]),f=(0,e.useCallback)((e,t)=>{u(n=>n.map(n=>n.id===e?{...n,read:t}:n))},[u]),p=(0,e.useCallback)(e=>f(e,!0),[f]),m=(0,e.useCallback)(e=>f(e,!1),[f]),h=(0,e.useCallback)(()=>{u(e=>e.map(e=>({...e,read:!0})))},[u]),g=(0,e.useCallback)(e=>{u(t=>t.filter(t=>t.id!==e))},[u]),_=(0,e.useCallback)(()=>u(()=>[]),[u]);return(0,e.useEffect)(()=>{if(!i||typeof navigator>`u`||!navigator.serviceWorker)return;let e=navigator.serviceWorker,n=e=>{let n=e.data;if(!n||n.type!==a)return;let r=t(n.notification??n.payload??n,0);r&&d(r)};return e.addEventListener(`message`,n),()=>e.removeEventListener(`message`,n)},[i,a,d]),{items:c,unreadCount:(0,e.useMemo)(()=>c.filter(e=>!e.read).length,[c]),add:d,markRead:p,markUnread:m,markAllRead:h,remove:g,clear:_}}exports.useNotificationInbox=n;
2
+ //# sourceMappingURL=use-notification-inbox.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-notification-inbox.cjs","names":[],"sources":["../../../src/components/NotificationCenter/use-notification-inbox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\n\n/** One entry in the inbox. */\nexport interface NotificationItem {\n /** Stable id. Re-adding an existing id updates that entry instead of duplicating it. */\n id: string;\n title: string;\n /** Body text. Optional — a title-only notification is a legitimate shape. */\n body?: string;\n /** Epoch milliseconds. Used for ordering and for the relative timestamp. */\n receivedAt: number;\n read?: boolean;\n /** Where activating the notification should take the user. */\n url?: string;\n /** Free-form payload the app passes through, e.g. the entity it refers to. */\n data?: Record<string, unknown>;\n}\n\nexport interface UseNotificationInboxOptions {\n /** Entries the inbox starts with — typically what the API returned. */\n initialItems?: readonly NotificationItem[];\n /**\n * Listen for `message` events from the service worker and add whatever\n * arrives. Default `true`.\n *\n * This is the missing half of web push: a service worker runs outside the\n * page and cannot touch React state, so a push that arrives while the app is\n * open shows an OS notification and then vanishes as far as the UI is\n * concerned. Have the worker `postMessage` the payload and it lands here.\n */\n listenToServiceWorker?: boolean;\n /**\n * Message `type` to accept from the worker. Default `\"tempest:notification\"`.\n *\n * Filtering by type matters because the SW message channel is shared — a\n * sync-progress ping or a cache-updated notice would otherwise show up in the\n * user's inbox.\n */\n messageType?: string;\n /**\n * Cap on stored entries; the oldest are dropped past it. Default `100`.\n *\n * An inbox fed by push grows without bound otherwise, and it lives in memory.\n */\n limit?: number;\n /** Called whenever the list changes — the hook for persisting it. */\n onChange?: (items: readonly NotificationItem[]) => void;\n}\n\nexport interface UseNotificationInboxResult {\n items: NotificationItem[];\n unreadCount: number;\n /** Add an entry, or update the existing one with the same id. */\n add: (item: NotificationItem) => void;\n markRead: (id: string) => void;\n markUnread: (id: string) => void;\n markAllRead: () => void;\n remove: (id: string) => void;\n clear: () => void;\n}\n\n/** Coerce an unknown service-worker payload into an inbox entry. */\nfunction toItem(payload: Record<string, unknown>, index: number): NotificationItem | null {\n const title = typeof payload.title === \"string\" ? payload.title : null;\n if (!title) return null;\n return {\n id: typeof payload.id === \"string\" ? payload.id : `sw-${payload.tag ?? index}-${title}`,\n title,\n body: typeof payload.body === \"string\" ? payload.body : undefined,\n receivedAt:\n typeof payload.receivedAt === \"number\" ? payload.receivedAt : new Date().getTime(),\n url: typeof payload.url === \"string\" ? payload.url : undefined,\n data: (payload.data as Record<string, unknown> | undefined) ?? undefined,\n read: false,\n };\n}\n\n/**\n * Client-side inbox state for received notifications.\n *\n * Newest first, deduplicated by `id`, capped at `limit`. Pairs with\n * `NotificationCenter` for the UI and with the `push` module for the source:\n * point a service worker's `postMessage` at it and a push that arrives while the\n * app is open shows up in the inbox instead of only as an OS notification.\n *\n * Persistence is deliberately left out — where an inbox belongs (server, Dexie,\n * `localStorage`) is an app decision. Use `onChange` to write it wherever it goes,\n * and `initialItems` to read it back.\n *\n * @example\n * const inbox = useNotificationInbox({\n * initialItems: await api.notifications.list(),\n * onChange: (items) => storage.set(\"inbox\", items),\n * });\n *\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * />\n */\nexport function useNotificationInbox(\n options: UseNotificationInboxOptions = {},\n): UseNotificationInboxResult {\n const {\n initialItems = [],\n listenToServiceWorker = true,\n messageType = \"tempest:notification\",\n limit = 100,\n onChange,\n } = options;\n\n const [items, setItems] = useState<NotificationItem[]>(() =>\n [...initialItems].sort((a, b) => b.receivedAt - a.receivedAt).slice(0, limit),\n );\n\n const update = useCallback(\n (next: (current: NotificationItem[]) => NotificationItem[]) => {\n setItems((current) => {\n const result = next(current);\n onChange?.(result);\n return result;\n });\n },\n [onChange],\n );\n\n const add = useCallback(\n (item: NotificationItem) => {\n update((current) => {\n const without = current.filter((entry) => entry.id !== item.id);\n return [item, ...without]\n .sort((a, b) => b.receivedAt - a.receivedAt)\n .slice(0, limit);\n });\n },\n [update, limit],\n );\n\n const setRead = useCallback(\n (id: string, read: boolean) => {\n update((current) =>\n current.map((entry) => (entry.id === id ? { ...entry, read } : entry)),\n );\n },\n [update],\n );\n\n const markRead = useCallback((id: string) => setRead(id, true), [setRead]);\n const markUnread = useCallback((id: string) => setRead(id, false), [setRead]);\n\n const markAllRead = useCallback(() => {\n update((current) => current.map((entry) => ({ ...entry, read: true })));\n }, [update]);\n\n const remove = useCallback(\n (id: string) => {\n update((current) => current.filter((entry) => entry.id !== id));\n },\n [update],\n );\n\n const clear = useCallback(() => update(() => []), [update]);\n\n useEffect(() => {\n if (!listenToServiceWorker) return;\n if (typeof navigator === \"undefined\" || !navigator.serviceWorker) return;\n\n /**\n * Captured, not re-read in the cleanup.\n *\n * Looking `navigator.serviceWorker` up again at teardown throws if it is\n * gone by then, and the listener would leak either way — the container has\n * to be the same object the listener was added to.\n */\n const container = navigator.serviceWorker;\n\n const handler = (event: MessageEvent) => {\n const payload = event.data as Record<string, unknown> | null;\n if (!payload || payload.type !== messageType) return;\n const raw = (payload.notification ?? payload.payload ?? payload) as Record<\n string,\n unknown\n >;\n const item = toItem(raw, 0);\n if (item) add(item);\n };\n\n container.addEventListener(\"message\", handler);\n return () => container.removeEventListener(\"message\", handler);\n }, [listenToServiceWorker, messageType, add]);\n\n const unreadCount = useMemo(() => items.filter((item) => !item.read).length, [items]);\n\n return { items, unreadCount, add, markRead, markUnread, markAllRead, remove, clear };\n}\n"],"mappings":"uBA8DA,SAAS,EAAO,EAAkC,EAAwC,CACtF,IAAM,EAAQ,OAAO,EAAQ,OAAU,SAAW,EAAQ,MAAQ,KAElE,OADK,EACE,CACH,GAAI,OAAO,EAAQ,IAAO,SAAW,EAAQ,GAAK,MAAM,EAAQ,KAAO,EAAM,GAAG,IAChF,QACA,KAAM,OAAO,EAAQ,MAAS,SAAW,EAAQ,KAAO,IAAA,GACxD,WACI,OAAO,EAAQ,YAAe,SAAW,EAAQ,WAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,EACrF,IAAK,OAAO,EAAQ,KAAQ,SAAW,EAAQ,IAAM,IAAA,GACrD,KAAO,EAAQ,MAAgD,IAAA,GAC/D,KAAM,EACV,EAVmB,IAWvB,CA2BA,SAAgB,EACZ,EAAuC,CAAC,EACd,CAC1B,GAAM,CACF,eAAe,CAAC,EAChB,wBAAwB,GACxB,cAAc,uBACd,QAAQ,IACR,YACA,EAEE,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,KACV,CAAC,GAAG,CAAY,CAAC,CAAC,MAAM,EAAG,IAAM,EAAE,WAAa,EAAE,UAAU,CAAC,CAAC,MAAM,EAAG,CAAK,CAChF,EAEM,GAAA,EAAA,EAAA,YAAA,CACD,GAA8D,CAC3D,EAAU,GAAY,CAClB,IAAM,EAAS,EAAK,CAAO,EAE3B,OADA,IAAW,CAAM,EACV,CACX,CAAC,CACL,EACA,CAAC,CAAQ,CACb,EAEM,GAAA,EAAA,EAAA,YAAA,CACD,GAA2B,CACxB,EAAQ,GAEG,CAAC,EAAM,GADE,EAAQ,OAAQ,GAAU,EAAM,KAAO,EAAK,EAC3C,CAAO,CAAC,CACpB,MAAM,EAAG,IAAM,EAAE,WAAa,EAAE,UAAU,CAAC,CAC3C,MAAM,EAAG,CAAK,CACtB,CACL,EACA,CAAC,EAAQ,CAAK,CAClB,EAEM,GAAA,EAAA,EAAA,YAAA,EACD,EAAY,IAAkB,CAC3B,EAAQ,GACJ,EAAQ,IAAK,GAAW,EAAM,KAAO,EAAK,CAAE,GAAG,EAAO,MAAK,EAAI,CAAM,CACzE,CACJ,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAA,EAAA,YAAA,CAAwB,GAAe,EAAQ,EAAI,EAAI,EAAG,CAAC,CAAO,CAAC,EACnE,GAAA,EAAA,EAAA,YAAA,CAA0B,GAAe,EAAQ,EAAI,EAAK,EAAG,CAAC,CAAO,CAAC,EAEtE,GAAA,EAAA,EAAA,YAAA,KAAgC,CAClC,EAAQ,GAAY,EAAQ,IAAK,IAAW,CAAE,GAAG,EAAO,KAAM,EAAK,EAAE,CAAC,CAC1E,EAAG,CAAC,CAAM,CAAC,EAEL,GAAA,EAAA,EAAA,YAAA,CACD,GAAe,CACZ,EAAQ,GAAY,EAAQ,OAAQ,GAAU,EAAM,KAAO,CAAE,CAAC,CAClE,EACA,CAAC,CAAM,CACX,EAEM,GAAA,EAAA,EAAA,YAAA,KAA0B,MAAa,CAAC,CAAC,EAAG,CAAC,CAAM,CAAC,EAgC1D,OA9BA,EAAA,EAAA,UAAA,KAAgB,CAEZ,GADI,CAAC,GACD,OAAO,UAAc,KAAe,CAAC,UAAU,cAAe,OASlE,IAAM,EAAY,UAAU,cAEtB,EAAW,GAAwB,CACrC,IAAM,EAAU,EAAM,KACtB,GAAI,CAAC,GAAW,EAAQ,OAAS,EAAa,OAK9C,IAAM,EAAO,EAJA,EAAQ,cAAgB,EAAQ,SAAW,EAI/B,CAAC,EACtB,GAAM,EAAI,CAAI,CACtB,EAGA,OADA,EAAU,iBAAiB,UAAW,CAAO,MAChC,EAAU,oBAAoB,UAAW,CAAO,CACjE,EAAG,CAAC,EAAuB,EAAa,CAAG,CAAC,EAIrC,CAAE,QAAO,aAAA,EAAA,EAAA,QAAA,KAFkB,EAAM,OAAQ,GAAS,CAAC,EAAK,IAAI,CAAC,CAAC,OAAQ,CAAC,CAAK,CAEnE,EAAa,MAAK,WAAU,aAAY,cAAa,SAAQ,OAAM,CACvF"}
@@ -0,0 +1,63 @@
1
+ import { useCallback as e, useEffect as t, useMemo as n, useState as r } from "react";
2
+ //#region src/components/NotificationCenter/use-notification-inbox.ts
3
+ function i(e, t) {
4
+ let n = typeof e.title == "string" ? e.title : null;
5
+ return n ? {
6
+ id: typeof e.id == "string" ? e.id : `sw-${e.tag ?? t}-${n}`,
7
+ title: n,
8
+ body: typeof e.body == "string" ? e.body : void 0,
9
+ receivedAt: typeof e.receivedAt == "number" ? e.receivedAt : (/* @__PURE__ */ new Date()).getTime(),
10
+ url: typeof e.url == "string" ? e.url : void 0,
11
+ data: e.data ?? void 0,
12
+ read: !1
13
+ } : null;
14
+ }
15
+ function a(a = {}) {
16
+ let { initialItems: o = [], listenToServiceWorker: s = !0, messageType: c = "tempest:notification", limit: l = 100, onChange: u } = a, [d, f] = r(() => [...o].sort((e, t) => t.receivedAt - e.receivedAt).slice(0, l)), p = e((e) => {
17
+ f((t) => {
18
+ let n = e(t);
19
+ return u?.(n), n;
20
+ });
21
+ }, [u]), m = e((e) => {
22
+ p((t) => [e, ...t.filter((t) => t.id !== e.id)].sort((e, t) => t.receivedAt - e.receivedAt).slice(0, l));
23
+ }, [p, l]), h = e((e, t) => {
24
+ p((n) => n.map((n) => n.id === e ? {
25
+ ...n,
26
+ read: t
27
+ } : n));
28
+ }, [p]), g = e((e) => h(e, !0), [h]), _ = e((e) => h(e, !1), [h]), v = e(() => {
29
+ p((e) => e.map((e) => ({
30
+ ...e,
31
+ read: !0
32
+ })));
33
+ }, [p]), y = e((e) => {
34
+ p((t) => t.filter((t) => t.id !== e));
35
+ }, [p]), b = e(() => p(() => []), [p]);
36
+ return t(() => {
37
+ if (!s || typeof navigator > "u" || !navigator.serviceWorker) return;
38
+ let e = navigator.serviceWorker, t = (e) => {
39
+ let t = e.data;
40
+ if (!t || t.type !== c) return;
41
+ let n = i(t.notification ?? t.payload ?? t, 0);
42
+ n && m(n);
43
+ };
44
+ return e.addEventListener("message", t), () => e.removeEventListener("message", t);
45
+ }, [
46
+ s,
47
+ c,
48
+ m
49
+ ]), {
50
+ items: d,
51
+ unreadCount: n(() => d.filter((e) => !e.read).length, [d]),
52
+ add: m,
53
+ markRead: g,
54
+ markUnread: _,
55
+ markAllRead: v,
56
+ remove: y,
57
+ clear: b
58
+ };
59
+ }
60
+ //#endregion
61
+ export { a as useNotificationInbox };
62
+
63
+ //# sourceMappingURL=use-notification-inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-notification-inbox.js","names":[],"sources":["../../../src/components/NotificationCenter/use-notification-inbox.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from \"react\";\n\n/** One entry in the inbox. */\nexport interface NotificationItem {\n /** Stable id. Re-adding an existing id updates that entry instead of duplicating it. */\n id: string;\n title: string;\n /** Body text. Optional — a title-only notification is a legitimate shape. */\n body?: string;\n /** Epoch milliseconds. Used for ordering and for the relative timestamp. */\n receivedAt: number;\n read?: boolean;\n /** Where activating the notification should take the user. */\n url?: string;\n /** Free-form payload the app passes through, e.g. the entity it refers to. */\n data?: Record<string, unknown>;\n}\n\nexport interface UseNotificationInboxOptions {\n /** Entries the inbox starts with — typically what the API returned. */\n initialItems?: readonly NotificationItem[];\n /**\n * Listen for `message` events from the service worker and add whatever\n * arrives. Default `true`.\n *\n * This is the missing half of web push: a service worker runs outside the\n * page and cannot touch React state, so a push that arrives while the app is\n * open shows an OS notification and then vanishes as far as the UI is\n * concerned. Have the worker `postMessage` the payload and it lands here.\n */\n listenToServiceWorker?: boolean;\n /**\n * Message `type` to accept from the worker. Default `\"tempest:notification\"`.\n *\n * Filtering by type matters because the SW message channel is shared — a\n * sync-progress ping or a cache-updated notice would otherwise show up in the\n * user's inbox.\n */\n messageType?: string;\n /**\n * Cap on stored entries; the oldest are dropped past it. Default `100`.\n *\n * An inbox fed by push grows without bound otherwise, and it lives in memory.\n */\n limit?: number;\n /** Called whenever the list changes — the hook for persisting it. */\n onChange?: (items: readonly NotificationItem[]) => void;\n}\n\nexport interface UseNotificationInboxResult {\n items: NotificationItem[];\n unreadCount: number;\n /** Add an entry, or update the existing one with the same id. */\n add: (item: NotificationItem) => void;\n markRead: (id: string) => void;\n markUnread: (id: string) => void;\n markAllRead: () => void;\n remove: (id: string) => void;\n clear: () => void;\n}\n\n/** Coerce an unknown service-worker payload into an inbox entry. */\nfunction toItem(payload: Record<string, unknown>, index: number): NotificationItem | null {\n const title = typeof payload.title === \"string\" ? payload.title : null;\n if (!title) return null;\n return {\n id: typeof payload.id === \"string\" ? payload.id : `sw-${payload.tag ?? index}-${title}`,\n title,\n body: typeof payload.body === \"string\" ? payload.body : undefined,\n receivedAt:\n typeof payload.receivedAt === \"number\" ? payload.receivedAt : new Date().getTime(),\n url: typeof payload.url === \"string\" ? payload.url : undefined,\n data: (payload.data as Record<string, unknown> | undefined) ?? undefined,\n read: false,\n };\n}\n\n/**\n * Client-side inbox state for received notifications.\n *\n * Newest first, deduplicated by `id`, capped at `limit`. Pairs with\n * `NotificationCenter` for the UI and with the `push` module for the source:\n * point a service worker's `postMessage` at it and a push that arrives while the\n * app is open shows up in the inbox instead of only as an OS notification.\n *\n * Persistence is deliberately left out — where an inbox belongs (server, Dexie,\n * `localStorage`) is an app decision. Use `onChange` to write it wherever it goes,\n * and `initialItems` to read it back.\n *\n * @example\n * const inbox = useNotificationInbox({\n * initialItems: await api.notifications.list(),\n * onChange: (items) => storage.set(\"inbox\", items),\n * });\n *\n * <NotificationCenter\n * items={inbox.items}\n * onMarkRead={inbox.markRead}\n * onMarkAllRead={inbox.markAllRead}\n * onDismiss={inbox.remove}\n * />\n */\nexport function useNotificationInbox(\n options: UseNotificationInboxOptions = {},\n): UseNotificationInboxResult {\n const {\n initialItems = [],\n listenToServiceWorker = true,\n messageType = \"tempest:notification\",\n limit = 100,\n onChange,\n } = options;\n\n const [items, setItems] = useState<NotificationItem[]>(() =>\n [...initialItems].sort((a, b) => b.receivedAt - a.receivedAt).slice(0, limit),\n );\n\n const update = useCallback(\n (next: (current: NotificationItem[]) => NotificationItem[]) => {\n setItems((current) => {\n const result = next(current);\n onChange?.(result);\n return result;\n });\n },\n [onChange],\n );\n\n const add = useCallback(\n (item: NotificationItem) => {\n update((current) => {\n const without = current.filter((entry) => entry.id !== item.id);\n return [item, ...without]\n .sort((a, b) => b.receivedAt - a.receivedAt)\n .slice(0, limit);\n });\n },\n [update, limit],\n );\n\n const setRead = useCallback(\n (id: string, read: boolean) => {\n update((current) =>\n current.map((entry) => (entry.id === id ? { ...entry, read } : entry)),\n );\n },\n [update],\n );\n\n const markRead = useCallback((id: string) => setRead(id, true), [setRead]);\n const markUnread = useCallback((id: string) => setRead(id, false), [setRead]);\n\n const markAllRead = useCallback(() => {\n update((current) => current.map((entry) => ({ ...entry, read: true })));\n }, [update]);\n\n const remove = useCallback(\n (id: string) => {\n update((current) => current.filter((entry) => entry.id !== id));\n },\n [update],\n );\n\n const clear = useCallback(() => update(() => []), [update]);\n\n useEffect(() => {\n if (!listenToServiceWorker) return;\n if (typeof navigator === \"undefined\" || !navigator.serviceWorker) return;\n\n /**\n * Captured, not re-read in the cleanup.\n *\n * Looking `navigator.serviceWorker` up again at teardown throws if it is\n * gone by then, and the listener would leak either way — the container has\n * to be the same object the listener was added to.\n */\n const container = navigator.serviceWorker;\n\n const handler = (event: MessageEvent) => {\n const payload = event.data as Record<string, unknown> | null;\n if (!payload || payload.type !== messageType) return;\n const raw = (payload.notification ?? payload.payload ?? payload) as Record<\n string,\n unknown\n >;\n const item = toItem(raw, 0);\n if (item) add(item);\n };\n\n container.addEventListener(\"message\", handler);\n return () => container.removeEventListener(\"message\", handler);\n }, [listenToServiceWorker, messageType, add]);\n\n const unreadCount = useMemo(() => items.filter((item) => !item.read).length, [items]);\n\n return { items, unreadCount, add, markRead, markUnread, markAllRead, remove, clear };\n}\n"],"mappings":";;AA8DA,SAAS,EAAO,GAAkC,GAAwC;CACtF,IAAM,IAAQ,OAAO,EAAQ,SAAU,WAAW,EAAQ,QAAQ;CAElE,OADK,IACE;EACH,IAAI,OAAO,EAAQ,MAAO,WAAW,EAAQ,KAAK,MAAM,EAAQ,OAAO,EAAM,GAAG;EAChF;EACA,MAAM,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO,KAAA;EACxD,YACI,OAAO,EAAQ,cAAe,WAAW,EAAQ,8BAAa,IAAI,KAAK,EAAA,CAAE,QAAQ;EACrF,KAAK,OAAO,EAAQ,OAAQ,WAAW,EAAQ,MAAM,KAAA;EACrD,MAAO,EAAQ,QAAgD,KAAA;EAC/D,MAAM;CACV,IAVmB;AAWvB;AA2BA,SAAgB,EACZ,IAAuC,CAAC,GACd;CAC1B,IAAM,EACF,kBAAe,CAAC,GAChB,2BAAwB,IACxB,iBAAc,wBACd,WAAQ,KACR,gBACA,GAEE,CAAC,GAAO,KAAY,QACtB,CAAC,GAAG,CAAY,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,MAAM,GAAG,CAAK,CAChF,GAEM,IAAS,GACV,MAA8D;EAC3D,GAAU,MAAY;GAClB,IAAM,IAAS,EAAK,CAAO;GAE3B,OADA,IAAW,CAAM,GACV;EACX,CAAC;CACL,GACA,CAAC,CAAQ,CACb,GAEM,IAAM,GACP,MAA2B;EACxB,GAAQ,MAEG,CAAC,GAAM,GADE,EAAQ,QAAQ,MAAU,EAAM,OAAO,EAAK,EAC3C,CAAO,CAAC,CACpB,MAAM,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,CAAC,CAC3C,MAAM,GAAG,CAAK,CACtB;CACL,GACA,CAAC,GAAQ,CAAK,CAClB,GAEM,IAAU,GACX,GAAY,MAAkB;EAC3B,GAAQ,MACJ,EAAQ,KAAK,MAAW,EAAM,OAAO,IAAK;GAAE,GAAG;GAAO;EAAK,IAAI,CAAM,CACzE;CACJ,GACA,CAAC,CAAM,CACX,GAEM,IAAW,GAAa,MAAe,EAAQ,GAAI,EAAI,GAAG,CAAC,CAAO,CAAC,GACnE,IAAa,GAAa,MAAe,EAAQ,GAAI,EAAK,GAAG,CAAC,CAAO,CAAC,GAEtE,IAAc,QAAkB;EAClC,GAAQ,MAAY,EAAQ,KAAK,OAAW;GAAE,GAAG;GAAO,MAAM;EAAK,EAAE,CAAC;CAC1E,GAAG,CAAC,CAAM,CAAC,GAEL,IAAS,GACV,MAAe;EACZ,GAAQ,MAAY,EAAQ,QAAQ,MAAU,EAAM,OAAO,CAAE,CAAC;CAClE,GACA,CAAC,CAAM,CACX,GAEM,IAAQ,QAAkB,QAAa,CAAC,CAAC,GAAG,CAAC,CAAM,CAAC;CAgC1D,OA9BA,QAAgB;EAEZ,IADI,CAAC,KACD,OAAO,YAAc,OAAe,CAAC,UAAU,eAAe;EASlE,IAAM,IAAY,UAAU,eAEtB,KAAW,MAAwB;GACrC,IAAM,IAAU,EAAM;GACtB,IAAI,CAAC,KAAW,EAAQ,SAAS,GAAa;GAK9C,IAAM,IAAO,EAJA,EAAQ,gBAAgB,EAAQ,WAAW,GAI/B,CAAC;GAC1B,AAAI,KAAM,EAAI,CAAI;EACtB;EAGA,OADA,EAAU,iBAAiB,WAAW,CAAO,SAChC,EAAU,oBAAoB,WAAW,CAAO;CACjE,GAAG;EAAC;EAAuB;EAAa;CAAG,CAAC,GAIrC;EAAE;EAAO,aAFI,QAAc,EAAM,QAAQ,MAAS,CAAC,EAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAK,CAEnE;EAAa;EAAK;EAAU;EAAY;EAAa;EAAQ;CAAM;AACvF"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../Spinner/Spinner.cjs"),n=require("./RefreshIndicator.module.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e,t){let n=t*1.5;return Math.min(e/2,n)}function o({onRefresh:o,children:s,threshold:c=80,disabled:l,className:u}){let d=(0,r.useRef)(null),f=(0,r.useRef)(null),[p,m]=(0,r.useState)(0),[h,g]=(0,r.useState)(!1),_=f.current!==null,v=e=>{if(l||h)return;let t=d.current;if(!t||t.scrollTop>0)return;let n=e.touches[0];n&&(f.current=n.clientY)},y=e=>{if(l||h)return;let t=f.current;if(t===null)return;let n=e.touches[0];if(!n)return;let r=n.clientY-t;if(r<=0){m(0);return}m(a(r,c))},b=()=>{f.current=null,m(0)},x=async()=>{if(l||h){b();return}if(!(f.current!==null&&p>=c)){b();return}f.current=null,g(!0),m(c);try{await o()}finally{g(!1),m(0)}},S=h||p>0,C=Math.min(1,p/c);return(0,i.jsxs)(`div`,{ref:d,className:e.cn(n.default.wrapper,u),"data-refreshing":h,onTouchStart:v,onTouchMove:y,onTouchEnd:x,children:[(0,i.jsx)(`div`,{className:n.default.indicator,"aria-hidden":!S,style:{opacity:h?1:C,transform:`translateX(-50%) translateY(${S?p:0}px) rotate(${C*360}deg)`},children:(0,i.jsx)(t.Spinner,{size:`sm`,label:`Atualizando`})}),(0,i.jsx)(`div`,{className:e.cn(n.default.content,(_||h)&&n.default.dragging),style:{transform:`translateY(${S?p:0}px)`},children:s})]})}exports.RefreshIndicator=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("../Spinner/Spinner.cjs"),n=require("./RefreshIndicator.module.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e,t){let n=t*1.5;return Math.min(e/2,n)}function o({onRefresh:o,children:s,threshold:c=80,disabled:l,className:u}){let d=(0,r.useRef)(null),f=(0,r.useRef)(null),[p,m]=(0,r.useState)(0),[h,g]=(0,r.useState)(!1),[_,v]=(0,r.useState)(!1),y=e=>{if(l||h)return;let t=d.current;if(!t||t.scrollTop>0)return;let n=e.touches[0];n&&(f.current=n.clientY,v(!0))},b=e=>{if(l||h)return;let t=f.current;if(t===null)return;let n=e.touches[0];if(!n)return;let r=n.clientY-t;if(r<=0){m(0);return}m(a(r,c))},x=()=>{f.current=null,v(!1),m(0)},S=async()=>{if(l||h){x();return}if(!(f.current!==null&&p>=c)){x();return}f.current=null,v(!1),g(!0),m(c);try{await o()}finally{g(!1),m(0)}},C=h||p>0,w=Math.min(1,p/c);return(0,i.jsxs)(`div`,{ref:d,className:e.cn(n.default.wrapper,u),"data-refreshing":h,onTouchStart:y,onTouchMove:b,onTouchEnd:S,children:[(0,i.jsx)(`div`,{className:n.default.indicator,"aria-hidden":!C,style:{opacity:h?1:w,transform:`translateX(-50%) translateY(${C?p:0}px) rotate(${w*360}deg)`},children:(0,i.jsx)(t.Spinner,{size:`sm`,label:`Atualizando`})}),(0,i.jsx)(`div`,{className:e.cn(n.default.content,(_||h)&&n.default.dragging),style:{transform:`translateY(${C?p:0}px)`},children:s})]})}exports.RefreshIndicator=o;
2
2
  //# sourceMappingURL=RefreshIndicator.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"RefreshIndicator.cjs","names":[],"sources":["../../../src/components/RefreshIndicator/RefreshIndicator.tsx"],"sourcesContent":["import { useRef, useState, type ReactNode, type TouchEvent } from \"react\";\nimport { Spinner } from \"@/components/Spinner\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RefreshIndicator.module.css\";\n\nexport interface RefreshIndicatorProps {\n /** Invoked when the user pulls past `threshold` and releases. May be async. */\n onRefresh: () => void | Promise<void>;\n /** Scrollable content to wrap. */\n children: ReactNode;\n /** Pixels the user must pull past to trigger a refresh. Defaults to `80`. */\n threshold?: number;\n /** When true, pull-to-refresh is inert. */\n disabled?: boolean;\n className?: string;\n}\n\n/** Apply resistance so the content lags behind the finger and the pull never runs away. */\nfunction applyResistance(distance: number, threshold: number): number {\n const cap = threshold * 1.5;\n return Math.min(distance / 2, cap);\n}\n\n/**\n * RefreshIndicator — pull-to-refresh wrapper for touch devices.\n *\n * Wraps scrollable `children` and listens for a downward drag that starts while the\n * container is scrolled to the top. Pulling past `threshold` and releasing triggers\n * `onRefresh`; the SDK {@link Spinner} is shown while pulling and during the refresh.\n */\nexport function RefreshIndicator({\n onRefresh,\n children,\n threshold = 80,\n disabled,\n className,\n}: RefreshIndicatorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const startYRef = useRef<number | null>(null);\n const [pull, setPull] = useState(0);\n const [refreshing, setRefreshing] = useState(false);\n\n const engaged = startYRef.current !== null;\n\n const handleTouchStart = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const container = containerRef.current;\n if (!container || container.scrollTop > 0) return;\n const touch = event.touches[0];\n if (!touch) return;\n startYRef.current = touch.clientY;\n };\n\n const handleTouchMove = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const startY = startYRef.current;\n if (startY === null) return;\n const touch = event.touches[0];\n if (!touch) return;\n const delta = touch.clientY - startY;\n if (delta <= 0) {\n setPull(0);\n return;\n }\n setPull(applyResistance(delta, threshold));\n };\n\n const reset = (): void => {\n startYRef.current = null;\n setPull(0);\n };\n\n const handleTouchEnd = async (): Promise<void> => {\n if (disabled || refreshing) {\n reset();\n return;\n }\n const startY = startYRef.current;\n const triggered = startY !== null && pull >= threshold;\n if (!triggered) {\n reset();\n return;\n }\n startYRef.current = null;\n setRefreshing(true);\n setPull(threshold);\n try {\n await onRefresh();\n } finally {\n setRefreshing(false);\n setPull(0);\n }\n };\n\n const showIndicator = refreshing || pull > 0;\n const progress = Math.min(1, pull / threshold);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.wrapper, className)}\n data-refreshing={refreshing}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n <div\n className={styles.indicator}\n aria-hidden={!showIndicator}\n style={{\n opacity: refreshing ? 1 : progress,\n transform: `translateX(-50%) translateY(${showIndicator ? pull : 0}px) rotate(${progress * 360}deg)`,\n }}\n >\n <Spinner size=\"sm\" label=\"Atualizando\" />\n </div>\n <div\n className={cn(styles.content, (engaged || refreshing) && styles.dragging)}\n style={{ transform: `translateY(${showIndicator ? pull : 0}px)` }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":"2KAkBA,SAAS,EAAgB,EAAkB,EAA2B,CAClE,IAAM,EAAM,EAAY,IACxB,OAAO,KAAK,IAAI,EAAW,EAAG,CAAG,CACrC,CASA,SAAgB,EAAiB,CAC7B,YACA,WACA,YAAY,GACZ,WACA,aACsB,CACtB,IAAM,GAAA,EAAA,EAAA,OAAA,CAAsC,IAAI,EAC1C,GAAA,EAAA,EAAA,OAAA,CAAkC,IAAI,EACtC,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAAoB,CAAC,EAC5B,CAAC,EAAY,IAAA,EAAA,EAAA,SAAA,CAA0B,EAAK,EAE5C,EAAU,EAAU,UAAY,KAEhC,EAAoB,GAA4C,CAClE,GAAI,GAAY,EAAY,OAC5B,IAAM,EAAY,EAAa,QAC/B,GAAI,CAAC,GAAa,EAAU,UAAY,EAAG,OAC3C,IAAM,EAAQ,EAAM,QAAQ,GACvB,IACL,EAAU,QAAU,EAAM,QAC9B,EAEM,EAAmB,GAA4C,CACjE,GAAI,GAAY,EAAY,OAC5B,IAAM,EAAS,EAAU,QACzB,GAAI,IAAW,KAAM,OACrB,IAAM,EAAQ,EAAM,QAAQ,GAC5B,GAAI,CAAC,EAAO,OACZ,IAAM,EAAQ,EAAM,QAAU,EAC9B,GAAI,GAAS,EAAG,CACZ,EAAQ,CAAC,EACT,MACJ,CACA,EAAQ,EAAgB,EAAO,CAAS,CAAC,CAC7C,EAEM,MAAoB,CACtB,EAAU,QAAU,KACpB,EAAQ,CAAC,CACb,EAEM,EAAiB,SAA2B,CAC9C,GAAI,GAAY,EAAY,CACxB,EAAM,EACN,MACJ,CAGA,GAAI,EAFW,EAAU,UACI,MAAQ,GAAQ,GAC7B,CACZ,EAAM,EACN,MACJ,CACA,EAAU,QAAU,KACpB,EAAc,EAAI,EAClB,EAAQ,CAAS,EACjB,GAAI,CACA,MAAM,EAAU,CACpB,QAAU,CACN,EAAc,EAAK,EACnB,EAAQ,CAAC,CACb,CACJ,EAEM,EAAgB,GAAc,EAAO,EACrC,EAAW,KAAK,IAAI,EAAG,EAAO,CAAS,EAE7C,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,IAAK,EACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EACvC,kBAAiB,EACjB,aAAc,EACd,YAAa,EACb,WAAY,WANhB,EAQI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,UAClB,cAAa,CAAC,EACd,MAAO,CACH,QAAS,EAAa,EAAI,EAC1B,UAAW,+BAA+B,EAAgB,EAAO,EAAE,aAAa,EAAW,IAAI,KACnG,YAEA,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CAAS,KAAK,KAAK,MAAM,aAAe,CAAA,CACvC,CAAA,GACL,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,SAAU,GAAW,IAAe,EAAA,QAAO,QAAQ,EACxE,MAAO,CAAE,UAAW,cAAc,EAAgB,EAAO,EAAE,IAAK,EAE/D,UACA,CAAA,CACJ,GAEb"}
1
+ {"version":3,"file":"RefreshIndicator.cjs","names":[],"sources":["../../../src/components/RefreshIndicator/RefreshIndicator.tsx"],"sourcesContent":["import { useRef, useState, type ReactNode, type TouchEvent } from \"react\";\nimport { Spinner } from \"@/components/Spinner\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RefreshIndicator.module.css\";\n\nexport interface RefreshIndicatorProps {\n /** Invoked when the user pulls past `threshold` and releases. May be async. */\n onRefresh: () => void | Promise<void>;\n /** Scrollable content to wrap. */\n children: ReactNode;\n /** Pixels the user must pull past to trigger a refresh. Defaults to `80`. */\n threshold?: number;\n /** When true, pull-to-refresh is inert. */\n disabled?: boolean;\n className?: string;\n}\n\n/** Apply resistance so the content lags behind the finger and the pull never runs away. */\nfunction applyResistance(distance: number, threshold: number): number {\n const cap = threshold * 1.5;\n return Math.min(distance / 2, cap);\n}\n\n/**\n * RefreshIndicator — pull-to-refresh wrapper for touch devices.\n *\n * Wraps scrollable `children` and listens for a downward drag that starts while the\n * container is scrolled to the top. Pulling past `threshold` and releasing triggers\n * `onRefresh`; the SDK {@link Spinner} is shown while pulling and during the refresh.\n */\nexport function RefreshIndicator({\n onRefresh,\n children,\n threshold = 80,\n disabled,\n className,\n}: RefreshIndicatorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const startYRef = useRef<number | null>(null);\n const [pull, setPull] = useState(0);\n const [refreshing, setRefreshing] = useState(false);\n /**\n * Whether a drag is in progress. Kept in state, not derived from\n * `startYRef.current`: a ref is not reactive, so reading it during render ties\n * the rendered class to a value React never re-renders for — it only looked\n * correct here because the touch handlers happen to call `setPull` too.\n */\n const [engaged, setEngaged] = useState(false);\n\n const handleTouchStart = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const container = containerRef.current;\n if (!container || container.scrollTop > 0) return;\n const touch = event.touches[0];\n if (!touch) return;\n startYRef.current = touch.clientY;\n setEngaged(true);\n };\n\n const handleTouchMove = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const startY = startYRef.current;\n if (startY === null) return;\n const touch = event.touches[0];\n if (!touch) return;\n const delta = touch.clientY - startY;\n if (delta <= 0) {\n setPull(0);\n return;\n }\n setPull(applyResistance(delta, threshold));\n };\n\n const reset = (): void => {\n startYRef.current = null;\n setEngaged(false);\n setPull(0);\n };\n\n const handleTouchEnd = async (): Promise<void> => {\n if (disabled || refreshing) {\n reset();\n return;\n }\n const startY = startYRef.current;\n const triggered = startY !== null && pull >= threshold;\n if (!triggered) {\n reset();\n return;\n }\n startYRef.current = null;\n setEngaged(false);\n setRefreshing(true);\n setPull(threshold);\n try {\n await onRefresh();\n } finally {\n setRefreshing(false);\n setPull(0);\n }\n };\n\n const showIndicator = refreshing || pull > 0;\n const progress = Math.min(1, pull / threshold);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.wrapper, className)}\n data-refreshing={refreshing}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n <div\n className={styles.indicator}\n aria-hidden={!showIndicator}\n style={{\n opacity: refreshing ? 1 : progress,\n transform: `translateX(-50%) translateY(${showIndicator ? pull : 0}px) rotate(${progress * 360}deg)`,\n }}\n >\n <Spinner size=\"sm\" label=\"Atualizando\" />\n </div>\n <div\n className={cn(styles.content, (engaged || refreshing) && styles.dragging)}\n style={{ transform: `translateY(${showIndicator ? pull : 0}px)` }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":"2KAkBA,SAAS,EAAgB,EAAkB,EAA2B,CAClE,IAAM,EAAM,EAAY,IACxB,OAAO,KAAK,IAAI,EAAW,EAAG,CAAG,CACrC,CASA,SAAgB,EAAiB,CAC7B,YACA,WACA,YAAY,GACZ,WACA,aACsB,CACtB,IAAM,GAAA,EAAA,EAAA,OAAA,CAAsC,IAAI,EAC1C,GAAA,EAAA,EAAA,OAAA,CAAkC,IAAI,EACtC,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAAoB,CAAC,EAC5B,CAAC,EAAY,IAAA,EAAA,EAAA,SAAA,CAA0B,EAAK,EAO5C,CAAC,EAAS,IAAA,EAAA,EAAA,SAAA,CAAuB,EAAK,EAEtC,EAAoB,GAA4C,CAClE,GAAI,GAAY,EAAY,OAC5B,IAAM,EAAY,EAAa,QAC/B,GAAI,CAAC,GAAa,EAAU,UAAY,EAAG,OAC3C,IAAM,EAAQ,EAAM,QAAQ,GACvB,IACL,EAAU,QAAU,EAAM,QAC1B,EAAW,EAAI,EACnB,EAEM,EAAmB,GAA4C,CACjE,GAAI,GAAY,EAAY,OAC5B,IAAM,EAAS,EAAU,QACzB,GAAI,IAAW,KAAM,OACrB,IAAM,EAAQ,EAAM,QAAQ,GAC5B,GAAI,CAAC,EAAO,OACZ,IAAM,EAAQ,EAAM,QAAU,EAC9B,GAAI,GAAS,EAAG,CACZ,EAAQ,CAAC,EACT,MACJ,CACA,EAAQ,EAAgB,EAAO,CAAS,CAAC,CAC7C,EAEM,MAAoB,CACtB,EAAU,QAAU,KACpB,EAAW,EAAK,EAChB,EAAQ,CAAC,CACb,EAEM,EAAiB,SAA2B,CAC9C,GAAI,GAAY,EAAY,CACxB,EAAM,EACN,MACJ,CAGA,GAAI,EAFW,EAAU,UACI,MAAQ,GAAQ,GAC7B,CACZ,EAAM,EACN,MACJ,CACA,EAAU,QAAU,KACpB,EAAW,EAAK,EAChB,EAAc,EAAI,EAClB,EAAQ,CAAS,EACjB,GAAI,CACA,MAAM,EAAU,CACpB,QAAU,CACN,EAAc,EAAK,EACnB,EAAQ,CAAC,CACb,CACJ,EAEM,EAAgB,GAAc,EAAO,EACrC,EAAW,KAAK,IAAI,EAAG,EAAO,CAAS,EAE7C,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,IAAK,EACL,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EACvC,kBAAiB,EACjB,aAAc,EACd,YAAa,EACb,WAAY,WANhB,EAQI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,UAClB,cAAa,CAAC,EACd,MAAO,CACH,QAAS,EAAa,EAAI,EAC1B,UAAW,+BAA+B,EAAgB,EAAO,EAAE,aAAa,EAAW,IAAI,KACnG,YAEA,EAAA,EAAA,IAAA,CAAC,EAAA,QAAD,CAAS,KAAK,KAAK,MAAM,aAAe,CAAA,CACvC,CAAA,GACL,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GAAG,EAAA,QAAO,SAAU,GAAW,IAAe,EAAA,QAAO,QAAQ,EACxE,MAAO,CAAE,UAAW,cAAc,EAAgB,EAAO,EAAE,IAAK,EAE/D,UACA,CAAA,CACJ,GAEb"}
@@ -9,13 +9,13 @@ function s(e, t) {
9
9
  return Math.min(e / 2, n);
10
10
  }
11
11
  function c({ onRefresh: c, children: l, threshold: u = 80, disabled: d, className: f }) {
12
- let p = r(null), m = r(null), [h, g] = i(0), [_, v] = i(!1), y = m.current !== null, b = (e) => {
12
+ let p = r(null), m = r(null), [h, g] = i(0), [_, v] = i(!1), [y, b] = i(!1), x = (e) => {
13
13
  if (d || _) return;
14
14
  let t = p.current;
15
15
  if (!t || t.scrollTop > 0) return;
16
16
  let n = e.touches[0];
17
- n && (m.current = n.clientY);
18
- }, x = (e) => {
17
+ n && (m.current = n.clientY, b(!0));
18
+ }, S = (e) => {
19
19
  if (d || _) return;
20
20
  let t = m.current;
21
21
  if (t === null) return;
@@ -27,37 +27,37 @@ function c({ onRefresh: c, children: l, threshold: u = 80, disabled: d, classNam
27
27
  return;
28
28
  }
29
29
  g(s(r, u));
30
- }, S = () => {
31
- m.current = null, g(0);
32
- }, C = async () => {
30
+ }, C = () => {
31
+ m.current = null, b(!1), g(0);
32
+ }, w = async () => {
33
33
  if (d || _) {
34
- S();
34
+ C();
35
35
  return;
36
36
  }
37
37
  if (!(m.current !== null && h >= u)) {
38
- S();
38
+ C();
39
39
  return;
40
40
  }
41
- m.current = null, v(!0), g(u);
41
+ m.current = null, b(!1), v(!0), g(u);
42
42
  try {
43
43
  await c();
44
44
  } finally {
45
45
  v(!1), g(0);
46
46
  }
47
- }, w = _ || h > 0, T = Math.min(1, h / u);
47
+ }, T = _ || h > 0, E = Math.min(1, h / u);
48
48
  return /* @__PURE__ */ o("div", {
49
49
  ref: p,
50
50
  className: e(n.wrapper, f),
51
51
  "data-refreshing": _,
52
- onTouchStart: b,
53
- onTouchMove: x,
54
- onTouchEnd: C,
52
+ onTouchStart: x,
53
+ onTouchMove: S,
54
+ onTouchEnd: w,
55
55
  children: [/* @__PURE__ */ a("div", {
56
56
  className: n.indicator,
57
- "aria-hidden": !w,
57
+ "aria-hidden": !T,
58
58
  style: {
59
- opacity: _ ? 1 : T,
60
- transform: `translateX(-50%) translateY(${w ? h : 0}px) rotate(${T * 360}deg)`
59
+ opacity: _ ? 1 : E,
60
+ transform: `translateX(-50%) translateY(${T ? h : 0}px) rotate(${E * 360}deg)`
61
61
  },
62
62
  children: /* @__PURE__ */ a(t, {
63
63
  size: "sm",
@@ -65,7 +65,7 @@ function c({ onRefresh: c, children: l, threshold: u = 80, disabled: d, classNam
65
65
  })
66
66
  }), /* @__PURE__ */ a("div", {
67
67
  className: e(n.content, (y || _) && n.dragging),
68
- style: { transform: `translateY(${w ? h : 0}px)` },
68
+ style: { transform: `translateY(${T ? h : 0}px)` },
69
69
  children: l
70
70
  })]
71
71
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RefreshIndicator.js","names":[],"sources":["../../../src/components/RefreshIndicator/RefreshIndicator.tsx"],"sourcesContent":["import { useRef, useState, type ReactNode, type TouchEvent } from \"react\";\nimport { Spinner } from \"@/components/Spinner\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RefreshIndicator.module.css\";\n\nexport interface RefreshIndicatorProps {\n /** Invoked when the user pulls past `threshold` and releases. May be async. */\n onRefresh: () => void | Promise<void>;\n /** Scrollable content to wrap. */\n children: ReactNode;\n /** Pixels the user must pull past to trigger a refresh. Defaults to `80`. */\n threshold?: number;\n /** When true, pull-to-refresh is inert. */\n disabled?: boolean;\n className?: string;\n}\n\n/** Apply resistance so the content lags behind the finger and the pull never runs away. */\nfunction applyResistance(distance: number, threshold: number): number {\n const cap = threshold * 1.5;\n return Math.min(distance / 2, cap);\n}\n\n/**\n * RefreshIndicator — pull-to-refresh wrapper for touch devices.\n *\n * Wraps scrollable `children` and listens for a downward drag that starts while the\n * container is scrolled to the top. Pulling past `threshold` and releasing triggers\n * `onRefresh`; the SDK {@link Spinner} is shown while pulling and during the refresh.\n */\nexport function RefreshIndicator({\n onRefresh,\n children,\n threshold = 80,\n disabled,\n className,\n}: RefreshIndicatorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const startYRef = useRef<number | null>(null);\n const [pull, setPull] = useState(0);\n const [refreshing, setRefreshing] = useState(false);\n\n const engaged = startYRef.current !== null;\n\n const handleTouchStart = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const container = containerRef.current;\n if (!container || container.scrollTop > 0) return;\n const touch = event.touches[0];\n if (!touch) return;\n startYRef.current = touch.clientY;\n };\n\n const handleTouchMove = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const startY = startYRef.current;\n if (startY === null) return;\n const touch = event.touches[0];\n if (!touch) return;\n const delta = touch.clientY - startY;\n if (delta <= 0) {\n setPull(0);\n return;\n }\n setPull(applyResistance(delta, threshold));\n };\n\n const reset = (): void => {\n startYRef.current = null;\n setPull(0);\n };\n\n const handleTouchEnd = async (): Promise<void> => {\n if (disabled || refreshing) {\n reset();\n return;\n }\n const startY = startYRef.current;\n const triggered = startY !== null && pull >= threshold;\n if (!triggered) {\n reset();\n return;\n }\n startYRef.current = null;\n setRefreshing(true);\n setPull(threshold);\n try {\n await onRefresh();\n } finally {\n setRefreshing(false);\n setPull(0);\n }\n };\n\n const showIndicator = refreshing || pull > 0;\n const progress = Math.min(1, pull / threshold);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.wrapper, className)}\n data-refreshing={refreshing}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n <div\n className={styles.indicator}\n aria-hidden={!showIndicator}\n style={{\n opacity: refreshing ? 1 : progress,\n transform: `translateX(-50%) translateY(${showIndicator ? pull : 0}px) rotate(${progress * 360}deg)`,\n }}\n >\n <Spinner size=\"sm\" label=\"Atualizando\" />\n </div>\n <div\n className={cn(styles.content, (engaged || refreshing) && styles.dragging)}\n style={{ transform: `translateY(${showIndicator ? pull : 0}px)` }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAS,EAAgB,GAAkB,GAA2B;CAClE,IAAM,IAAM,IAAY;CACxB,OAAO,KAAK,IAAI,IAAW,GAAG,CAAG;AACrC;AASA,SAAgB,EAAiB,EAC7B,cACA,aACA,eAAY,IACZ,aACA,gBACsB;CACtB,IAAM,IAAe,EAAuB,IAAI,GAC1C,IAAY,EAAsB,IAAI,GACtC,CAAC,GAAM,KAAW,EAAS,CAAC,GAC5B,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,IAAU,EAAU,YAAY,MAEhC,KAAoB,MAA4C;EAClE,IAAI,KAAY,GAAY;EAC5B,IAAM,IAAY,EAAa;EAC/B,IAAI,CAAC,KAAa,EAAU,YAAY,GAAG;EAC3C,IAAM,IAAQ,EAAM,QAAQ;EACvB,MACL,EAAU,UAAU,EAAM;CAC9B,GAEM,KAAmB,MAA4C;EACjE,IAAI,KAAY,GAAY;EAC5B,IAAM,IAAS,EAAU;EACzB,IAAI,MAAW,MAAM;EACrB,IAAM,IAAQ,EAAM,QAAQ;EAC5B,IAAI,CAAC,GAAO;EACZ,IAAM,IAAQ,EAAM,UAAU;EAC9B,IAAI,KAAS,GAAG;GACZ,EAAQ,CAAC;GACT;EACJ;EACA,EAAQ,EAAgB,GAAO,CAAS,CAAC;CAC7C,GAEM,UAAoB;EAEtB,AADA,EAAU,UAAU,MACpB,EAAQ,CAAC;CACb,GAEM,IAAiB,YAA2B;EAC9C,IAAI,KAAY,GAAY;GACxB,EAAM;GACN;EACJ;EAGA,IAAI,EAFW,EAAU,YACI,QAAQ,KAAQ,IAC7B;GACZ,EAAM;GACN;EACJ;EAGA,AAFA,EAAU,UAAU,MACpB,EAAc,EAAI,GAClB,EAAQ,CAAS;EACjB,IAAI;GACA,MAAM,EAAU;EACpB,UAAU;GAEN,AADA,EAAc,EAAK,GACnB,EAAQ,CAAC;EACb;CACJ,GAEM,IAAgB,KAAc,IAAO,GACrC,IAAW,KAAK,IAAI,GAAG,IAAO,CAAS;CAE7C,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,SAAS,CAAS;EACvC,mBAAiB;EACjB,cAAc;EACd,aAAa;EACb,YAAY;YANhB,CAQI,kBAAC,OAAD;GACI,WAAW,EAAO;GAClB,eAAa,CAAC;GACd,OAAO;IACH,SAAS,IAAa,IAAI;IAC1B,WAAW,+BAA+B,IAAgB,IAAO,EAAE,aAAa,IAAW,IAAI;GACnG;aAEA,kBAAC,GAAD;IAAS,MAAK;IAAK,OAAM;GAAe,CAAA;EACvC,CAAA,GACL,kBAAC,OAAD;GACI,WAAW,EAAG,EAAO,UAAU,KAAW,MAAe,EAAO,QAAQ;GACxE,OAAO,EAAE,WAAW,cAAc,IAAgB,IAAO,EAAE,KAAK;GAE/D;EACA,CAAA,CACJ;;AAEb"}
1
+ {"version":3,"file":"RefreshIndicator.js","names":[],"sources":["../../../src/components/RefreshIndicator/RefreshIndicator.tsx"],"sourcesContent":["import { useRef, useState, type ReactNode, type TouchEvent } from \"react\";\nimport { Spinner } from \"@/components/Spinner\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RefreshIndicator.module.css\";\n\nexport interface RefreshIndicatorProps {\n /** Invoked when the user pulls past `threshold` and releases. May be async. */\n onRefresh: () => void | Promise<void>;\n /** Scrollable content to wrap. */\n children: ReactNode;\n /** Pixels the user must pull past to trigger a refresh. Defaults to `80`. */\n threshold?: number;\n /** When true, pull-to-refresh is inert. */\n disabled?: boolean;\n className?: string;\n}\n\n/** Apply resistance so the content lags behind the finger and the pull never runs away. */\nfunction applyResistance(distance: number, threshold: number): number {\n const cap = threshold * 1.5;\n return Math.min(distance / 2, cap);\n}\n\n/**\n * RefreshIndicator — pull-to-refresh wrapper for touch devices.\n *\n * Wraps scrollable `children` and listens for a downward drag that starts while the\n * container is scrolled to the top. Pulling past `threshold` and releasing triggers\n * `onRefresh`; the SDK {@link Spinner} is shown while pulling and during the refresh.\n */\nexport function RefreshIndicator({\n onRefresh,\n children,\n threshold = 80,\n disabled,\n className,\n}: RefreshIndicatorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const startYRef = useRef<number | null>(null);\n const [pull, setPull] = useState(0);\n const [refreshing, setRefreshing] = useState(false);\n /**\n * Whether a drag is in progress. Kept in state, not derived from\n * `startYRef.current`: a ref is not reactive, so reading it during render ties\n * the rendered class to a value React never re-renders for — it only looked\n * correct here because the touch handlers happen to call `setPull` too.\n */\n const [engaged, setEngaged] = useState(false);\n\n const handleTouchStart = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const container = containerRef.current;\n if (!container || container.scrollTop > 0) return;\n const touch = event.touches[0];\n if (!touch) return;\n startYRef.current = touch.clientY;\n setEngaged(true);\n };\n\n const handleTouchMove = (event: TouchEvent<HTMLDivElement>): void => {\n if (disabled || refreshing) return;\n const startY = startYRef.current;\n if (startY === null) return;\n const touch = event.touches[0];\n if (!touch) return;\n const delta = touch.clientY - startY;\n if (delta <= 0) {\n setPull(0);\n return;\n }\n setPull(applyResistance(delta, threshold));\n };\n\n const reset = (): void => {\n startYRef.current = null;\n setEngaged(false);\n setPull(0);\n };\n\n const handleTouchEnd = async (): Promise<void> => {\n if (disabled || refreshing) {\n reset();\n return;\n }\n const startY = startYRef.current;\n const triggered = startY !== null && pull >= threshold;\n if (!triggered) {\n reset();\n return;\n }\n startYRef.current = null;\n setEngaged(false);\n setRefreshing(true);\n setPull(threshold);\n try {\n await onRefresh();\n } finally {\n setRefreshing(false);\n setPull(0);\n }\n };\n\n const showIndicator = refreshing || pull > 0;\n const progress = Math.min(1, pull / threshold);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.wrapper, className)}\n data-refreshing={refreshing}\n onTouchStart={handleTouchStart}\n onTouchMove={handleTouchMove}\n onTouchEnd={handleTouchEnd}\n >\n <div\n className={styles.indicator}\n aria-hidden={!showIndicator}\n style={{\n opacity: refreshing ? 1 : progress,\n transform: `translateX(-50%) translateY(${showIndicator ? pull : 0}px) rotate(${progress * 360}deg)`,\n }}\n >\n <Spinner size=\"sm\" label=\"Atualizando\" />\n </div>\n <div\n className={cn(styles.content, (engaged || refreshing) && styles.dragging)}\n style={{ transform: `translateY(${showIndicator ? pull : 0}px)` }}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAkBA,SAAS,EAAgB,GAAkB,GAA2B;CAClE,IAAM,IAAM,IAAY;CACxB,OAAO,KAAK,IAAI,IAAW,GAAG,CAAG;AACrC;AASA,SAAgB,EAAiB,EAC7B,cACA,aACA,eAAY,IACZ,aACA,gBACsB;CACtB,IAAM,IAAe,EAAuB,IAAI,GAC1C,IAAY,EAAsB,IAAI,GACtC,CAAC,GAAM,KAAW,EAAS,CAAC,GAC5B,CAAC,GAAY,KAAiB,EAAS,EAAK,GAO5C,CAAC,GAAS,KAAc,EAAS,EAAK,GAEtC,KAAoB,MAA4C;EAClE,IAAI,KAAY,GAAY;EAC5B,IAAM,IAAY,EAAa;EAC/B,IAAI,CAAC,KAAa,EAAU,YAAY,GAAG;EAC3C,IAAM,IAAQ,EAAM,QAAQ;EACvB,MACL,EAAU,UAAU,EAAM,SAC1B,EAAW,EAAI;CACnB,GAEM,KAAmB,MAA4C;EACjE,IAAI,KAAY,GAAY;EAC5B,IAAM,IAAS,EAAU;EACzB,IAAI,MAAW,MAAM;EACrB,IAAM,IAAQ,EAAM,QAAQ;EAC5B,IAAI,CAAC,GAAO;EACZ,IAAM,IAAQ,EAAM,UAAU;EAC9B,IAAI,KAAS,GAAG;GACZ,EAAQ,CAAC;GACT;EACJ;EACA,EAAQ,EAAgB,GAAO,CAAS,CAAC;CAC7C,GAEM,UAAoB;EAGtB,AAFA,EAAU,UAAU,MACpB,EAAW,EAAK,GAChB,EAAQ,CAAC;CACb,GAEM,IAAiB,YAA2B;EAC9C,IAAI,KAAY,GAAY;GACxB,EAAM;GACN;EACJ;EAGA,IAAI,EAFW,EAAU,YACI,QAAQ,KAAQ,IAC7B;GACZ,EAAM;GACN;EACJ;EAIA,AAHA,EAAU,UAAU,MACpB,EAAW,EAAK,GAChB,EAAc,EAAI,GAClB,EAAQ,CAAS;EACjB,IAAI;GACA,MAAM,EAAU;EACpB,UAAU;GAEN,AADA,EAAc,EAAK,GACnB,EAAQ,CAAC;EACb;CACJ,GAEM,IAAgB,KAAc,IAAO,GACrC,IAAW,KAAK,IAAI,GAAG,IAAO,CAAS;CAE7C,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,SAAS,CAAS;EACvC,mBAAiB;EACjB,cAAc;EACd,aAAa;EACb,YAAY;YANhB,CAQI,kBAAC,OAAD;GACI,WAAW,EAAO;GAClB,eAAa,CAAC;GACd,OAAO;IACH,SAAS,IAAa,IAAI;IAC1B,WAAW,+BAA+B,IAAgB,IAAO,EAAE,aAAa,IAAW,IAAI;GACnG;aAEA,kBAAC,GAAD;IAAS,MAAK;IAAK,OAAM;GAAe,CAAA;EACvC,CAAA,GACL,kBAAC,OAAD;GACI,WAAW,EAAG,EAAO,UAAU,KAAW,MAAe,EAAO,QAAQ;GACxE,OAAO,EAAE,WAAW,cAAc,IAAgB,IAAO,EAAE,KAAK;GAE/D;EACA,CAAA,CACJ;;AAEb"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Tooltip.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({content:i,placement:a=`top`,children:o,disabled:s=!1,openDelay:c=150}){let[l,u]=(0,n.useState)(!1),d=(0,n.useId)(),f=null;function p(){s||(f&&clearTimeout(f),f=setTimeout(()=>u(!0),c))}function m(){f&&=(clearTimeout(f),null),u(!1)}let h=(0,n.cloneElement)(o,{onMouseEnter:p,onMouseLeave:m,onFocus:p,onBlur:m,"aria-describedby":l?d:void 0});return(0,r.jsxs)(`span`,{className:t.default.trigger,children:[h,l&&(0,r.jsx)(`span`,{id:d,role:`tooltip`,className:e.cn(t.default.bubble,t.default[a]),children:i})]})}exports.Tooltip=i;
1
+ const e=require("../../utils/cn.cjs"),t=require("./Tooltip.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({content:i,placement:a=`top`,children:o,disabled:s=!1,openDelay:c=150}){let[l,u]=(0,n.useState)(!1),d=(0,n.useId)(),f=(0,n.useRef)(null);function p(){f.current!==null&&(clearTimeout(f.current),f.current=null)}function m(){s||(p(),f.current=setTimeout(()=>u(!0),c))}function h(){p(),u(!1)}(0,n.useEffect)(()=>()=>{p()},[]);let g=(0,n.cloneElement)(o,{onMouseEnter:m,onMouseLeave:h,onFocus:m,onBlur:h,"aria-describedby":l?d:void 0});return(0,r.jsxs)(`span`,{className:t.default.trigger,children:[g,l&&(0,r.jsx)(`span`,{id:d,role:`tooltip`,className:e.cn(t.default.bubble,t.default[a]),children:i})]})}exports.Tooltip=i;
2
2
  //# sourceMappingURL=Tooltip.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.cjs","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import { cloneElement, useId, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function show(): void {\n if (disabled) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n setOpen(false);\n }\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"8HAyBA,SAAgB,EAAQ,CACpB,UACA,YAAY,MACZ,WACA,WAAW,GACX,YAAY,KACC,CACb,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA6B,EAAK,EACzC,GAAA,EAAA,EAAA,MAAA,CAAkB,EACpB,EAA8C,KAElD,SAAS,GAAa,CACd,IACA,GAAO,aAAa,CAAK,EAC7B,EAAQ,eAAiB,EAAQ,EAAI,EAAG,CAAS,EACrD,CAEA,SAAS,GAAa,CAClB,AAEI,KADA,aAAa,CAAK,EACV,MAEZ,EAAQ,EAAK,CACjB,CAEA,IAAM,GAAA,EAAA,EAAA,aAAA,CAAuB,EAAU,CACnC,aAAc,EACd,aAAc,EACd,QAAS,EACT,OAAQ,EACR,mBAAoB,EAAO,EAAY,IAAA,EAC3C,CAA4B,EAE5B,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,iBAAxB,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,GAAI,EACJ,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,EAAA,QAAO,EAAU,WAE7C,CACC,CAAA,CAER,GAEd"}
1
+ {"version":3,"file":"Tooltip.cjs","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"8HA2BA,SAAgB,EAAQ,CACpB,UACA,YAAY,MACZ,WACA,WAAW,GACX,YAAY,KACC,CACb,GAAM,CAAC,EAAM,IAAA,EAAA,EAAA,SAAA,CAA6B,EAAK,EACzC,GAAA,EAAA,EAAA,MAAA,CAAkB,EASlB,GAAA,EAAA,EAAA,OAAA,CAAwD,IAAI,EAElE,SAAS,GAAmB,CACpB,EAAS,UAAY,OACrB,aAAa,EAAS,OAAO,EAC7B,EAAS,QAAU,KAE3B,CAEA,SAAS,GAAa,CACd,IACJ,EAAW,EACX,EAAS,QAAU,eAAiB,EAAQ,EAAI,EAAG,CAAS,EAChE,CAEA,SAAS,GAAa,CAClB,EAAW,EACX,EAAQ,EAAK,CACjB,EAEA,EAAA,EAAA,UAAA,SACiB,CACT,EAAW,CACf,EACD,CAAC,CAAC,EAEL,IAAM,GAAA,EAAA,EAAA,aAAA,CAAuB,EAAU,CACnC,aAAc,EACd,aAAc,EACd,QAAS,EACT,OAAQ,EACR,mBAAoB,EAAO,EAAY,IAAA,EAC3C,CAA4B,EAE5B,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,iBAAxB,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,GAAI,EACJ,KAAK,UACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,EAAA,QAAO,EAAU,WAE7C,CACC,CAAA,CAER,GAEd"}
@@ -1,34 +1,40 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
2
  import t from "./Tooltip.module.js";
3
- import { cloneElement as n, useId as r, useState as i } from "react";
4
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
3
+ import { cloneElement as n, useEffect as r, useId as i, useRef as a, useState as o } from "react";
4
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
5
5
  //#region src/components/Tooltip/Tooltip.tsx
6
- function s({ content: s, placement: c = "top", children: l, disabled: u = !1, openDelay: d = 150 }) {
7
- let [f, p] = i(!1), m = r(), h = null;
8
- function g() {
9
- u || (h && clearTimeout(h), h = setTimeout(() => p(!0), d));
6
+ function l({ content: l, placement: u = "top", children: d, disabled: f = !1, openDelay: p = 150 }) {
7
+ let [m, h] = o(!1), g = i(), _ = a(null);
8
+ function v() {
9
+ _.current !== null && (clearTimeout(_.current), _.current = null);
10
10
  }
11
- function _() {
12
- h &&= (clearTimeout(h), null), p(!1);
11
+ function y() {
12
+ f || (v(), _.current = setTimeout(() => h(!0), p));
13
13
  }
14
- let v = n(l, {
15
- onMouseEnter: g,
16
- onMouseLeave: _,
17
- onFocus: g,
18
- onBlur: _,
19
- "aria-describedby": f ? m : void 0
14
+ function b() {
15
+ v(), h(!1);
16
+ }
17
+ r(() => () => {
18
+ v();
19
+ }, []);
20
+ let x = n(d, {
21
+ onMouseEnter: y,
22
+ onMouseLeave: b,
23
+ onFocus: y,
24
+ onBlur: b,
25
+ "aria-describedby": m ? g : void 0
20
26
  });
21
- return /* @__PURE__ */ o("span", {
27
+ return /* @__PURE__ */ c("span", {
22
28
  className: t.trigger,
23
- children: [v, f && /* @__PURE__ */ a("span", {
24
- id: m,
29
+ children: [x, m && /* @__PURE__ */ s("span", {
30
+ id: g,
25
31
  role: "tooltip",
26
- className: e(t.bubble, t[c]),
27
- children: s
32
+ className: e(t.bubble, t[u]),
33
+ children: l
28
34
  })]
29
35
  });
30
36
  }
31
37
  //#endregion
32
- export { s as Tooltip };
38
+ export { l as Tooltip };
33
39
 
34
40
  //# sourceMappingURL=Tooltip.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import { cloneElement, useId, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n function show(): void {\n if (disabled) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n setOpen(false);\n }\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AAyBA,SAAgB,EAAQ,EACpB,YACA,eAAY,OACZ,aACA,cAAW,IACX,eAAY,OACC;CACb,IAAM,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAM,GACpB,IAA8C;CAElD,SAAS,IAAa;EACd,MACA,KAAO,aAAa,CAAK,GAC7B,IAAQ,iBAAiB,EAAQ,EAAI,GAAG,CAAS;CACrD;CAEA,SAAS,IAAa;EAKlB,AAJA,AAEI,OADA,aAAa,CAAK,GACV,OAEZ,EAAQ,EAAK;CACjB;CAEA,IAAM,IAAU,EAAa,GAAU;EACnC,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EACR,oBAAoB,IAAO,IAAY,KAAA;CAC3C,CAA4B;CAE5B,OACI,kBAAC,QAAD;EAAM,WAAW,EAAO;YAAxB,CACK,GACA,KACG,kBAAC,QAAD;GACI,IAAI;GACJ,MAAK;GACL,WAAW,EAAG,EAAO,QAAQ,EAAO,EAAU;aAE7C;EACC,CAAA,CAER;;AAEd"}
1
+ {"version":3,"file":"Tooltip.js","names":[],"sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/* eslint-disable react-hooks/refs -- the handlers below read the timer ref, and they only\n run on user events (hover/focus), never during render */\nimport { cloneElement, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Tooltip.module.css\";\n\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\n\nexport interface TooltipProps {\n /** Tooltip content. Plain text recommended; rich content also works. */\n content: ReactNode;\n /** Placement relative to the trigger. Default: `\"top\"`. */\n placement?: TooltipPlacement;\n /** Single React element to attach hover/focus listeners to. */\n children: ReactElement;\n /** Disable the tooltip without changing the trigger. */\n disabled?: boolean;\n /** Delay (ms) before showing the tooltip. Default: 150. */\n openDelay?: number;\n}\n\n/**\n * Lightweight tooltip. Shows on hover and on focus (keyboard-friendly). Wraps\n * a single child element via `cloneElement`, so the trigger keeps its own ref\n * and props.\n */\nexport function Tooltip({\n content,\n placement = \"top\",\n children,\n disabled = false,\n openDelay = 150,\n}: TooltipProps) {\n const [open, setOpen] = useState<boolean>(false);\n const tooltipId = useId();\n /**\n * Handle of the pending open timer.\n *\n * A ref, not a `let` in the component body: that binding is recreated on every\n * render, so any re-render between `show` and `hide` (a parent update, a state\n * change elsewhere) dropped the handle and `clearTimeout` silently cancelled\n * nothing — the tooltip then opened after the pointer had already left.\n */\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n function clearTimer(): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }\n\n function show(): void {\n if (disabled) return;\n clearTimer();\n timerRef.current = setTimeout(() => setOpen(true), openDelay);\n }\n\n function hide(): void {\n clearTimer();\n setOpen(false);\n }\n\n useEffect(() => {\n return () => {\n clearTimer();\n };\n }, []);\n\n const trigger = cloneElement(children, {\n onMouseEnter: show,\n onMouseLeave: hide,\n onFocus: show,\n onBlur: hide,\n \"aria-describedby\": open ? tooltipId : undefined,\n } as Record<string, unknown>);\n\n return (\n <span className={styles.trigger}>\n {trigger}\n {open && (\n <span\n id={tooltipId}\n role=\"tooltip\"\n className={cn(styles.bubble, styles[placement])}\n >\n {content}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AA2BA,SAAgB,EAAQ,EACpB,YACA,eAAY,OACZ,aACA,cAAW,IACX,eAAY,OACC;CACb,IAAM,CAAC,GAAM,KAAW,EAAkB,EAAK,GACzC,IAAY,EAAM,GASlB,IAAW,EAA6C,IAAI;CAElE,SAAS,IAAmB;EACxB,AAAI,EAAS,YAAY,SACrB,aAAa,EAAS,OAAO,GAC7B,EAAS,UAAU;CAE3B;CAEA,SAAS,IAAa;EACd,MACJ,EAAW,GACX,EAAS,UAAU,iBAAiB,EAAQ,EAAI,GAAG,CAAS;CAChE;CAEA,SAAS,IAAa;EAElB,AADA,EAAW,GACX,EAAQ,EAAK;CACjB;CAEA,cACiB;EACT,EAAW;CACf,GACD,CAAC,CAAC;CAEL,IAAM,IAAU,EAAa,GAAU;EACnC,cAAc;EACd,cAAc;EACd,SAAS;EACT,QAAQ;EACR,oBAAoB,IAAO,IAAY,KAAA;CAC3C,CAA4B;CAE5B,OACI,kBAAC,QAAD;EAAM,WAAW,EAAO;YAAxB,CACK,GACA,KACG,kBAAC,QAAD;GACI,IAAI;GACJ,MAAK;GACL,WAAW,EAAG,EAAO,QAAQ,EAAO,EAAU;aAE7C;EACC,CAAA,CAER;;AAEd"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../utils/cn.cjs"),t=require("../../utils/compare-values.cjs"),n=require("./VirtualTable.module.cjs");let r=require("react"),i=require("react/jsx-runtime");function a(e){if(e===`right`)return n.default.alignRight;if(e===`center`)return n.default.alignCenter}function o({data:o,columns:s,rowHeight:c,height:l,overscan:u=4,rowKey:d,initialSort:f,onRowClick:p,emptyMessage:m=`Nenhum registro encontrado.`,caption:h,scrollToIndex:g,className:_,...v}){let y=(0,r.useRef)(null),[b,x]=(0,r.useState)(0),[S,C]=(0,r.useState)(0),[w,T]=(0,r.useState)(f??null);(0,r.useEffect)(()=>{let e=y.current;if(!e||(C(e.clientHeight),typeof ResizeObserver>`u`))return;let t=new ResizeObserver(()=>C(e.clientHeight));return t.observe(e),()=>t.disconnect()},[]),(0,r.useEffect)(()=>{let e=y.current;!e||g==null||(e.scrollTop=Math.max(0,Math.min(g,o.length-1))*c)},[g,c,o.length]);let E=(0,r.useMemo)(()=>{if(!w)return o;let e=w.direction===`asc`?1:-1;return[...o].sort((n,r)=>t.compareValues(n[w.key],r[w.key])*e)},[o,w]),D=Math.max(0,Math.floor(b/c)-u),O=Math.ceil(S/c)+u*2,k=Math.min(E.length,D+O),A=E.slice(D,k),j=(0,r.useCallback)(e=>{T(t=>!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null);let t=y.current;t&&(t.scrollTop=0),x(0)},[]);return(0,i.jsx)(`div`,{ref:y,className:e.cn(n.default.scroll,_),style:{height:l},onScroll:e=>x(e.currentTarget.scrollTop),...v,children:(0,i.jsxs)(`table`,{className:n.default.table,"aria-rowcount":E.length,children:[h?(0,i.jsx)(`caption`,{className:n.default.caption,children:h}):null,(0,i.jsx)(`thead`,{className:n.default.head,children:(0,i.jsx)(`tr`,{children:s.map(t=>{let r=w?.key===t.key;return(0,i.jsx)(`th`,{scope:`col`,className:e.cn(n.default.th,a(t.align)),style:{width:t.width},"aria-sort":r?w?.direction===`asc`?`ascending`:`descending`:t.sortable?`none`:void 0,children:t.sortable?(0,i.jsxs)(`button`,{type:`button`,className:n.default.sortButton,onClick:()=>j(t.key),children:[t.header,(0,i.jsx)(`span`,{className:n.default.sortIndicator,"aria-hidden":!0,children:r?w?.direction===`asc`?`▲`:`▼`:`↕`})]}):t.header},String(t.key))})})}),(0,i.jsx)(`tbody`,{children:E.length===0?(0,i.jsx)(`tr`,{children:(0,i.jsx)(`td`,{className:n.default.emptyRow,colSpan:s.length,children:m})}):(0,i.jsxs)(i.Fragment,{children:[D>0&&(0,i.jsx)(`tr`,{"aria-hidden":!0,style:{height:D*c}}),A.map((t,r)=>{let o=D+r;return(0,i.jsx)(`tr`,{"aria-rowindex":o+1,className:e.cn(n.default.tr,p&&n.default.clickable),style:{height:c},tabIndex:p?0:void 0,onClick:p?()=>p(t,o):void 0,onKeyDown:p?e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),p(t,o))}:void 0,children:s.map(r=>(0,i.jsx)(`td`,{className:e.cn(n.default.td,a(r.align)),style:{width:r.width},children:r.render?r.render(t,o):t[r.key]??null},String(r.key)))},d?d(t,o):o)}),k<E.length&&(0,i.jsx)(`tr`,{"aria-hidden":!0,style:{height:(E.length-k)*c}})]})})]})})}exports.VirtualTable=o;
2
+ //# sourceMappingURL=VirtualTable.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualTable.cjs","names":[],"sources":["../../../src/components/VirtualTable/VirtualTable.tsx"],"sourcesContent":["import {\n type HTMLAttributes,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { cn } from \"@/utils/cn\";\nimport { compareValues } from \"@/utils/compare-values\";\n\nimport type { TableAlign } from \"../Table\";\nimport styles from \"./VirtualTable.module.css\";\n\nexport type VirtualTableSortDirection = \"asc\" | \"desc\";\n\nexport interface VirtualTableSort<T> {\n key: keyof T;\n direction: VirtualTableSortDirection;\n}\n\nexport interface VirtualTableColumn<T> {\n /** Property of the row this column reads from. Doubles as the cell key. */\n key: keyof T;\n /** Column heading. */\n header: ReactNode;\n /** Custom cell renderer. Defaults to `row[key]`. */\n render?: (row: T, index: number) => ReactNode;\n /** Enable click-to-sort on this column's header. */\n sortable?: boolean;\n /** Text alignment. */\n align?: TableAlign;\n /**\n * Column width. Recommended for every column: virtualized rows enter and\n * leave the DOM as you scroll, so letting the browser auto-size columns from\n * whatever is currently rendered makes them jump mid-scroll.\n */\n width?: string | number;\n}\n\nexport interface VirtualTableProps<T> extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Full dataset. Every row is accounted for; only the visible ones render. */\n data: T[];\n /** Column definitions. */\n columns: VirtualTableColumn<T>[];\n /**\n * Row height in px. Must be uniform and must match what the CSS produces,\n * because it is what maps scroll offset to row index.\n */\n rowHeight: number;\n /** Height of the scroll viewport — a number of px or any CSS length. */\n height: number | string;\n /** Rows rendered above and below the viewport. Default `4`. */\n overscan?: number;\n /** Stable key extractor. Defaults to the row's index in `data`. */\n rowKey?: (row: T, index: number) => string | number;\n /** Sort applied before any header interaction. */\n initialSort?: VirtualTableSort<T>;\n /** Called when a row is activated by click, `Enter` or `Space`. */\n onRowClick?: (row: T, index: number) => void;\n /** Content shown when `data` is empty. */\n emptyMessage?: ReactNode;\n /** Accessible name for the table. Rendered as a visually hidden `<caption>`. */\n caption?: ReactNode;\n /** Scroll this row into view whenever it changes. */\n scrollToIndex?: number;\n}\n\n/** Resolve a column's alignment to its CSS module class. */\nfunction alignClass(align: TableAlign | undefined): string | undefined {\n if (align === \"right\") return styles.alignRight;\n if (align === \"center\") return styles.alignCenter;\n return undefined;\n}\n\n/**\n * A table that stays responsive at 10k+ rows by rendering only the visible window.\n *\n * `Table` renders every row it is given and `DataTable` paginates to keep that\n * count small; neither has an answer for \"show me all 40 000 rows in one\n * scrollable grid\". This does, at the cost of one constraint: **`rowHeight` must\n * be uniform**, since mapping a scroll offset to a row index is what makes the\n * window computable without measuring anything.\n *\n * It stays a real `<table>`. The window is produced by two spacer rows — one\n * above the visible slice, one below — instead of absolutely positioning rows.\n * That is deliberate: `position: absolute` on `<tr>` collapses table layout, so\n * every column width would have to be computed by hand, and the element would\n * stop being a table for assistive technology. With spacer rows the browser keeps\n * doing column layout and screen readers keep announcing a grid.\n *\n * Because only a slice is in the DOM, `aria-rowcount` on the table and\n * `aria-rowindex` on each row carry the real numbers — without them a screen\n * reader announces \"row 3 of 20\" while the user is on row 5003 of 40 000.\n *\n * @example\n * <VirtualTable\n * data={rows}\n * columns={[\n * { key: \"id\", header: \"#\", width: 80, sortable: true },\n * { key: \"name\", header: \"Nome\", width: 240, sortable: true },\n * { key: \"total\", header: \"Total\", align: \"right\", width: 120, sortable: true },\n * ]}\n * rowHeight={40}\n * height={480}\n * rowKey={(row) => row.id}\n * />\n */\nexport function VirtualTable<T>({\n data,\n columns,\n rowHeight,\n height,\n overscan = 4,\n rowKey,\n initialSort,\n onRowClick,\n emptyMessage = \"Nenhum registro encontrado.\",\n caption,\n scrollToIndex,\n className,\n ...rest\n}: VirtualTableProps<T>) {\n const scrollRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState<number>(0);\n const [viewport, setViewport] = useState<number>(0);\n const [sort, setSort] = useState<VirtualTableSort<T> | null>(initialSort ?? null);\n\n useEffect(() => {\n const element = scrollRef.current;\n if (!element) return;\n setViewport(element.clientHeight);\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => setViewport(element.clientHeight));\n observer.observe(element);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n const element = scrollRef.current;\n if (!element || scrollToIndex == null) return;\n element.scrollTop = Math.max(0, Math.min(scrollToIndex, data.length - 1)) * rowHeight;\n }, [scrollToIndex, rowHeight, data.length]);\n\n const sorted = useMemo<T[]>(() => {\n if (!sort) return data;\n const factor = sort.direction === \"asc\" ? 1 : -1;\n return [...data].sort((a, b) => compareValues(a[sort.key], b[sort.key]) * factor);\n }, [data, sort]);\n\n const start = Math.max(0, Math.floor(scrollTop / rowHeight) - overscan);\n const windowSize = Math.ceil(viewport / rowHeight) + overscan * 2;\n const end = Math.min(sorted.length, start + windowSize);\n const slice = sorted.slice(start, end);\n\n /**\n * Advance a column through asc → desc → unsorted, and jump back to the top.\n *\n * The scroll reset happens here rather than in an effect keyed on the sort\n * state for two reasons: this is the actual event that invalidates the offset\n * (keeping it would leave the user at the same pixel of a different dataset,\n * which reads as the table having jumped somewhere random), and an effect would\n * also fire on mount — clobbering an initial `scrollToIndex`.\n */\n const toggleSort = useCallback((key: keyof T): void => {\n setSort((current) => {\n if (!current || current.key !== key) return { key, direction: \"asc\" };\n if (current.direction === \"asc\") return { key, direction: \"desc\" };\n return null;\n });\n const element = scrollRef.current;\n if (element) element.scrollTop = 0;\n setScrollTop(0);\n }, []);\n\n return (\n <div\n ref={scrollRef}\n className={cn(styles.scroll, className)}\n style={{ height }}\n onScroll={(event) => setScrollTop(event.currentTarget.scrollTop)}\n {...rest}\n >\n <table className={styles.table} aria-rowcount={sorted.length}>\n {caption ? <caption className={styles.caption}>{caption}</caption> : null}\n <thead className={styles.head}>\n <tr>\n {columns.map((column) => {\n const isSorted = sort?.key === column.key;\n return (\n <th\n key={String(column.key)}\n scope=\"col\"\n className={cn(styles.th, alignClass(column.align))}\n style={{ width: column.width }}\n aria-sort={\n isSorted\n ? sort?.direction === \"asc\"\n ? \"ascending\"\n : \"descending\"\n : column.sortable\n ? \"none\"\n : undefined\n }\n >\n {column.sortable ? (\n <button\n type=\"button\"\n className={styles.sortButton}\n onClick={() => toggleSort(column.key)}\n >\n {column.header}\n <span className={styles.sortIndicator} aria-hidden>\n {isSorted\n ? sort?.direction === \"asc\"\n ? \"▲\"\n : \"▼\"\n : \"↕\"}\n </span>\n </button>\n ) : (\n column.header\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n <tbody>\n {sorted.length === 0 ? (\n <tr>\n <td className={styles.emptyRow} colSpan={columns.length}>\n {emptyMessage}\n </td>\n </tr>\n ) : (\n <>\n {start > 0 && <tr aria-hidden style={{ height: start * rowHeight }} />}\n {slice.map((row, offset) => {\n const index = start + offset;\n return (\n <tr\n key={rowKey ? rowKey(row, index) : index}\n aria-rowindex={index + 1}\n className={cn(styles.tr, onRowClick && styles.clickable)}\n style={{ height: rowHeight }}\n tabIndex={onRowClick ? 0 : undefined}\n onClick={\n onRowClick ? () => onRowClick(row, index) : undefined\n }\n onKeyDown={\n onRowClick\n ? (event) => {\n if (\n event.key === \"Enter\" ||\n event.key === \" \"\n ) {\n event.preventDefault();\n onRowClick(row, index);\n }\n }\n : undefined\n }\n >\n {columns.map((column) => (\n <td\n key={String(column.key)}\n className={cn(styles.td, alignClass(column.align))}\n style={{ width: column.width }}\n >\n {column.render\n ? column.render(row, index)\n : ((row[column.key] as ReactNode) ?? null)}\n </td>\n ))}\n </tr>\n );\n })}\n {end < sorted.length && (\n <tr\n aria-hidden\n style={{ height: (sorted.length - end) * rowHeight }}\n />\n )}\n </>\n )}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ import { cn as e } from "../../utils/cn.js";
2
+ import { compareValues as t } from "../../utils/compare-values.js";
3
+ import n from "./VirtualTable.module.js";
4
+ import { useCallback as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
5
+ import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
6
+ //#region src/components/VirtualTable/VirtualTable.tsx
7
+ function d(e) {
8
+ if (e === "right") return n.alignRight;
9
+ if (e === "center") return n.alignCenter;
10
+ }
11
+ function f({ data: f, columns: p, rowHeight: m, height: h, overscan: g = 4, rowKey: _, initialSort: v, onRowClick: y, emptyMessage: b = "Nenhum registro encontrado.", caption: x, scrollToIndex: S, className: C, ...w }) {
12
+ let T = o(null), [E, D] = s(0), [O, k] = s(0), [A, j] = s(v ?? null);
13
+ i(() => {
14
+ let e = T.current;
15
+ if (!e || (k(e.clientHeight), typeof ResizeObserver > "u")) return;
16
+ let t = new ResizeObserver(() => k(e.clientHeight));
17
+ return t.observe(e), () => t.disconnect();
18
+ }, []), i(() => {
19
+ let e = T.current;
20
+ !e || S == null || (e.scrollTop = Math.max(0, Math.min(S, f.length - 1)) * m);
21
+ }, [
22
+ S,
23
+ m,
24
+ f.length
25
+ ]);
26
+ let M = a(() => {
27
+ if (!A) return f;
28
+ let e = A.direction === "asc" ? 1 : -1;
29
+ return [...f].sort((n, r) => t(n[A.key], r[A.key]) * e);
30
+ }, [f, A]), N = Math.max(0, Math.floor(E / m) - g), P = Math.ceil(O / m) + g * 2, F = Math.min(M.length, N + P), I = M.slice(N, F), L = r((e) => {
31
+ j((t) => !t || t.key !== e ? {
32
+ key: e,
33
+ direction: "asc"
34
+ } : t.direction === "asc" ? {
35
+ key: e,
36
+ direction: "desc"
37
+ } : null);
38
+ let t = T.current;
39
+ t && (t.scrollTop = 0), D(0);
40
+ }, []);
41
+ return /* @__PURE__ */ l("div", {
42
+ ref: T,
43
+ className: e(n.scroll, C),
44
+ style: { height: h },
45
+ onScroll: (e) => D(e.currentTarget.scrollTop),
46
+ ...w,
47
+ children: /* @__PURE__ */ u("table", {
48
+ className: n.table,
49
+ "aria-rowcount": M.length,
50
+ children: [
51
+ x ? /* @__PURE__ */ l("caption", {
52
+ className: n.caption,
53
+ children: x
54
+ }) : null,
55
+ /* @__PURE__ */ l("thead", {
56
+ className: n.head,
57
+ children: /* @__PURE__ */ l("tr", { children: p.map((t) => {
58
+ let r = A?.key === t.key;
59
+ return /* @__PURE__ */ l("th", {
60
+ scope: "col",
61
+ className: e(n.th, d(t.align)),
62
+ style: { width: t.width },
63
+ "aria-sort": r ? A?.direction === "asc" ? "ascending" : "descending" : t.sortable ? "none" : void 0,
64
+ children: t.sortable ? /* @__PURE__ */ u("button", {
65
+ type: "button",
66
+ className: n.sortButton,
67
+ onClick: () => L(t.key),
68
+ children: [t.header, /* @__PURE__ */ l("span", {
69
+ className: n.sortIndicator,
70
+ "aria-hidden": !0,
71
+ children: r ? A?.direction === "asc" ? "▲" : "▼" : "↕"
72
+ })]
73
+ }) : t.header
74
+ }, String(t.key));
75
+ }) })
76
+ }),
77
+ /* @__PURE__ */ l("tbody", { children: M.length === 0 ? /* @__PURE__ */ l("tr", { children: /* @__PURE__ */ l("td", {
78
+ className: n.emptyRow,
79
+ colSpan: p.length,
80
+ children: b
81
+ }) }) : /* @__PURE__ */ u(c, { children: [
82
+ N > 0 && /* @__PURE__ */ l("tr", {
83
+ "aria-hidden": !0,
84
+ style: { height: N * m }
85
+ }),
86
+ I.map((t, r) => {
87
+ let i = N + r;
88
+ return /* @__PURE__ */ l("tr", {
89
+ "aria-rowindex": i + 1,
90
+ className: e(n.tr, y && n.clickable),
91
+ style: { height: m },
92
+ tabIndex: y ? 0 : void 0,
93
+ onClick: y ? () => y(t, i) : void 0,
94
+ onKeyDown: y ? (e) => {
95
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), y(t, i));
96
+ } : void 0,
97
+ children: p.map((r) => /* @__PURE__ */ l("td", {
98
+ className: e(n.td, d(r.align)),
99
+ style: { width: r.width },
100
+ children: r.render ? r.render(t, i) : t[r.key] ?? null
101
+ }, String(r.key)))
102
+ }, _ ? _(t, i) : i);
103
+ }),
104
+ F < M.length && /* @__PURE__ */ l("tr", {
105
+ "aria-hidden": !0,
106
+ style: { height: (M.length - F) * m }
107
+ })
108
+ ] }) })
109
+ ]
110
+ })
111
+ });
112
+ }
113
+ //#endregion
114
+ export { f as VirtualTable };
115
+
116
+ //# sourceMappingURL=VirtualTable.js.map