tempest-react-sdk 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -7
- package/dist/access/Can.cjs +1 -1
- package/dist/access/Can.cjs.map +1 -1
- package/dist/access/Can.js +3 -3
- package/dist/access/Can.js.map +1 -1
- package/dist/audio/audio-bus.cjs +2 -0
- package/dist/audio/audio-bus.cjs.map +1 -0
- package/dist/audio/audio-bus.js +116 -0
- package/dist/audio/audio-bus.js.map +1 -0
- package/dist/audio/level-meter.cjs +1 -1
- package/dist/audio/level-meter.cjs.map +1 -1
- package/dist/audio/level-meter.js +15 -15
- package/dist/audio/level-meter.js.map +1 -1
- package/dist/audio/noise-floor.cjs +2 -0
- package/dist/audio/noise-floor.cjs.map +1 -0
- package/dist/audio/noise-floor.js +27 -0
- package/dist/audio/noise-floor.js.map +1 -0
- package/dist/audio/use-audio-bus.cjs +2 -0
- package/dist/audio/use-audio-bus.cjs.map +1 -0
- package/dist/audio/use-audio-bus.js +11 -0
- package/dist/audio/use-audio-bus.js.map +1 -0
- package/dist/audio/voice-activity.cjs +2 -0
- package/dist/audio/voice-activity.cjs.map +1 -0
- package/dist/audio/voice-activity.js +26 -0
- package/dist/audio/voice-activity.js.map +1 -0
- package/dist/audio/voice-chain-stages.cjs +2 -0
- package/dist/audio/voice-chain-stages.cjs.map +1 -0
- package/dist/audio/voice-chain-stages.js +82 -0
- package/dist/audio/voice-chain-stages.js.map +1 -0
- package/dist/audio/voice-chain.cjs +2 -0
- package/dist/audio/voice-chain.cjs.map +1 -0
- package/dist/audio/voice-chain.js +74 -0
- package/dist/audio/voice-chain.js.map +1 -0
- package/dist/auth/lazy-with-retry.cjs.map +1 -1
- package/dist/auth/lazy-with-retry.js.map +1 -1
- package/dist/auth/passkey.cjs.map +1 -1
- package/dist/auth/passkey.js.map +1 -1
- package/dist/br/BrazilStateCitySelect.cjs +1 -1
- package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
- package/dist/br/BrazilStateCitySelect.js +36 -28
- package/dist/br/BrazilStateCitySelect.js.map +1 -1
- package/dist/br/data/br-centroids.cjs +1 -1
- package/dist/br/data/br-centroids.cjs.map +1 -1
- package/dist/br/data/br-centroids.js +11105 -11045
- package/dist/br/data/br-centroids.js.map +1 -1
- package/dist/br/data/br-locations.cjs +1 -1
- package/dist/br/data/br-locations.cjs.map +1 -1
- package/dist/br/data/br-locations.js +5803 -137
- package/dist/br/data/br-locations.js.map +1 -1
- package/dist/br/data/br-uf-geo.cjs +1 -1
- package/dist/br/data/br-uf-geo.cjs.map +1 -1
- package/dist/br/data/br-uf-geo.js +5721 -6777
- package/dist/br/data/br-uf-geo.js.map +1 -1
- package/dist/br/data/mun/AC.cjs +1 -1
- package/dist/br/data/mun/AC.cjs.map +1 -1
- package/dist/br/data/mun/AC.js +681 -689
- package/dist/br/data/mun/AC.js.map +1 -1
- package/dist/br/data/mun/AL.cjs +1 -1
- package/dist/br/data/mun/AL.cjs.map +1 -1
- package/dist/br/data/mun/AL.js +949 -844
- package/dist/br/data/mun/AL.js.map +1 -1
- package/dist/br/data/mun/AM.cjs +1 -1
- package/dist/br/data/mun/AM.cjs.map +1 -1
- package/dist/br/data/mun/AM.js +4337 -4218
- package/dist/br/data/mun/AM.js.map +1 -1
- package/dist/br/data/mun/AP.cjs +1 -1
- package/dist/br/data/mun/AP.cjs.map +1 -1
- package/dist/br/data/mun/AP.js +786 -780
- package/dist/br/data/mun/AP.js.map +1 -1
- package/dist/br/data/mun/BA.cjs +1 -1
- package/dist/br/data/mun/BA.cjs.map +1 -1
- package/dist/br/data/mun/BA.js +6329 -5633
- package/dist/br/data/mun/BA.js.map +1 -1
- package/dist/br/data/mun/CE.cjs +1 -1
- package/dist/br/data/mun/CE.cjs.map +1 -1
- package/dist/br/data/mun/CE.js +2544 -2487
- package/dist/br/data/mun/CE.js.map +1 -1
- package/dist/br/data/mun/DF.cjs +1 -1
- package/dist/br/data/mun/DF.cjs.map +1 -1
- package/dist/br/data/mun/DF.js +17 -16
- package/dist/br/data/mun/DF.js.map +1 -1
- package/dist/br/data/mun/ES.cjs +1 -1
- package/dist/br/data/mun/ES.cjs.map +1 -1
- package/dist/br/data/mun/ES.js +1101 -1070
- package/dist/br/data/mun/ES.js.map +1 -1
- package/dist/br/data/mun/GO.cjs +1 -1
- package/dist/br/data/mun/GO.cjs.map +1 -1
- package/dist/br/data/mun/GO.js +4632 -4636
- package/dist/br/data/mun/GO.js.map +1 -1
- package/dist/br/data/mun/MA.cjs +1 -1
- package/dist/br/data/mun/MA.cjs.map +1 -1
- package/dist/br/data/mun/MA.js +3603 -3457
- package/dist/br/data/mun/MA.js.map +1 -1
- package/dist/br/data/mun/MG.cjs +1 -1
- package/dist/br/data/mun/MG.cjs.map +1 -1
- package/dist/br/data/mun/MG.js +12716 -11739
- package/dist/br/data/mun/MG.js.map +1 -1
- package/dist/br/data/mun/MS.cjs +1 -1
- package/dist/br/data/mun/MS.cjs.map +1 -1
- package/dist/br/data/mun/MS.js +2182 -2335
- package/dist/br/data/mun/MS.js.map +1 -1
- package/dist/br/data/mun/MT.cjs +1 -1
- package/dist/br/data/mun/MT.cjs.map +1 -1
- package/dist/br/data/mun/MT.js +4808 -4896
- package/dist/br/data/mun/MT.js.map +1 -1
- package/dist/br/data/mun/PA.cjs +1 -1
- package/dist/br/data/mun/PA.cjs.map +1 -1
- package/dist/br/data/mun/PA.js +4981 -4901
- package/dist/br/data/mun/PA.js.map +1 -1
- package/dist/br/data/mun/PB.cjs +1 -1
- package/dist/br/data/mun/PB.cjs.map +1 -1
- package/dist/br/data/mun/PB.js +2418 -1956
- package/dist/br/data/mun/PB.js.map +1 -1
- package/dist/br/data/mun/PE.cjs +1 -1
- package/dist/br/data/mun/PE.cjs.map +1 -1
- package/dist/br/data/mun/PE.js +2164 -1987
- package/dist/br/data/mun/PE.js.map +1 -1
- package/dist/br/data/mun/PI.cjs +1 -1
- package/dist/br/data/mun/PI.cjs.map +1 -1
- package/dist/br/data/mun/PI.js +2982 -2844
- package/dist/br/data/mun/PI.js.map +1 -1
- package/dist/br/data/mun/PR.cjs +1 -1
- package/dist/br/data/mun/PR.cjs.map +1 -1
- package/dist/br/data/mun/PR.js +5425 -5240
- package/dist/br/data/mun/PR.js.map +1 -1
- package/dist/br/data/mun/RJ.cjs +1 -1
- package/dist/br/data/mun/RJ.cjs.map +1 -1
- package/dist/br/data/mun/RJ.js +1395 -1178
- package/dist/br/data/mun/RJ.js.map +1 -1
- package/dist/br/data/mun/RN.cjs +1 -1
- package/dist/br/data/mun/RN.cjs.map +1 -1
- package/dist/br/data/mun/RN.js +1608 -1382
- package/dist/br/data/mun/RN.js.map +1 -1
- package/dist/br/data/mun/RO.cjs +1 -1
- package/dist/br/data/mun/RO.cjs.map +1 -1
- package/dist/br/data/mun/RO.js +1392 -1470
- package/dist/br/data/mun/RO.js.map +1 -1
- package/dist/br/data/mun/RR.cjs +1 -1
- package/dist/br/data/mun/RR.cjs.map +1 -1
- package/dist/br/data/mun/RR.js +795 -778
- package/dist/br/data/mun/RR.js.map +1 -1
- package/dist/br/data/mun/RS.cjs +1 -1
- package/dist/br/data/mun/RS.cjs.map +1 -1
- package/dist/br/data/mun/RS.js +7579 -6424
- package/dist/br/data/mun/RS.js.map +1 -1
- package/dist/br/data/mun/SC.cjs +1 -1
- package/dist/br/data/mun/SC.cjs.map +1 -1
- package/dist/br/data/mun/SC.js +3979 -3357
- package/dist/br/data/mun/SC.js.map +1 -1
- package/dist/br/data/mun/SE.cjs +1 -1
- package/dist/br/data/mun/SE.cjs.map +1 -1
- package/dist/br/data/mun/SE.js +812 -625
- package/dist/br/data/mun/SE.js.map +1 -1
- package/dist/br/data/mun/SP.cjs +1 -1
- package/dist/br/data/mun/SP.cjs.map +1 -1
- package/dist/br/data/mun/SP.js +7866 -6803
- package/dist/br/data/mun/SP.js.map +1 -1
- package/dist/br/data/mun/TO.cjs +1 -1
- package/dist/br/data/mun/TO.cjs.map +1 -1
- package/dist/br/data/mun/TO.js +2763 -2817
- package/dist/br/data/mun/TO.js.map +1 -1
- package/dist/br/locations.cjs +1 -1
- package/dist/br/locations.cjs.map +1 -1
- package/dist/br/locations.js +97 -24
- package/dist/br/locations.js.map +1 -1
- package/dist/br/scales.cjs +1 -1
- package/dist/br/scales.cjs.map +1 -1
- package/dist/br/scales.js +8 -4
- package/dist/br/scales.js.map +1 -1
- package/dist/br/whatsapp.cjs +2 -0
- package/dist/br/whatsapp.cjs.map +1 -0
- package/dist/br/whatsapp.js +21 -0
- package/dist/br/whatsapp.js.map +1 -0
- package/dist/br.cjs +1 -1
- package/dist/br.cjs.map +1 -1
- package/dist/br.d.ts +188 -10
- package/dist/br.js +18 -17
- package/dist/br.js.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.js +30 -27
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/components/Command/Command.cjs +1 -1
- package/dist/components/Command/Command.cjs.map +1 -1
- package/dist/components/Command/Command.js +78 -75
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/Drawer/Drawer.cjs +1 -1
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.js +42 -39
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +111 -67
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/Modal/Modal.cjs +1 -1
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.js +45 -42
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Money/Money.cjs +1 -1
- package/dist/components/Money/Money.cjs.map +1 -1
- package/dist/components/Money/Money.js +10 -9
- package/dist/components/Money/Money.js.map +1 -1
- package/dist/components/Pagination/Pagination.cjs +1 -1
- package/dist/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.js +38 -31
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/Pagination/Pagination.module.cjs +1 -1
- package/dist/components/Pagination/Pagination.module.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.module.js +4 -4
- package/dist/components/Pagination/Pagination.module.js.map +1 -1
- package/dist/components/Portal/Portal.cjs +1 -1
- package/dist/components/Portal/Portal.cjs.map +1 -1
- package/dist/components/Portal/Portal.js +9 -6
- package/dist/components/Portal/Portal.js.map +1 -1
- package/dist/components/Portal/portal-host.cjs +2 -0
- package/dist/components/Portal/portal-host.cjs.map +1 -0
- package/dist/components/Portal/portal-host.js +11 -0
- package/dist/components/Portal/portal-host.js.map +1 -0
- package/dist/components/RangeSlider/RangeSlider.cjs +1 -1
- package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.js +23 -23
- package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/Scheduler/Scheduler.cjs +1 -1
- package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.js +83 -82
- package/dist/components/Scheduler/Scheduler.js.map +1 -1
- package/dist/components/Slider/Slider.cjs +1 -1
- package/dist/components/Slider/Slider.cjs.map +1 -1
- package/dist/components/Slider/Slider.js +9 -9
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/Toast/ToastProvider.cjs +1 -1
- package/dist/components/Toast/ToastProvider.cjs.map +1 -1
- package/dist/components/Toast/ToastProvider.js +55 -53
- package/dist/components/Toast/ToastProvider.js.map +1 -1
- package/dist/components/VirtualList/VirtualList.cjs +1 -1
- package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
- package/dist/components/VirtualList/VirtualList.js +41 -31
- package/dist/components/VirtualList/VirtualList.js.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.cjs +1 -1
- package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.js +78 -68
- package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
- package/dist/forms/FormField.cjs +1 -1
- package/dist/forms/FormField.cjs.map +1 -1
- package/dist/forms/FormField.js +23 -21
- package/dist/forms/FormField.js.map +1 -1
- package/dist/forms/masked-inputs.cjs +1 -1
- package/dist/forms/masked-inputs.cjs.map +1 -1
- package/dist/forms/masked-inputs.js +20 -19
- package/dist/forms/masked-inputs.js.map +1 -1
- package/dist/hooks/use-fullscreen-element.cjs +2 -0
- package/dist/hooks/use-fullscreen-element.cjs.map +1 -0
- package/dist/hooks/use-fullscreen-element.js +20 -0
- package/dist/hooks/use-fullscreen-element.js.map +1 -0
- package/dist/hooks/use-fullscreen.cjs +2 -0
- package/dist/hooks/use-fullscreen.cjs.map +1 -0
- package/dist/hooks/use-fullscreen.js +66 -0
- package/dist/hooks/use-fullscreen.js.map +1 -0
- package/dist/hooks/use-push-to-talk.cjs +2 -0
- package/dist/hooks/use-push-to-talk.cjs.map +1 -0
- package/dist/hooks/use-push-to-talk.js +61 -0
- package/dist/hooks/use-push-to-talk.js.map +1 -0
- package/dist/http/describe-api-error.cjs.map +1 -1
- package/dist/http/describe-api-error.js.map +1 -1
- package/dist/http/errors.cjs +1 -1
- package/dist/http/errors.cjs.map +1 -1
- package/dist/http/errors.js +34 -16
- package/dist/http/errors.js.map +1 -1
- package/dist/http/parse-response.cjs +2 -2
- package/dist/http/parse-response.cjs.map +1 -1
- package/dist/http/parse-response.js +9 -8
- package/dist/http/parse-response.js.map +1 -1
- package/dist/i18n/create-i18n.cjs +1 -1
- package/dist/i18n/create-i18n.cjs.map +1 -1
- package/dist/i18n/create-i18n.js +28 -27
- package/dist/i18n/create-i18n.js.map +1 -1
- package/dist/query/QueryProvider.cjs +1 -1
- package/dist/query/QueryProvider.cjs.map +1 -1
- package/dist/query/QueryProvider.js +13 -11
- package/dist/query/QueryProvider.js.map +1 -1
- package/dist/query/foreign-client-warning.cjs +2 -0
- package/dist/query/foreign-client-warning.cjs.map +1 -0
- package/dist/query/foreign-client-warning.js +12 -0
- package/dist/query/foreign-client-warning.js.map +1 -0
- package/dist/router/AppRouter.cjs +1 -1
- package/dist/router/AppRouter.cjs.map +1 -1
- package/dist/router/AppRouter.js +28 -21
- package/dist/router/AppRouter.js.map +1 -1
- package/dist/router/RouteGuard.cjs +1 -1
- package/dist/router/RouteGuard.cjs.map +1 -1
- package/dist/router/RouteGuard.js +4 -4
- package/dist/router/RouteGuard.js.map +1 -1
- package/dist/sse/create-event-stream.cjs +1 -1
- package/dist/sse/create-event-stream.cjs.map +1 -1
- package/dist/sse/create-event-stream.js +34 -38
- package/dist/sse/create-event-stream.js.map +1 -1
- package/dist/styles/AIChat.css +79 -0
- package/dist/styles/Accordion.css +12 -0
- package/dist/styles/Alert.css +32 -0
- package/dist/styles/AppBar.css +17 -0
- package/dist/styles/AppShell.css +8 -0
- package/dist/styles/AspectRatio.css +3 -0
- package/dist/styles/AudioPlayer.css +13 -0
- package/dist/styles/AudioRecorder.css +20 -0
- package/dist/styles/Avatar.css +13 -0
- package/dist/styles/AvatarGroup.css +18 -0
- package/dist/styles/Badge.css +25 -0
- package/dist/styles/Banner.css +13 -0
- package/dist/styles/BarList.css +10 -0
- package/dist/styles/BarcodeScanner.css +21 -0
- package/dist/styles/BottomNavigation.css +10 -0
- package/dist/styles/BottomSheet.css +11 -0
- package/dist/styles/BrazilMap.css +20 -0
- package/dist/styles/Breadcrumbs.css +8 -0
- package/dist/styles/Button.css +42 -0
- package/dist/styles/Calendar.css +16 -0
- package/dist/styles/Card.css +15 -0
- package/dist/styles/Carousel.css +17 -0
- package/dist/styles/Center.css +5 -0
- package/dist/styles/Chat.css +35 -0
- package/dist/styles/Checkbox.css +11 -0
- package/dist/styles/ChipInput.css +13 -0
- package/dist/styles/CodeBlock.css +25 -0
- package/dist/styles/Collapsible.css +6 -0
- package/dist/styles/Combobox.css +18 -0
- package/dist/styles/Command.css +20 -0
- package/dist/styles/ContextMenu.css +11 -0
- package/dist/styles/CopyButton.css +4 -0
- package/dist/styles/DataList.css +3 -0
- package/dist/styles/DataTable.css +19 -0
- package/dist/styles/DateRangePicker.css +22 -0
- package/dist/styles/DescriptionList.css +4 -0
- package/dist/styles/Divider.css +11 -0
- package/dist/styles/Drawer.css +22 -0
- package/dist/styles/DropdownMenu.css +16 -0
- package/dist/styles/Dropzone.css +7 -0
- package/dist/styles/EmptyState.css +6 -0
- package/dist/styles/ErrorState.css +6 -0
- package/dist/styles/ErrorText.css +2 -0
- package/dist/styles/FileUpload.css +17 -0
- package/dist/styles/FilterBar.css +19 -0
- package/dist/styles/FloatingActionButton.css +23 -0
- package/dist/styles/Form.css +21 -0
- package/dist/styles/HoverCard.css +11 -0
- package/dist/styles/IconPicker.css +6 -0
- package/dist/styles/Image.css +2 -0
- package/dist/styles/ImageCropper.css +16 -0
- package/dist/styles/Input.css +24 -0
- package/dist/styles/InstallBanner.css +8 -0
- package/dist/styles/Kanban.css +15 -0
- package/dist/styles/Kbd.css +5 -0
- package/dist/styles/Label.css +3 -0
- package/dist/styles/Layout.css +22 -0
- package/dist/styles/Lightbox.css +21 -0
- package/dist/styles/ListTile.css +14 -0
- package/dist/styles/MapLegend.css +9 -0
- package/dist/styles/Markdown.css +24 -0
- package/dist/styles/Masonry.css +4 -0
- package/dist/styles/Menubar.css +17 -0
- package/dist/styles/Modal.css +21 -0
- package/dist/styles/ModalsManager.css +2 -0
- package/dist/styles/MultiSelect.css +21 -0
- package/dist/styles/MunicipalitySearch.css +6 -0
- package/dist/styles/NProgress.css +2 -0
- package/dist/styles/Navbar.css +10 -0
- package/dist/styles/NavigationMenu.css +12 -0
- package/dist/styles/NavigationRail.css +14 -0
- package/dist/styles/NotificationCenter.css +21 -0
- package/dist/styles/OfflineIndicator.css +7 -0
- package/dist/styles/Page.css +13 -0
- package/dist/styles/Pagination.css +12 -0
- package/dist/styles/PasswordInput.css +21 -0
- package/dist/styles/PinInput.css +13 -0
- package/dist/styles/PixQRCode.css +9 -0
- package/dist/styles/Popover.css +10 -0
- package/dist/styles/Progress.css +11 -0
- package/dist/styles/QRCode.css +3 -0
- package/dist/styles/Radio.css +14 -0
- package/dist/styles/RangeSlider.css +15 -0
- package/dist/styles/RatingStars.css +11 -0
- package/dist/styles/RefreshIndicator.css +5 -0
- package/dist/styles/Resizable.css +13 -0
- package/dist/styles/RichTextEditor.css +19 -0
- package/dist/styles/SafeArea.css +7 -0
- package/dist/styles/Scheduler.css +24 -0
- package/dist/styles/ScrollArea.css +7 -0
- package/dist/styles/SearchBar.css +11 -0
- package/dist/styles/SegmentedControl.css +12 -0
- package/dist/styles/Select.css +14 -0
- package/dist/styles/Sidebar.css +19 -0
- package/dist/styles/SignaturePad.css +6 -0
- package/dist/styles/Skeleton.css +7 -0
- package/dist/styles/Slider.css +15 -0
- package/dist/styles/Spacer.css +4 -0
- package/dist/styles/Sparkline.css +4 -0
- package/dist/styles/Spinner.css +12 -0
- package/dist/styles/Stat.css +12 -0
- package/dist/styles/Stepper.css +17 -0
- package/dist/styles/StepperInput.css +15 -0
- package/dist/styles/Switch.css +11 -0
- package/dist/styles/SyncStatusBadge.css +13 -0
- package/dist/styles/Table.css +16 -0
- package/dist/styles/Tabs.css +14 -0
- package/dist/styles/Tag.css +14 -0
- package/dist/styles/Textarea.css +13 -0
- package/dist/styles/TimePicker.css +14 -0
- package/dist/styles/Timeline.css +17 -0
- package/dist/styles/Toast.css +21 -0
- package/dist/styles/Toggle.css +12 -0
- package/dist/styles/ToggleGroup.css +9 -0
- package/dist/styles/Tooltip.css +10 -0
- package/dist/styles/Tour.css +20 -0
- package/dist/styles/TrajectoryMap.css +16 -0
- package/dist/styles/Transfer.css +21 -0
- package/dist/styles/TreeView.css +17 -0
- package/dist/styles/TruncateText.css +2 -0
- package/dist/styles/UpdatePrompt.css +10 -0
- package/dist/styles/VirtualList.css +5 -0
- package/dist/styles/VirtualTable.css +17 -0
- package/dist/styles/VisuallyHidden.css +2 -0
- package/dist/styles/Wizard.css +4 -0
- package/dist/styles/actions.css +104 -0
- package/dist/styles/advanced.css +66 -0
- package/dist/styles/br.css +41 -0
- package/dist/styles/chat.css +113 -0
- package/dist/styles/core.css +40 -0
- package/dist/styles/data.css +222 -0
- package/dist/styles/editor.css +19 -0
- package/dist/styles/feedback.css +201 -0
- package/dist/styles/forms.css +321 -0
- package/dist/styles/geo.css +16 -0
- package/dist/styles/icons.css +6 -0
- package/dist/styles/identity.css +48 -0
- package/dist/styles/layout.css +122 -0
- package/dist/styles/media.css +52 -0
- package/dist/styles/navigation.css +167 -0
- package/dist/styles/overlay.css +73 -0
- package/dist/styles/utility.css +39 -0
- package/dist/styles.css +1 -1
- package/dist/sw/register-service-worker.cjs +1 -1
- package/dist/sw/register-service-worker.cjs.map +1 -1
- package/dist/sw/register-service-worker.js +21 -17
- package/dist/sw/register-service-worker.js.map +1 -1
- package/dist/sw.d.ts +17 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +1382 -76
- package/dist/tempest-react-sdk.js +293 -281
- package/dist/utils/dev-mode.cjs.map +1 -1
- package/dist/utils/dev-mode.js.map +1 -1
- package/dist/utils/duplicate-instance.cjs +2 -0
- package/dist/utils/duplicate-instance.cjs.map +1 -0
- package/dist/utils/duplicate-instance.js +6 -0
- package/dist/utils/duplicate-instance.js.map +1 -0
- package/dist/utils/format.cjs +1 -1
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js +22 -14
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/intl-cache.cjs +2 -0
- package/dist/utils/intl-cache.cjs.map +1 -0
- package/dist/utils/intl-cache.js +23 -0
- package/dist/utils/intl-cache.js.map +1 -0
- package/dist/utils/json-frame.cjs +2 -0
- package/dist/utils/json-frame.cjs.map +1 -0
- package/dist/utils/json-frame.js +30 -0
- package/dist/utils/json-frame.js.map +1 -0
- package/dist/utils/numbers.cjs +1 -1
- package/dist/utils/numbers.cjs.map +1 -1
- package/dist/utils/numbers.js +8 -7
- package/dist/utils/numbers.js.map +1 -1
- package/dist/vision/results.cjs +1 -1
- package/dist/vision/results.cjs.map +1 -1
- package/dist/vision/results.js +26 -17
- package/dist/vision/results.js.map +1 -1
- package/dist/vision.d.ts +29 -0
- package/dist/vite/tempest-icons.cjs +1 -1
- package/dist/vite/tempest-icons.cjs.map +1 -1
- package/dist/vite/tempest-icons.js +1 -1
- package/dist/vite/tempest-icons.js.map +1 -1
- package/dist/webrtc/link-stats.cjs +2 -0
- package/dist/webrtc/link-stats.cjs.map +1 -0
- package/dist/webrtc/link-stats.js +74 -0
- package/dist/webrtc/link-stats.js.map +1 -0
- package/dist/webrtc/opus-sdp.cjs +3 -0
- package/dist/webrtc/opus-sdp.cjs.map +1 -0
- package/dist/webrtc/opus-sdp.js +94 -0
- package/dist/webrtc/opus-sdp.js.map +1 -0
- package/dist/webrtc/sender-bitrate.cjs +2 -0
- package/dist/webrtc/sender-bitrate.cjs.map +1 -0
- package/dist/webrtc/sender-bitrate.js +15 -0
- package/dist/webrtc/sender-bitrate.js.map +1 -0
- package/dist/webrtc/use-link-stats.cjs +2 -0
- package/dist/webrtc/use-link-stats.cjs.map +1 -0
- package/dist/webrtc/use-link-stats.js +22 -0
- package/dist/webrtc/use-link-stats.js.map +1 -0
- package/dist/ws/create-web-socket.cjs +1 -1
- package/dist/ws/create-web-socket.cjs.map +1 -1
- package/dist/ws/create-web-socket.js +130 -58
- package/dist/ws/create-web-socket.js.map +1 -1
- package/dist/ws/resilience.cjs +2 -0
- package/dist/ws/resilience.cjs.map +1 -0
- package/dist/ws/resilience.js +21 -0
- package/dist/ws/resilience.js.map +1 -0
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +47 -39
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +5 -2
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import t from "
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { usePortalHost as t } from "../Portal/portal-host.js";
|
|
3
|
+
import n from "./Toast.module.js";
|
|
4
|
+
import { createContext as r, useCallback as i, useContext as a, useEffect as o, useMemo as s, useRef as c, useState as l } from "react";
|
|
5
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
6
|
+
import { createPortal as f } from "react-dom";
|
|
6
7
|
//#region src/components/Toast/ToastProvider.tsx
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
let e =
|
|
8
|
+
var p = r(null);
|
|
9
|
+
function m() {
|
|
10
|
+
let e = a(p);
|
|
10
11
|
if (!e) throw Error("useToast must be used inside a <ToastProvider>");
|
|
11
12
|
return e;
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
let [
|
|
14
|
+
function h({ children: e, defaultDuration: t = 4e3, position: n = "top-right" }) {
|
|
15
|
+
let [r, a] = l([]), o = c(0), f = i((e) => {
|
|
15
16
|
a((t) => t.filter((t) => t.id !== e));
|
|
16
|
-
}, []), m =
|
|
17
|
-
let n = e.id ?? `toast-${++
|
|
17
|
+
}, []), m = i((e) => {
|
|
18
|
+
let n = e.id ?? `toast-${++o.current}`, r = {
|
|
18
19
|
id: n,
|
|
19
20
|
title: e.title,
|
|
20
21
|
description: e.description,
|
|
@@ -22,9 +23,9 @@ function m({ children: e, defaultDuration: t = 4e3, position: n = "top-right" })
|
|
|
22
23
|
duration: e.duration ?? t
|
|
23
24
|
};
|
|
24
25
|
return a((e) => [...e, r]), n;
|
|
25
|
-
}, [t]), h =
|
|
26
|
+
}, [t]), h = s(() => ({
|
|
26
27
|
show: m,
|
|
27
|
-
dismiss:
|
|
28
|
+
dismiss: f,
|
|
28
29
|
success: (e, t) => m({
|
|
29
30
|
...t,
|
|
30
31
|
description: e,
|
|
@@ -45,70 +46,71 @@ function m({ children: e, defaultDuration: t = 4e3, position: n = "top-right" })
|
|
|
45
46
|
description: e,
|
|
46
47
|
variant: "info"
|
|
47
48
|
})
|
|
48
|
-
}), [m,
|
|
49
|
-
return /* @__PURE__ */
|
|
49
|
+
}), [m, f]);
|
|
50
|
+
return /* @__PURE__ */ d(p.Provider, {
|
|
50
51
|
value: h,
|
|
51
|
-
children: [e, /* @__PURE__ */
|
|
52
|
-
toasts:
|
|
53
|
-
onDismiss:
|
|
52
|
+
children: [e, /* @__PURE__ */ u(_, {
|
|
53
|
+
toasts: r,
|
|
54
|
+
onDismiss: f,
|
|
54
55
|
position: n
|
|
55
56
|
})]
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
|
-
function
|
|
59
|
+
function g(e) {
|
|
59
60
|
switch (e) {
|
|
60
|
-
case "top-left": return
|
|
61
|
-
case "top-center": return
|
|
62
|
-
case "bottom-right": return
|
|
63
|
-
case "bottom-left": return
|
|
64
|
-
case "bottom-center": return
|
|
65
|
-
default: return
|
|
61
|
+
case "top-left": return n.positionTopLeft;
|
|
62
|
+
case "top-center": return n.positionTopCenter;
|
|
63
|
+
case "bottom-right": return n.positionBottomRight;
|
|
64
|
+
case "bottom-left": return n.positionBottomLeft;
|
|
65
|
+
case "bottom-center": return n.positionBottomCenter;
|
|
66
|
+
default: return n.positionTopRight;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
function _({ toasts: r, onDismiss: i, position: a }) {
|
|
70
|
+
let o = t();
|
|
71
|
+
return o ? f(/* @__PURE__ */ u("div", {
|
|
72
|
+
className: e(n.container, g(a)),
|
|
71
73
|
"aria-live": "polite",
|
|
72
74
|
"aria-atomic": "false",
|
|
73
|
-
children:
|
|
75
|
+
children: r.map((e) => /* @__PURE__ */ u(v, {
|
|
74
76
|
toast: e,
|
|
75
|
-
onDismiss:
|
|
77
|
+
onDismiss: i
|
|
76
78
|
}, e.id))
|
|
77
|
-
}),
|
|
79
|
+
}), o) : null;
|
|
78
80
|
}
|
|
79
|
-
function
|
|
80
|
-
return
|
|
81
|
-
if (!
|
|
82
|
-
let e = setTimeout(() => r(
|
|
81
|
+
function v({ toast: t, onDismiss: r }) {
|
|
82
|
+
return o(() => {
|
|
83
|
+
if (!t.duration) return;
|
|
84
|
+
let e = setTimeout(() => r(t.id), t.duration);
|
|
83
85
|
return () => clearTimeout(e);
|
|
84
86
|
}, [
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
t.id,
|
|
88
|
+
t.duration,
|
|
87
89
|
r
|
|
88
|
-
]), /* @__PURE__ */
|
|
89
|
-
className: e(
|
|
90
|
-
children: [/* @__PURE__ */
|
|
91
|
-
className:
|
|
92
|
-
children:
|
|
93
|
-
}),
|
|
94
|
-
className:
|
|
95
|
-
children:
|
|
96
|
-
})] }), /* @__PURE__ */
|
|
90
|
+
]), /* @__PURE__ */ d("div", {
|
|
91
|
+
className: e(n.toast, n[t.variant]),
|
|
92
|
+
children: [/* @__PURE__ */ d("div", { children: [t.title && /* @__PURE__ */ u("p", {
|
|
93
|
+
className: n.title,
|
|
94
|
+
children: t.title
|
|
95
|
+
}), t.description && /* @__PURE__ */ u("p", {
|
|
96
|
+
className: n.description,
|
|
97
|
+
children: t.description
|
|
98
|
+
})] }), /* @__PURE__ */ u("button", {
|
|
97
99
|
type: "button",
|
|
98
|
-
className:
|
|
100
|
+
className: n.close,
|
|
99
101
|
"aria-label": "Fechar notificação",
|
|
100
|
-
onClick: () => r(
|
|
101
|
-
children: /* @__PURE__ */
|
|
102
|
+
onClick: () => r(t.id),
|
|
103
|
+
children: /* @__PURE__ */ u(y, {})
|
|
102
104
|
})]
|
|
103
105
|
});
|
|
104
106
|
}
|
|
105
|
-
function
|
|
106
|
-
return /* @__PURE__ */
|
|
107
|
+
function y() {
|
|
108
|
+
return /* @__PURE__ */ u("svg", {
|
|
107
109
|
width: "14",
|
|
108
110
|
height: "14",
|
|
109
111
|
viewBox: "0 0 24 24",
|
|
110
112
|
fill: "none",
|
|
111
|
-
children: /* @__PURE__ */
|
|
113
|
+
children: /* @__PURE__ */ u("path", {
|
|
112
114
|
d: "M6 6l12 12M6 18L18 6",
|
|
113
115
|
stroke: "currentColor",
|
|
114
116
|
strokeWidth: "2",
|
|
@@ -117,6 +119,6 @@ function v() {
|
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
//#endregion
|
|
120
|
-
export {
|
|
122
|
+
export { h as ToastProvider, m as useToast };
|
|
121
123
|
|
|
122
124
|
//# sourceMappingURL=ToastProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToastProvider.js","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the provider is the queue: enqueue, dedupe, auto-\n * dismiss timers, pause on hover and the portal that renders them. Splitting the\n * queue from the renderer would put a timer in one file and the element it dismisses\n * in another.\n */\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\n/**\n * The portalled stack, and the page's live region for toasts.\n *\n * `aria-atomic=\"false\"` matters: with `\"true\"` a screen reader re-reads the\n * **entire** stack on every change, so the third toast of a batch is announced as\n * all three, and dismissing one re-announces the survivors. `\"false\"` announces only\n * the node that was added, which is what the user needs.\n *\n * The stack stays the live region rather than delegating to `useAnnounce`, because\n * routing it through the shared announcer would put the same text in the document\n * twice — once visible, once hidden — and every `getByText(\"Salvo\")` in a consuming\n * app's test suite would start matching two nodes. The announcer is for messages\n * that have no on-screen home; a toast has one.\n */\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n if (typeof document === \"undefined\") return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n document.body,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\n/**\n * One toast.\n *\n * No `role=\"status\"` of its own: it already lives inside the container's live\n * region, and a live region nested in a live region is announced twice by some\n * screen readers.\n */\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])}>\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ToastProvider.js","names":[],"sources":["../../../src/components/Toast/ToastProvider.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines — the provider is the queue: enqueue, dedupe, auto-\n * dismiss timers, pause on hover and the portal that renders them. Splitting the\n * queue from the renderer would put a timer in one file and the element it dismisses\n * in another.\n */\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Toast.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ToastVariant = \"success\" | \"warning\" | \"error\" | \"info\";\n\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: string;\n variant?: ToastVariant;\n /** Auto-dismiss timeout in ms. Pass `0` to keep the toast until dismissed manually. */\n duration?: number;\n}\n\ninterface ToastEntry extends Required<Omit<ToastOptions, \"id\" | \"title\" | \"description\">> {\n id: string;\n title?: string;\n description?: string;\n}\n\nexport interface ToastApi {\n show: (options: ToastOptions) => string;\n dismiss: (id: string) => void;\n success: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n error: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n warning: (\n description: string,\n options?: Omit<ToastOptions, \"variant\" | \"description\">,\n ) => string;\n info: (description: string, options?: Omit<ToastOptions, \"variant\" | \"description\">) => string;\n}\n\nconst ToastContext = createContext<ToastApi | null>(null);\n\n/**\n * Access the toast API. Must be used inside a {@link ToastProvider}.\n *\n * @returns Methods to show and dismiss toasts.\n */\nexport function useToast(): ToastApi {\n const ctx = useContext(ToastContext);\n if (!ctx) throw new Error(\"useToast must be used inside a <ToastProvider>\");\n return ctx;\n}\n\nexport type ToastPosition =\n \"top-right\" | \"top-left\" | \"top-center\" | \"bottom-right\" | \"bottom-left\" | \"bottom-center\";\n\nexport interface ToastProviderProps {\n children: ReactNode;\n /** Default auto-dismiss duration (ms). Default 4000. */\n defaultDuration?: number;\n /** Stack position on screen. Default `\"top-right\"`. */\n position?: ToastPosition;\n}\n\n/**\n * Renders a portalled toast container and exposes the imperative {@link useToast} API.\n */\nexport function ToastProvider({\n children,\n defaultDuration = 4000,\n position = \"top-right\",\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<ToastEntry[]>([]);\n const counter = useRef<number>(0);\n\n const dismiss = useCallback((id: string): void => {\n setToasts((current) => current.filter((toast) => toast.id !== id));\n }, []);\n\n const show = useCallback(\n (options: ToastOptions): string => {\n const id = options.id ?? `toast-${++counter.current}`;\n const entry: ToastEntry = {\n id,\n title: options.title,\n description: options.description,\n variant: options.variant ?? \"info\",\n duration: options.duration ?? defaultDuration,\n };\n setToasts((current) => [...current, entry]);\n return id;\n },\n [defaultDuration],\n );\n\n const api = useMemo<ToastApi>(\n () => ({\n show,\n dismiss,\n success: (description, options) =>\n show({ ...options, description, variant: \"success\" }),\n error: (description, options) => show({ ...options, description, variant: \"error\" }),\n warning: (description, options) =>\n show({ ...options, description, variant: \"warning\" }),\n info: (description, options) => show({ ...options, description, variant: \"info\" }),\n }),\n [show, dismiss],\n );\n\n return (\n <ToastContext.Provider value={api}>\n {children}\n <ToastContainer toasts={toasts} onDismiss={dismiss} position={position} />\n </ToastContext.Provider>\n );\n}\n\ninterface ContainerProps {\n toasts: ToastEntry[];\n onDismiss: (id: string) => void;\n position: ToastPosition;\n}\n\nfunction positionClass(position: ToastPosition): string {\n switch (position) {\n case \"top-left\":\n return styles.positionTopLeft;\n case \"top-center\":\n return styles.positionTopCenter;\n case \"bottom-right\":\n return styles.positionBottomRight;\n case \"bottom-left\":\n return styles.positionBottomLeft;\n case \"bottom-center\":\n return styles.positionBottomCenter;\n case \"top-right\":\n default:\n return styles.positionTopRight;\n }\n}\n\n/**\n * The portalled stack, and the page's live region for toasts.\n *\n * `aria-atomic=\"false\"` matters: with `\"true\"` a screen reader re-reads the\n * **entire** stack on every change, so the third toast of a batch is announced as\n * all three, and dismissing one re-announces the survivors. `\"false\"` announces only\n * the node that was added, which is what the user needs.\n *\n * The stack stays the live region rather than delegating to `useAnnounce`, because\n * routing it through the shared announcer would put the same text in the document\n * twice — once visible, once hidden — and every `getByText(\"Salvo\")` in a consuming\n * app's test suite would start matching two nodes. The announcer is for messages\n * that have no on-screen home; a toast has one.\n */\nfunction ToastContainer({ toasts, onDismiss, position }: ContainerProps) {\n const portalHost = usePortalHost();\n\n if (!portalHost) return null;\n return createPortal(\n <div\n className={cn(styles.container, positionClass(position))}\n aria-live=\"polite\"\n aria-atomic=\"false\"\n >\n {toasts.map((toast) => (\n <ToastItem key={toast.id} toast={toast} onDismiss={onDismiss} />\n ))}\n </div>,\n portalHost,\n );\n}\n\ninterface ItemProps {\n toast: ToastEntry;\n onDismiss: (id: string) => void;\n}\n\n/**\n * One toast.\n *\n * No `role=\"status\"` of its own: it already lives inside the container's live\n * region, and a live region nested in a live region is announced twice by some\n * screen readers.\n */\nfunction ToastItem({ toast, onDismiss }: ItemProps) {\n useEffect(() => {\n if (!toast.duration) return;\n const timer = setTimeout(() => onDismiss(toast.id), toast.duration);\n return () => clearTimeout(timer);\n }, [toast.id, toast.duration, onDismiss]);\n\n return (\n <div className={cn(styles.toast, styles[toast.variant])}>\n <div>\n {toast.title && <p className={styles.title}>{toast.title}</p>}\n {toast.description && <p className={styles.description}>{toast.description}</p>}\n </div>\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar notificação\"\n onClick={() => onDismiss(toast.id)}\n >\n <CloseIcon />\n </button>\n </div>\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./VirtualList.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("./VirtualList.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i({item:e,index:n,top:i,height:a,render:o}){return(0,r.jsx)(`div`,{role:`listitem`,className:t.default.row,style:{top:i,height:a},children:o(e,n)})}var a=(0,n.memo)(i);function o({items:i,itemHeight:o,renderItem:s,height:c,overscan:l=4,getKey:u,className:d,style:f,label:p}){let m=(0,n.useRef)(null),[h,g]=(0,n.useState)(0),[_,v]=(0,n.useState)(0);(0,n.useEffect)(()=>{let e=m.current;if(!e||(v(e.clientHeight),typeof ResizeObserver>`u`))return;let t=new ResizeObserver(()=>v(e.clientHeight));return t.observe(e),()=>t.disconnect()},[]);let y=i.length*o,b=_>0&&y-_>1,x=Math.max(0,Math.floor(h/o)-l),S=Math.ceil(_/o)+l*2,C=Math.min(i.length,x+S);return(0,r.jsx)(`div`,{ref:m,className:e.cn(t.default.scroll,d),style:{height:c,...f},onScroll:e=>g(e.target.scrollTop),role:`list`,"aria-label":p,tabIndex:b?0:void 0,children:(0,r.jsx)(`div`,{className:t.default.spacer,style:{height:y},children:i.slice(x,C).map((e,t)=>{let n=x+t;return(0,r.jsx)(a,{item:e,index:n,top:n*o,height:o,render:s},u?u(e,n):n)})})})}exports.VirtualList=o;
|
|
2
2
|
//# sourceMappingURL=VirtualList.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualList.cjs","names":[],"sources":["../../../src/components/VirtualList/VirtualList.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — items, itemHeight, height and overscan are the\n * window maths; renderItem and getKey the row contract; label the accessible name of\n * a scroll region. Nine props is what windowing costs — the alternative is the app\n * doing the maths.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VirtualList.module.css\";\n\nexport interface VirtualListProps<T> {\n items: T[];\n /** Fixed pixel height for each row. */\n itemHeight: number;\n /** Render a single row. */\n renderItem: (item: T, index: number) => ReactNode;\n /** Container height (px) or any CSS length. */\n height: number | string;\n /** Number of items rendered above/below the viewport. Default: 4. */\n overscan?: number;\n /** Stable key derivation; defaults to the index. */\n getKey?: (item: T, index: number) => string | number;\n className?: string;\n style?: CSSProperties;\n /**\n * Accessible name for the list. Worth setting when the list scrolls: it\n * then carries a tab stop of its own, and a focus stop that announces\n * nothing is worse than no focus stop.\n */\n label?: string;\n}\n\n/**\n * Fixed-height virtual list. Renders only the visible window plus a small\n * overscan buffer. Suitable for lists of thousands of identical rows.\n *\n * For variable heights, use `react-window`/`@tanstack/react-virtual` — those\n * solve a more general problem at the cost of extra setup.\n */\nexport function VirtualList<T>({\n items,\n itemHeight,\n renderItem,\n height,\n overscan = 4,\n getKey,\n className,\n style,\n label,\n}: VirtualListProps<T>) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState<number>(0);\n const [viewport, setViewport] = useState<number>(0);\n\n useEffect(() => {\n const element = containerRef.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 const totalHeight = items.length * itemHeight;\n /*\n * Rows are absolutely positioned inside the spacer, so nothing in a\n * virtual list is focusable by default: a keyboard user can see the\n * scrollbar and has no way to move it. The tab stop only appears while\n * there is something past the fold — measured from the numbers already at\n * hand rather than from the DOM.\n */\n const scrollable = viewport > 0 && totalHeight - viewport > 1;\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const visibleCount = Math.ceil(viewport / itemHeight) + overscan * 2;\n const end = Math.min(items.length, start + visibleCount);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.scroll, className)}\n style={{ height, ...style }}\n onScroll={(event) => setScrollTop((event.target as HTMLDivElement).scrollTop)}\n role=\"list\"\n aria-label={label}\n tabIndex={scrollable ? 0 : undefined}\n >\n <div className={styles.spacer} style={{ height: totalHeight }}>\n {items.slice(start, end).map((item, offset) => {\n const index = start + offset;\n
|
|
1
|
+
{"version":3,"file":"VirtualList.cjs","names":[],"sources":["../../../src/components/VirtualList/VirtualList.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — items, itemHeight, height and overscan are the\n * window maths; renderItem and getKey the row contract; label the accessible name of\n * a scroll region. Nine props is what windowing costs — the alternative is the app\n * doing the maths.\n */\nimport { memo, useEffect, useRef, useState } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VirtualList.module.css\";\n\nexport interface VirtualListProps<T> {\n items: T[];\n /** Fixed pixel height for each row. */\n itemHeight: number;\n /** Render a single row. */\n renderItem: (item: T, index: number) => ReactNode;\n /** Container height (px) or any CSS length. */\n height: number | string;\n /** Number of items rendered above/below the viewport. Default: 4. */\n overscan?: number;\n /** Stable key derivation; defaults to the index. */\n getKey?: (item: T, index: number) => string | number;\n className?: string;\n style?: CSSProperties;\n /**\n * Accessible name for the list. Worth setting when the list scrolls: it\n * then carries a tab stop of its own, and a focus stop that announces\n * nothing is worse than no focus stop.\n */\n label?: string;\n}\n\ninterface VirtualRowProps<T> {\n item: T;\n index: number;\n top: number;\n height: number;\n render: (item: T, index: number) => ReactNode;\n}\n\n/**\n * One windowed row, rendered on its own so React can skip it.\n *\n * Without this boundary every scroll event re-rendered the whole visible window\n * rather than the one row that entered it: measured at 126 `renderItem` calls\n * across twenty single-row scroll steps where twenty are needed, and the factor\n * grows with the number of visible rows.\n *\n * `render` is part of the comparison on purpose. Reading it from a ref instead\n * would let a row keep painting output from a stale closure — the row that shows\n * the selected item would never notice the selection changed. Keeping it in the\n * props means an app that wraps `renderItem` in `useCallback` gets the skip, and\n * an app that passes an inline arrow gets exactly today's behaviour.\n *\n * @param props - The row's item, its position and the caller's renderer.\n * @returns The positioned row element.\n */\nfunction VirtualRowImpl<T>({ item, index, top, height, render }: VirtualRowProps<T>) {\n return (\n <div role=\"listitem\" className={styles.row} style={{ top, height }}>\n {render(item, index)}\n </div>\n );\n}\n\n/**\n * `memo` erases the generic, so the memoised component is re-typed as the\n * function it wraps. The runtime value is unchanged; only the signature is\n * restored.\n */\nconst VirtualRow = memo(VirtualRowImpl) as typeof VirtualRowImpl;\n\n/**\n * Fixed-height virtual list. Renders only the visible window plus a small\n * overscan buffer. Suitable for lists of thousands of identical rows.\n *\n * For variable heights, use `react-window`/`@tanstack/react-virtual` — those\n * solve a more general problem at the cost of extra setup.\n */\nexport function VirtualList<T>({\n items,\n itemHeight,\n renderItem,\n height,\n overscan = 4,\n getKey,\n className,\n style,\n label,\n}: VirtualListProps<T>) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState<number>(0);\n const [viewport, setViewport] = useState<number>(0);\n\n useEffect(() => {\n const element = containerRef.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 const totalHeight = items.length * itemHeight;\n /*\n * Rows are absolutely positioned inside the spacer, so nothing in a\n * virtual list is focusable by default: a keyboard user can see the\n * scrollbar and has no way to move it. The tab stop only appears while\n * there is something past the fold — measured from the numbers already at\n * hand rather than from the DOM.\n */\n const scrollable = viewport > 0 && totalHeight - viewport > 1;\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const visibleCount = Math.ceil(viewport / itemHeight) + overscan * 2;\n const end = Math.min(items.length, start + visibleCount);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.scroll, className)}\n style={{ height, ...style }}\n onScroll={(event) => setScrollTop((event.target as HTMLDivElement).scrollTop)}\n role=\"list\"\n aria-label={label}\n tabIndex={scrollable ? 0 : undefined}\n >\n <div className={styles.spacer} style={{ height: totalHeight }}>\n {items.slice(start, end).map((item, offset) => {\n const index = start + offset;\n return (\n <VirtualRow\n key={getKey ? getKey(item, index) : index}\n item={item}\n index={index}\n top={index * itemHeight}\n height={itemHeight}\n render={renderItem}\n />\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":"kIA0DA,SAAS,EAAkB,CAAE,OAAM,QAAO,MAAK,SAAQ,UAA8B,CACjF,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,KAAK,WAAW,UAAW,EAAA,QAAO,IAAK,MAAO,CAAE,MAAK,QAAO,EAC5D,SAAA,EAAO,EAAM,CAAK,CAClB,CAAA,CAEb,CAOA,IAAM,GAAA,EAAa,EAAA,KAAA,CAAK,CAAc,EAStC,SAAgB,EAAe,CAC3B,QACA,aACA,aACA,SACA,WAAW,EACX,SACA,YACA,QACA,SACoB,CACpB,IAAM,GAAA,EAAe,EAAA,OAAA,CAAuB,IAAI,EAC1C,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,CAAiB,CAAC,EAC9C,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAiB,CAAC,GAElD,EAAA,EAAA,UAAA,KAAgB,CACZ,IAAM,EAAU,EAAa,QAG7B,GAFI,CAAC,IACL,EAAY,EAAQ,YAAY,EAC5B,OAAO,eAAmB,KAAa,OAC3C,IAAM,EAAW,IAAI,mBAAqB,EAAY,EAAQ,YAAY,CAAC,EAE3E,OADA,EAAS,QAAQ,CAAO,MACX,EAAS,WAAW,CACrC,EAAG,CAAC,CAAC,EAEL,IAAM,EAAc,EAAM,OAAS,EAQ7B,EAAa,EAAW,GAAK,EAAc,EAAW,EACtD,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAM,EAAY,CAAU,EAAI,CAAQ,EACjE,EAAe,KAAK,KAAK,EAAW,CAAU,EAAI,EAAW,EAC7D,EAAM,KAAK,IAAI,EAAM,OAAQ,EAAQ,CAAY,EAEvD,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,IAAK,EACL,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EACtC,MAAO,CAAE,SAAQ,GAAG,CAAM,EAC1B,SAAW,GAAU,EAAc,EAAM,OAA0B,SAAS,EAC5E,KAAK,OACL,aAAY,EACZ,SAAU,EAAa,EAAI,IAAA,GAE3B,UAAA,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,OAAQ,MAAO,CAAE,OAAQ,CAAY,EACvD,SAAA,EAAM,MAAM,EAAO,CAAG,CAAC,CAAC,KAAK,EAAM,IAAW,CAC3C,IAAM,EAAQ,EAAQ,EACtB,OACI,EAAA,EAAA,IAAA,CAAC,EAAD,CAEU,OACC,QACP,IAAK,EAAQ,EACb,OAAQ,EACR,OAAQ,CACX,EANQ,EAAS,EAAO,EAAM,CAAK,EAAI,CAMvC,CAET,CAAC,CACA,CAAA,CACJ,CAAA,CAEb"}
|
|
@@ -1,47 +1,57 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
import t from "./VirtualList.module.js";
|
|
3
|
-
import {
|
|
4
|
-
import { jsx as
|
|
3
|
+
import { memo as n, useEffect as r, useRef as i, useState as a } from "react";
|
|
4
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/VirtualList/VirtualList.tsx
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
function s({ item: e, index: n, top: r, height: i, render: a }) {
|
|
7
|
+
return /* @__PURE__ */ o("div", {
|
|
8
|
+
role: "listitem",
|
|
9
|
+
className: t.row,
|
|
10
|
+
style: {
|
|
11
|
+
top: r,
|
|
12
|
+
height: i
|
|
13
|
+
},
|
|
14
|
+
children: a(e, n)
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
var c = n(s);
|
|
18
|
+
function l({ items: n, itemHeight: s, renderItem: l, height: u, overscan: d = 4, getKey: f, className: p, style: m, label: h }) {
|
|
19
|
+
let g = i(null), [_, v] = a(0), [y, b] = a(0);
|
|
20
|
+
r(() => {
|
|
21
|
+
let e = g.current;
|
|
22
|
+
if (!e || (b(e.clientHeight), typeof ResizeObserver > "u")) return;
|
|
23
|
+
let t = new ResizeObserver(() => b(e.clientHeight));
|
|
12
24
|
return t.observe(e), () => t.disconnect();
|
|
13
25
|
}, []);
|
|
14
|
-
let
|
|
15
|
-
return /* @__PURE__ */
|
|
16
|
-
ref:
|
|
17
|
-
className: e(t.scroll,
|
|
26
|
+
let x = n.length * s, S = y > 0 && x - y > 1, C = Math.max(0, Math.floor(_ / s) - d), w = Math.ceil(y / s) + d * 2, T = Math.min(n.length, C + w);
|
|
27
|
+
return /* @__PURE__ */ o("div", {
|
|
28
|
+
ref: g,
|
|
29
|
+
className: e(t.scroll, p),
|
|
18
30
|
style: {
|
|
19
|
-
height:
|
|
20
|
-
...
|
|
31
|
+
height: u,
|
|
32
|
+
...m
|
|
21
33
|
},
|
|
22
|
-
onScroll: (e) =>
|
|
34
|
+
onScroll: (e) => v(e.target.scrollTop),
|
|
23
35
|
role: "list",
|
|
24
|
-
"aria-label":
|
|
25
|
-
tabIndex:
|
|
26
|
-
children: /* @__PURE__ */
|
|
36
|
+
"aria-label": h,
|
|
37
|
+
tabIndex: S ? 0 : void 0,
|
|
38
|
+
children: /* @__PURE__ */ o("div", {
|
|
27
39
|
className: t.spacer,
|
|
28
|
-
style: { height:
|
|
29
|
-
children:
|
|
30
|
-
let
|
|
31
|
-
return /* @__PURE__ */
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
children: c(e, r)
|
|
39
|
-
}, i);
|
|
40
|
+
style: { height: x },
|
|
41
|
+
children: n.slice(C, T).map((e, t) => {
|
|
42
|
+
let n = C + t;
|
|
43
|
+
return /* @__PURE__ */ o(c, {
|
|
44
|
+
item: e,
|
|
45
|
+
index: n,
|
|
46
|
+
top: n * s,
|
|
47
|
+
height: s,
|
|
48
|
+
render: l
|
|
49
|
+
}, f ? f(e, n) : n);
|
|
40
50
|
})
|
|
41
51
|
})
|
|
42
52
|
});
|
|
43
53
|
}
|
|
44
54
|
//#endregion
|
|
45
|
-
export {
|
|
55
|
+
export { l as VirtualList };
|
|
46
56
|
|
|
47
57
|
//# sourceMappingURL=VirtualList.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualList.js","names":[],"sources":["../../../src/components/VirtualList/VirtualList.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — items, itemHeight, height and overscan are the\n * window maths; renderItem and getKey the row contract; label the accessible name of\n * a scroll region. Nine props is what windowing costs — the alternative is the app\n * doing the maths.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VirtualList.module.css\";\n\nexport interface VirtualListProps<T> {\n items: T[];\n /** Fixed pixel height for each row. */\n itemHeight: number;\n /** Render a single row. */\n renderItem: (item: T, index: number) => ReactNode;\n /** Container height (px) or any CSS length. */\n height: number | string;\n /** Number of items rendered above/below the viewport. Default: 4. */\n overscan?: number;\n /** Stable key derivation; defaults to the index. */\n getKey?: (item: T, index: number) => string | number;\n className?: string;\n style?: CSSProperties;\n /**\n * Accessible name for the list. Worth setting when the list scrolls: it\n * then carries a tab stop of its own, and a focus stop that announces\n * nothing is worse than no focus stop.\n */\n label?: string;\n}\n\n/**\n * Fixed-height virtual list. Renders only the visible window plus a small\n * overscan buffer. Suitable for lists of thousands of identical rows.\n *\n * For variable heights, use `react-window`/`@tanstack/react-virtual` — those\n * solve a more general problem at the cost of extra setup.\n */\nexport function VirtualList<T>({\n items,\n itemHeight,\n renderItem,\n height,\n overscan = 4,\n getKey,\n className,\n style,\n label,\n}: VirtualListProps<T>) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState<number>(0);\n const [viewport, setViewport] = useState<number>(0);\n\n useEffect(() => {\n const element = containerRef.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 const totalHeight = items.length * itemHeight;\n /*\n * Rows are absolutely positioned inside the spacer, so nothing in a\n * virtual list is focusable by default: a keyboard user can see the\n * scrollbar and has no way to move it. The tab stop only appears while\n * there is something past the fold — measured from the numbers already at\n * hand rather than from the DOM.\n */\n const scrollable = viewport > 0 && totalHeight - viewport > 1;\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const visibleCount = Math.ceil(viewport / itemHeight) + overscan * 2;\n const end = Math.min(items.length, start + visibleCount);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.scroll, className)}\n style={{ height, ...style }}\n onScroll={(event) => setScrollTop((event.target as HTMLDivElement).scrollTop)}\n role=\"list\"\n aria-label={label}\n tabIndex={scrollable ? 0 : undefined}\n >\n <div className={styles.spacer} style={{ height: totalHeight }}>\n {items.slice(start, end).map((item, offset) => {\n const index = start + offset;\n
|
|
1
|
+
{"version":3,"file":"VirtualList.js","names":[],"sources":["../../../src/components/VirtualList/VirtualList.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — items, itemHeight, height and overscan are the\n * window maths; renderItem and getKey the row contract; label the accessible name of\n * a scroll region. Nine props is what windowing costs — the alternative is the app\n * doing the maths.\n */\nimport { memo, useEffect, useRef, useState } from \"react\";\nimport type { CSSProperties, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VirtualList.module.css\";\n\nexport interface VirtualListProps<T> {\n items: T[];\n /** Fixed pixel height for each row. */\n itemHeight: number;\n /** Render a single row. */\n renderItem: (item: T, index: number) => ReactNode;\n /** Container height (px) or any CSS length. */\n height: number | string;\n /** Number of items rendered above/below the viewport. Default: 4. */\n overscan?: number;\n /** Stable key derivation; defaults to the index. */\n getKey?: (item: T, index: number) => string | number;\n className?: string;\n style?: CSSProperties;\n /**\n * Accessible name for the list. Worth setting when the list scrolls: it\n * then carries a tab stop of its own, and a focus stop that announces\n * nothing is worse than no focus stop.\n */\n label?: string;\n}\n\ninterface VirtualRowProps<T> {\n item: T;\n index: number;\n top: number;\n height: number;\n render: (item: T, index: number) => ReactNode;\n}\n\n/**\n * One windowed row, rendered on its own so React can skip it.\n *\n * Without this boundary every scroll event re-rendered the whole visible window\n * rather than the one row that entered it: measured at 126 `renderItem` calls\n * across twenty single-row scroll steps where twenty are needed, and the factor\n * grows with the number of visible rows.\n *\n * `render` is part of the comparison on purpose. Reading it from a ref instead\n * would let a row keep painting output from a stale closure — the row that shows\n * the selected item would never notice the selection changed. Keeping it in the\n * props means an app that wraps `renderItem` in `useCallback` gets the skip, and\n * an app that passes an inline arrow gets exactly today's behaviour.\n *\n * @param props - The row's item, its position and the caller's renderer.\n * @returns The positioned row element.\n */\nfunction VirtualRowImpl<T>({ item, index, top, height, render }: VirtualRowProps<T>) {\n return (\n <div role=\"listitem\" className={styles.row} style={{ top, height }}>\n {render(item, index)}\n </div>\n );\n}\n\n/**\n * `memo` erases the generic, so the memoised component is re-typed as the\n * function it wraps. The runtime value is unchanged; only the signature is\n * restored.\n */\nconst VirtualRow = memo(VirtualRowImpl) as typeof VirtualRowImpl;\n\n/**\n * Fixed-height virtual list. Renders only the visible window plus a small\n * overscan buffer. Suitable for lists of thousands of identical rows.\n *\n * For variable heights, use `react-window`/`@tanstack/react-virtual` — those\n * solve a more general problem at the cost of extra setup.\n */\nexport function VirtualList<T>({\n items,\n itemHeight,\n renderItem,\n height,\n overscan = 4,\n getKey,\n className,\n style,\n label,\n}: VirtualListProps<T>) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scrollTop, setScrollTop] = useState<number>(0);\n const [viewport, setViewport] = useState<number>(0);\n\n useEffect(() => {\n const element = containerRef.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 const totalHeight = items.length * itemHeight;\n /*\n * Rows are absolutely positioned inside the spacer, so nothing in a\n * virtual list is focusable by default: a keyboard user can see the\n * scrollbar and has no way to move it. The tab stop only appears while\n * there is something past the fold — measured from the numbers already at\n * hand rather than from the DOM.\n */\n const scrollable = viewport > 0 && totalHeight - viewport > 1;\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const visibleCount = Math.ceil(viewport / itemHeight) + overscan * 2;\n const end = Math.min(items.length, start + visibleCount);\n\n return (\n <div\n ref={containerRef}\n className={cn(styles.scroll, className)}\n style={{ height, ...style }}\n onScroll={(event) => setScrollTop((event.target as HTMLDivElement).scrollTop)}\n role=\"list\"\n aria-label={label}\n tabIndex={scrollable ? 0 : undefined}\n >\n <div className={styles.spacer} style={{ height: totalHeight }}>\n {items.slice(start, end).map((item, offset) => {\n const index = start + offset;\n return (\n <VirtualRow\n key={getKey ? getKey(item, index) : index}\n item={item}\n index={index}\n top={index * itemHeight}\n height={itemHeight}\n render={renderItem}\n />\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AA0DA,SAAS,EAAkB,EAAE,SAAM,UAAO,QAAK,WAAQ,aAA8B;CACjF,OACI,kBAAC,OAAD;EAAK,MAAK;EAAW,WAAW,EAAO;EAAK,OAAO;GAAE;GAAK;EAAO;EAC5D,UAAA,EAAO,GAAM,CAAK;CAClB,CAAA;AAEb;AAOA,IAAM,IAAa,EAAK,CAAc;AAStC,SAAgB,EAAe,EAC3B,UACA,eACA,eACA,WACA,cAAW,GACX,WACA,cACA,UACA,YACoB;CACpB,IAAM,IAAe,EAAuB,IAAI,GAC1C,CAAC,GAAW,KAAgB,EAAiB,CAAC,GAC9C,CAAC,GAAU,KAAe,EAAiB,CAAC;CAElD,QAAgB;EACZ,IAAM,IAAU,EAAa;EAG7B,IAFI,CAAC,MACL,EAAY,EAAQ,YAAY,GAC5B,OAAO,iBAAmB,MAAa;EAC3C,IAAM,IAAW,IAAI,qBAAqB,EAAY,EAAQ,YAAY,CAAC;EAE3E,OADA,EAAS,QAAQ,CAAO,SACX,EAAS,WAAW;CACrC,GAAG,CAAC,CAAC;CAEL,IAAM,IAAc,EAAM,SAAS,GAQ7B,IAAa,IAAW,KAAK,IAAc,IAAW,GACtD,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,IAAY,CAAU,IAAI,CAAQ,GACjE,IAAe,KAAK,KAAK,IAAW,CAAU,IAAI,IAAW,GAC7D,IAAM,KAAK,IAAI,EAAM,QAAQ,IAAQ,CAAY;CAEvD,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,QAAQ,CAAS;EACtC,OAAO;GAAE;GAAQ,GAAG;EAAM;EAC1B,WAAW,MAAU,EAAc,EAAM,OAA0B,SAAS;EAC5E,MAAK;EACL,cAAY;EACZ,UAAU,IAAa,IAAI,KAAA;EAE3B,UAAA,kBAAC,OAAD;GAAK,WAAW,EAAO;GAAQ,OAAO,EAAE,QAAQ,EAAY;GACvD,UAAA,EAAM,MAAM,GAAO,CAAG,CAAC,CAAC,KAAK,GAAM,MAAW;IAC3C,IAAM,IAAQ,IAAQ;IACtB,OACI,kBAAC,GAAD;KAEU;KACC;KACP,KAAK,IAAQ;KACb,QAAQ;KACR,QAAQ;IACX,GANQ,IAAS,EAAO,GAAM,CAAK,IAAI,CAMvC;GAET,CAAC;EACA,CAAA;CACJ,CAAA;AAEb"}
|
|
@@ -1,2 +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({
|
|
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({row:t,index:r,rowHeight:o,columns:s,onRowClick:c}){return(0,i.jsx)(`tr`,{"aria-rowindex":r+1,className:e.cn(n.default.tr,c&&n.default.clickable),style:{height:o},tabIndex:c?0:void 0,onClick:c?()=>c(t,r):void 0,onKeyDown:c?e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),c(t,r))}:void 0,children:s.map(o=>(0,i.jsx)(`td`,{className:e.cn(n.default.td,a(o.align)),style:{width:o.width},children:o.render?o.render(t,r):t[o.key]??null},String(o.key)))})}var s=(0,r.memo)(o);function c({data:o,columns:c,rowHeight:l,height:u,overscan:d=4,rowKey:f,initialSort:p,onRowClick:m,emptyMessage:h=`Nenhum registro encontrado.`,caption:g,scrollToIndex:_,className:v,...y}){let b=(0,r.useRef)(null),[x,S]=(0,r.useState)(0),[C,w]=(0,r.useState)(0),[T,E]=(0,r.useState)(p??null);(0,r.useEffect)(()=>{let e=b.current;if(!e||(w(e.clientHeight),typeof ResizeObserver>`u`))return;let t=new ResizeObserver(()=>w(e.clientHeight));return t.observe(e),()=>t.disconnect()},[]),(0,r.useEffect)(()=>{let e=b.current;!e||_==null||(e.scrollTop=Math.max(0,Math.min(_,o.length-1))*l)},[_,l,o.length]);let D=(0,r.useMemo)(()=>{if(!T)return o;let e=T.direction===`asc`?1:-1;return[...o].sort((n,r)=>t.compareValues(n[T.key],r[T.key])*e)},[o,T]),O=Math.max(0,Math.floor(x/l)-d),k=Math.ceil(C/l)+d*2,A=Math.min(D.length,O+k),j=D.slice(O,A),M=(0,r.useCallback)(e=>{E(t=>!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null);let t=b.current;t&&(t.scrollTop=0),S(0)},[]);return(0,i.jsx)(`div`,{ref:b,className:e.cn(n.default.scroll,v),style:{height:u},onScroll:e=>S(e.currentTarget.scrollTop),...y,children:(0,i.jsxs)(`table`,{className:n.default.table,"aria-rowcount":D.length,children:[g?(0,i.jsx)(`caption`,{className:n.default.caption,children:g}):null,(0,i.jsx)(`thead`,{className:n.default.head,children:(0,i.jsx)(`tr`,{children:c.map(t=>{let r=T?.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?T?.direction===`asc`?`ascending`:`descending`:t.sortable?`none`:void 0,children:t.sortable?(0,i.jsxs)(`button`,{type:`button`,className:n.default.sortButton,onClick:()=>M(t.key),children:[t.header,(0,i.jsx)(`span`,{className:n.default.sortIndicator,"aria-hidden":!0,children:r?T?.direction===`asc`?`▲`:`▼`:`↕`})]}):t.header},String(t.key))})})}),(0,i.jsx)(`tbody`,{children:D.length===0?(0,i.jsx)(`tr`,{children:(0,i.jsx)(`td`,{className:n.default.emptyRow,colSpan:c.length,children:h})}):(0,i.jsxs)(i.Fragment,{children:[O>0&&(0,i.jsx)(`tr`,{"aria-hidden":!0,style:{height:O*l}}),j.map((e,t)=>{let n=O+t;return(0,i.jsx)(s,{row:e,index:n,rowHeight:l,columns:c,onRowClick:m},f?f(e,n):n)}),A<D.length&&(0,i.jsx)(`tr`,{"aria-hidden":!0,style:{height:(D.length-A)*l}})]})})]})})}exports.VirtualTable=c;
|
|
2
2
|
//# sourceMappingURL=VirtualTable.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualTable.cjs","names":[],"sources":["../../../src/components/VirtualTable/VirtualTable.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count — windowing needs its own measurements\n * (rowHeight, height, overscan, scrollToIndex) on top of everything a table already\n * takes (data, columns, rowKey, initialSort, onRowClick, emptyMessage, caption). The\n * caption is not optional chrome — a virtualised grid without one is unreadable to a\n * screen reader.\n */\nimport {\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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"}
|
|
1
|
+
{"version":3,"file":"VirtualTable.cjs","names":[],"sources":["../../../src/components/VirtualTable/VirtualTable.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count — windowing needs its own measurements\n * (rowHeight, height, overscan, scrollToIndex) on top of everything a table already\n * takes (data, columns, rowKey, initialSort, onRowClick, emptyMessage, caption). The\n * caption is not optional chrome — a virtualised grid without one is unreadable to a\n * screen reader.\n */\nimport {\n type HTMLAttributes,\n type ReactNode,\n memo,\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\ninterface VirtualTableRowProps<T> {\n row: T;\n index: number;\n rowHeight: number;\n columns: VirtualTableColumn<T>[];\n onRowClick: ((row: T, index: number) => void) | undefined;\n}\n\n/**\n * One windowed table row, rendered on its own so React can skip it.\n *\n * Scrolling changes state that lives inside `VirtualTable`, so without this\n * boundary every scroll event re-rendered every visible row and every cell in\n * them — including whatever `column.render` builds — rather than the one row\n * that entered the window.\n *\n * `columns` and `onRowClick` take part in the comparison because a cell renderer\n * closes over the app's state: leaving them out would freeze a row showing a\n * stale cell. An app that keeps its `columns` array stable (a module constant, or\n * `useMemo`) therefore gets the skip, and one that rebuilds it every render gets\n * exactly today's behaviour.\n *\n * @param props - The row, its position and the column contract.\n * @returns The table row element.\n */\nfunction VirtualTableRowImpl<T>({\n row,\n index,\n rowHeight,\n columns,\n onRowClick,\n}: VirtualTableRowProps<T>) {\n return (\n <tr\n aria-rowindex={index + 1}\n className={cn(styles.tr, onRowClick && styles.clickable)}\n style={{ height: rowHeight }}\n tabIndex={onRowClick ? 0 : undefined}\n onClick={onRowClick ? () => onRowClick(row, index) : undefined}\n onKeyDown={\n onRowClick\n ? (event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\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\n/**\n * `memo` erases the generic, so the memoised component is re-typed as the\n * function it wraps. The runtime value is unchanged; only the signature is\n * restored.\n */\nconst VirtualTableRow = memo(VirtualTableRowImpl) as typeof VirtualTableRowImpl;\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 <VirtualTableRow\n key={rowKey ? rowKey(row, index) : index}\n row={row}\n index={index}\n rowHeight={rowHeight}\n columns={columns}\n onRowClick={onRowClick}\n />\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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"}
|