tempest-react-sdk 0.53.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 +27 -6
- 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/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/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/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/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/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/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 +904 -41
- package/dist/tempest-react-sdk.js +293 -286
- package/dist/utils/dev-mode.cjs.map +1 -1
- package/dist/utils/dev-mode.js.map +1 -1
- 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/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 +81 -85
- package/dist/ws/create-web-socket.js.map +1 -1
- 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 +42 -48
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +5 -2
|
@@ -1,103 +1,104 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import s from "./
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
2
|
+
import { dateTimeFormat as t } from "../../utils/intl-cache.js";
|
|
3
|
+
import { dayRange as n, fractionOfWindow as r, hourMarks as i, isSameDay as a, layoutAllDay as o, layoutEvents as s } from "./scheduler-layout.js";
|
|
4
|
+
import c from "./Scheduler.module.js";
|
|
5
|
+
import { useEffect as l, useMemo as u, useState as d } from "react";
|
|
6
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/Scheduler/Scheduler.tsx
|
|
7
|
-
var
|
|
8
|
-
function
|
|
9
|
-
let [
|
|
10
|
-
|
|
11
|
-
if (
|
|
12
|
-
let e = setInterval(() =>
|
|
8
|
+
var h = 6e4;
|
|
9
|
+
function g({ events: g, anchor: _, days: v = 7, startHour: y = 8, endHour: b = 20, snapMinutes: x = 30, onEventClick: S, onSlotClick: C, renderEvent: w, locale: T = "pt-BR", showCurrentTime: E = !0, now: D, className: O, ...k }) {
|
|
10
|
+
let [A, j] = d(() => D ?? /* @__PURE__ */ new Date());
|
|
11
|
+
l(() => {
|
|
12
|
+
if (D || !E) return;
|
|
13
|
+
let e = setInterval(() => j(/* @__PURE__ */ new Date()), h);
|
|
13
14
|
return () => clearInterval(e);
|
|
14
|
-
}, [
|
|
15
|
-
let
|
|
16
|
-
startMinute:
|
|
17
|
-
endMinute:
|
|
18
|
-
}), [
|
|
19
|
-
events:
|
|
20
|
-
days:
|
|
21
|
-
window:
|
|
15
|
+
}, [D, E]);
|
|
16
|
+
let M = D ?? A, N = u(() => ({
|
|
17
|
+
startMinute: y * 60,
|
|
18
|
+
endMinute: b * 60
|
|
19
|
+
}), [y, b]), P = (_ ?? M).toDateString(), F = u(() => n(new Date(P), v), [P, v]), I = u(() => s({
|
|
20
|
+
events: g,
|
|
21
|
+
days: F,
|
|
22
|
+
window: N
|
|
22
23
|
}), [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
]),
|
|
27
|
-
events:
|
|
28
|
-
days:
|
|
29
|
-
}), [
|
|
24
|
+
g,
|
|
25
|
+
F,
|
|
26
|
+
N
|
|
27
|
+
]), L = u(() => o({
|
|
28
|
+
events: g,
|
|
29
|
+
days: F
|
|
30
|
+
}), [g, F]), R = u(() => i(N), [N]), z = t(T, {
|
|
30
31
|
weekday: "short",
|
|
31
32
|
day: "numeric"
|
|
32
|
-
}),
|
|
33
|
+
}), B = t(T, {
|
|
33
34
|
hour: "2-digit",
|
|
34
35
|
minute: "2-digit"
|
|
35
|
-
}),
|
|
36
|
-
let n = t.currentTarget.getBoundingClientRect(), r = n.height > 0 ? (t.clientY - n.top) / n.height : 0, i =
|
|
36
|
+
}), V = (e, t) => {
|
|
37
|
+
let n = t.currentTarget.getBoundingClientRect(), r = n.height > 0 ? (t.clientY - n.top) / n.height : 0, i = N.startMinute + r * (N.endMinute - N.startMinute), a = Math.round(i / x) * x, o = Math.max(N.startMinute, Math.min(N.endMinute, a)), s = new Date(e);
|
|
37
38
|
return s.setHours(0, o, 0, 0), s;
|
|
38
|
-
},
|
|
39
|
-
return /* @__PURE__ */
|
|
40
|
-
className: e(
|
|
41
|
-
...
|
|
39
|
+
}, H = E ? r(M, N) : null, U = F.findIndex((e) => a(e, M));
|
|
40
|
+
return /* @__PURE__ */ m("div", {
|
|
41
|
+
className: e(c.wrapper, O),
|
|
42
|
+
...k,
|
|
42
43
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
44
|
-
className:
|
|
45
|
-
style: { "--tempest-scheduler-days":
|
|
46
|
-
children: [/* @__PURE__ */
|
|
47
|
-
className: e(
|
|
48
|
-
children:
|
|
44
|
+
/* @__PURE__ */ m("div", {
|
|
45
|
+
className: c.head,
|
|
46
|
+
style: { "--tempest-scheduler-days": v },
|
|
47
|
+
children: [/* @__PURE__ */ p("span", { className: c.gutterHead }), F.map((t, n) => /* @__PURE__ */ p("span", {
|
|
48
|
+
className: e(c.dayHead, n === U && c.today),
|
|
49
|
+
children: z.format(t)
|
|
49
50
|
}, t.toISOString()))]
|
|
50
51
|
}),
|
|
51
|
-
|
|
52
|
-
className:
|
|
53
|
-
style: { "--tempest-scheduler-days":
|
|
54
|
-
children: [/* @__PURE__ */
|
|
55
|
-
className:
|
|
52
|
+
L.length > 0 && /* @__PURE__ */ m("div", {
|
|
53
|
+
className: c.allDayLane,
|
|
54
|
+
style: { "--tempest-scheduler-days": v },
|
|
55
|
+
children: [/* @__PURE__ */ p("span", {
|
|
56
|
+
className: c.gutterHead,
|
|
56
57
|
children: "Dia inteiro"
|
|
57
|
-
}), /* @__PURE__ */
|
|
58
|
-
className:
|
|
59
|
-
children:
|
|
58
|
+
}), /* @__PURE__ */ p("div", {
|
|
59
|
+
className: c.allDayTrack,
|
|
60
|
+
children: L.map(({ event: e, dayIndex: t, span: n }) => /* @__PURE__ */ p("button", {
|
|
60
61
|
type: "button",
|
|
61
|
-
className:
|
|
62
|
+
className: c.allDayEvent,
|
|
62
63
|
style: { gridColumn: `${t + 1} / span ${n}` },
|
|
63
|
-
onClick: () =>
|
|
64
|
-
disabled: !
|
|
64
|
+
onClick: () => S?.(e),
|
|
65
|
+
disabled: !S,
|
|
65
66
|
children: e.title
|
|
66
67
|
}, e.id))
|
|
67
68
|
})]
|
|
68
69
|
}),
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
className:
|
|
71
|
-
style: { "--tempest-scheduler-days":
|
|
70
|
+
/* @__PURE__ */ m("div", {
|
|
71
|
+
className: c.body,
|
|
72
|
+
style: { "--tempest-scheduler-days": v },
|
|
72
73
|
tabIndex: 0,
|
|
73
74
|
role: "group",
|
|
74
75
|
"aria-label": "Grade de horários",
|
|
75
|
-
children: [/* @__PURE__ */
|
|
76
|
-
className:
|
|
77
|
-
children:
|
|
78
|
-
className:
|
|
79
|
-
style: { top: `${(e -
|
|
80
|
-
children:
|
|
76
|
+
children: [/* @__PURE__ */ p("div", {
|
|
77
|
+
className: c.gutter,
|
|
78
|
+
children: R.map((e) => /* @__PURE__ */ p("span", {
|
|
79
|
+
className: c.hourLabel,
|
|
80
|
+
style: { top: `${(e - N.startMinute) / (N.endMinute - N.startMinute) * 100}%` },
|
|
81
|
+
children: B.format(new Date(2026, 0, 1, Math.floor(e / 60), e % 60))
|
|
81
82
|
}, e))
|
|
82
|
-
}), /* @__PURE__ */
|
|
83
|
-
className:
|
|
83
|
+
}), /* @__PURE__ */ m("div", {
|
|
84
|
+
className: c.grid,
|
|
84
85
|
"aria-label": "Agenda",
|
|
85
86
|
children: [
|
|
86
|
-
|
|
87
|
-
className: e(
|
|
87
|
+
F.map((t, n) => /* @__PURE__ */ p("div", {
|
|
88
|
+
className: e(c.dayColumn, n === U && c.todayColumn),
|
|
88
89
|
role: "group",
|
|
89
|
-
"aria-label":
|
|
90
|
-
onClick:
|
|
91
|
-
e.target === e.currentTarget &&
|
|
90
|
+
"aria-label": z.format(t),
|
|
91
|
+
onClick: C ? (e) => {
|
|
92
|
+
e.target === e.currentTarget && C(V(t, e));
|
|
92
93
|
} : void 0,
|
|
93
|
-
children:
|
|
94
|
-
className:
|
|
95
|
-
style: { top: `${(e -
|
|
94
|
+
children: R.map((e) => /* @__PURE__ */ p("span", {
|
|
95
|
+
className: c.hourLine,
|
|
96
|
+
style: { top: `${(e - N.startMinute) / (N.endMinute - N.startMinute) * 100}%` }
|
|
96
97
|
}, e))
|
|
97
98
|
}, t.toISOString())),
|
|
98
|
-
|
|
99
|
+
I.map((e) => /* @__PURE__ */ p("button", {
|
|
99
100
|
type: "button",
|
|
100
|
-
className:
|
|
101
|
+
className: c.event,
|
|
101
102
|
style: {
|
|
102
103
|
gridColumn: `${e.dayIndex + 1} / span 1`,
|
|
103
104
|
top: `${e.top * 100}%`,
|
|
@@ -105,20 +106,20 @@ function h({ events: h, anchor: g, days: _ = 7, startHour: v = 8, endHour: y = 2
|
|
|
105
106
|
left: `${e.column / e.columns * 100}%`,
|
|
106
107
|
width: `${1 / e.columns * 100}%`
|
|
107
108
|
},
|
|
108
|
-
onClick: () =>
|
|
109
|
-
disabled: !
|
|
110
|
-
title: `${e.event.title} — ${
|
|
111
|
-
children:
|
|
112
|
-
className:
|
|
113
|
-
children:
|
|
114
|
-
}), /* @__PURE__ */
|
|
115
|
-
className:
|
|
109
|
+
onClick: () => S?.(e.event),
|
|
110
|
+
disabled: !S,
|
|
111
|
+
title: `${e.event.title} — ${B.format(e.event.start)}`,
|
|
112
|
+
children: w ? w(e.event) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("span", {
|
|
113
|
+
className: c.eventTime,
|
|
114
|
+
children: B.format(e.event.start)
|
|
115
|
+
}), /* @__PURE__ */ p("span", {
|
|
116
|
+
className: c.eventTitle,
|
|
116
117
|
children: e.event.title
|
|
117
118
|
})] })
|
|
118
119
|
}, `${e.event.id}-${e.dayIndex}`)),
|
|
119
|
-
|
|
120
|
-
className:
|
|
121
|
-
style: { top: `${
|
|
120
|
+
H !== null && U >= 0 && /* @__PURE__ */ p("span", {
|
|
121
|
+
className: c.nowLine,
|
|
122
|
+
style: { top: `${H * 100}%` },
|
|
122
123
|
"aria-hidden": !0,
|
|
123
124
|
"data-testid": "scheduler-now"
|
|
124
125
|
})
|
|
@@ -129,6 +130,6 @@ function h({ events: h, anchor: g, days: _ = 7, startHour: v = 8, endHour: y = 2
|
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
132
|
//#endregion
|
|
132
|
-
export {
|
|
133
|
+
export { g as Scheduler };
|
|
133
134
|
|
|
134
135
|
//# sourceMappingURL=Scheduler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Scheduler.js","names":[],"sources":["../../../src/components/Scheduler/Scheduler.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the grid is defined by\n * anchor, days, startHour, endHour and snapMinutes; the content by events,\n * renderEvent, onEventClick and onSlotClick; the reading by locale, showCurrentTime\n * and now. The body lays out overlapping events into columns, which needs the whole\n * day's events at once.\n */\nimport { type HTMLAttributes, type ReactNode, useEffect, useMemo, useState } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport {\n dayRange,\n type DayWindow,\n fractionOfWindow,\n hourMarks,\n isSameDay,\n layoutAllDay,\n layoutEvents,\n type SchedulerEvent,\n} from \"./scheduler-layout\";\nimport styles from \"./Scheduler.module.css\";\n\nexport interface SchedulerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Events to place. Instants are read in the browser's local time. */\n events: readonly SchedulerEvent[];\n /** Any day within the range to show. Default: today. */\n anchor?: Date;\n /** How many consecutive days to render. `1` is a day view, `7` a week. Default `7`. */\n days?: number;\n /** First visible hour, `0`–`23`. Default `8`. */\n startHour?: number;\n /** Last visible hour, `1`–`24`. Default `20`. */\n endHour?: number;\n /** Minutes a click on empty space snaps to. Default `30`. */\n snapMinutes?: number;\n /** Called when an event is activated by click, `Enter` or `Space`. */\n onEventClick?: (event: SchedulerEvent) => void;\n /** Called with the snapped instant when empty space is clicked. */\n onSlotClick?: (start: Date) => void;\n /** Render an event's contents. Defaults to its title and start time. */\n renderEvent?: (event: SchedulerEvent) => ReactNode;\n /** Locale for the day and hour labels. Default `\"pt-BR\"`. */\n locale?: string;\n /** Draw the current-time line. Default `true`. */\n showCurrentTime?: boolean;\n /** Fixed \"now\" for the indicator. Default: the real clock, ticking each minute. */\n now?: Date;\n}\n\n/** Minutes between ticks of the current-time indicator. */\nconst TICK_MS = 60_000;\n\n/**\n * An agenda: events placed on a time grid across consecutive days.\n *\n * `Calendar` is a date *picker* — it answers \"which day?\". This answers \"what is on\n * those days, and when\", which needs a different structure entirely: a vertical time\n * axis, events sized by duration, and overlapping events sitting side by side.\n *\n * That last part is the one worth naming. Overlapping events are grouped into\n * clusters of mutual overlap and every event in a cluster shares one column count,\n * so widths line up; a column is reused the moment it frees, so `9–10`, `9–10`,\n * `10–11` takes two columns and not three. The layout is pure and lives in\n * `scheduler-layout.ts`.\n *\n * Times are local. An event crossing midnight is split into both day columns, and\n * the day range is built by incrementing the calendar day, so a DST boundary does\n * not duplicate or skip a date.\n *\n * @example\n * <Scheduler\n * events={bookings}\n * days={7}\n * startHour={7}\n * endHour={21}\n * onEventClick={(e) => open(e.id)}\n * onSlotClick={(start) => createAt(start)}\n * />\n */\nexport function Scheduler({\n events,\n anchor,\n days: dayCount = 7,\n startHour = 8,\n endHour = 20,\n snapMinutes = 30,\n onEventClick,\n onSlotClick,\n renderEvent,\n locale = \"pt-BR\",\n showCurrentTime = true,\n now,\n className,\n ...rest\n}: SchedulerProps) {\n const [clock, setClock] = useState<Date>(() => now ?? new Date());\n\n /**\n * Keep the current-time line moving.\n *\n * Skipped entirely when `now` is supplied: that is the hook tests and demos use\n * to be deterministic, and a timer would fight it.\n */\n useEffect(() => {\n if (now || !showCurrentTime) return;\n const id = setInterval(() => setClock(new Date()), TICK_MS);\n return () => clearInterval(id);\n }, [now, showCurrentTime]);\n\n const reference = now ?? clock;\n\n const window = useMemo<DayWindow>(\n () => ({ startMinute: startHour * 60, endMinute: endHour * 60 }),\n [startHour, endHour],\n );\n\n /**\n * The calendar day the range starts from, as a stable string.\n *\n * `reference` ticks every minute when the current-time line is live. Depending on\n * the Date itself would re-slice the day range — and therefore relayout every\n * event — once a minute; depending on the day only recomputes at midnight.\n */\n const anchorDay = (anchor ?? reference).toDateString();\n const dayList = useMemo(() => dayRange(new Date(anchorDay), dayCount), [anchorDay, dayCount]);\n\n const placed = useMemo(\n () => layoutEvents({ events, days: dayList, window }),\n [events, dayList, window],\n );\n const allDay = useMemo(() => layoutAllDay({ events, days: dayList }), [events, dayList]);\n const marks = useMemo(() => hourMarks(window), [window]);\n\n const dayLabel = new Intl.DateTimeFormat(locale, { weekday: \"short\", day: \"numeric\" });\n const timeLabel = new Intl.DateTimeFormat(locale, { hour: \"2-digit\", minute: \"2-digit\" });\n\n /** Turn a click's vertical position within a day column into a snapped instant. */\n const slotFromClick = (day: Date, event: React.MouseEvent<HTMLDivElement>): Date => {\n const rect = event.currentTarget.getBoundingClientRect();\n const fraction = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0;\n const raw = window.startMinute + fraction * (window.endMinute - window.startMinute);\n const snapped = Math.round(raw / snapMinutes) * snapMinutes;\n const clamped = Math.max(window.startMinute, Math.min(window.endMinute, snapped));\n const result = new Date(day);\n result.setHours(0, clamped, 0, 0);\n return result;\n };\n\n const currentFraction = showCurrentTime ? fractionOfWindow(reference, window) : null;\n const todayIndex = dayList.findIndex((day) => isSameDay(day, reference));\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n <div\n className={styles.head}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n >\n <span className={styles.gutterHead} />\n {dayList.map((day, index) => (\n <span\n key={day.toISOString()}\n className={cn(styles.dayHead, index === todayIndex && styles.today)}\n >\n {dayLabel.format(day)}\n </span>\n ))}\n </div>\n\n {allDay.length > 0 && (\n <div\n className={styles.allDayLane}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n >\n <span className={styles.gutterHead}>Dia inteiro</span>\n <div className={styles.allDayTrack}>\n {allDay.map(({ event, dayIndex, span }) => (\n <button\n key={event.id}\n type=\"button\"\n className={styles.allDayEvent}\n style={{ gridColumn: `${dayIndex + 1} / span ${span}` }}\n onClick={() => onEventClick?.(event)}\n disabled={!onEventClick}\n >\n {event.title}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/*\n * Focusable because it scrolls vertically: a scroll region that cannot be\n * focused is unreachable by keyboard, which is what `axe`'s\n * `scrollable-region-focusable` rule is about. It needs a name too, or the\n * new tab stop would announce nothing — but `group`, not `region`: a named\n * `region` is a landmark, and two schedulers on one page would then be two\n * identically-named landmarks (`landmark-unique`).\n */}\n <div\n className={styles.body}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n tabIndex={0}\n role=\"group\"\n aria-label=\"Grade de horários\"\n >\n <div className={styles.gutter}>\n {marks.map((minute) => (\n <span\n key={minute}\n className={styles.hourLabel}\n style={{\n top: `${((minute - window.startMinute) / (window.endMinute - window.startMinute)) * 100}%`,\n }}\n >\n {timeLabel.format(\n new Date(2026, 0, 1, Math.floor(minute / 60), minute % 60),\n )}\n </span>\n ))}\n </div>\n\n {/*\n * Not `role=\"grid\"`: that requires `row` children, and the events are\n * siblings of the day columns inside one CSS grid — a `row` wrapper\n * would stop the columns being grid items and collapse the layout.\n * A labelled group per day is the honest structure anyway: a screen\n * reader tabs the event buttons and the group name supplies the day.\n */}\n <div className={styles.grid} aria-label=\"Agenda\">\n {dayList.map((day, index) => (\n <div\n key={day.toISOString()}\n className={cn(\n styles.dayColumn,\n index === todayIndex && styles.todayColumn,\n )}\n role=\"group\"\n aria-label={dayLabel.format(day)}\n onClick={\n onSlotClick\n ? (event) => {\n // Only empty space creates: a click that\n // landed on an event is that event's.\n if (event.target !== event.currentTarget) return;\n onSlotClick(slotFromClick(day, event));\n }\n : undefined\n }\n >\n {marks.map((minute) => (\n <span\n key={minute}\n className={styles.hourLine}\n style={{\n top: `${((minute - window.startMinute) / (window.endMinute - window.startMinute)) * 100}%`,\n }}\n />\n ))}\n </div>\n ))}\n\n {placed.map((item) => (\n <button\n key={`${item.event.id}-${item.dayIndex}`}\n type=\"button\"\n className={styles.event}\n style={{\n gridColumn: `${item.dayIndex + 1} / span 1`,\n top: `${item.top * 100}%`,\n height: `${item.height * 100}%`,\n left: `${(item.column / item.columns) * 100}%`,\n width: `${(1 / item.columns) * 100}%`,\n }}\n onClick={() => onEventClick?.(item.event)}\n disabled={!onEventClick}\n title={`${item.event.title} — ${timeLabel.format(item.event.start)}`}\n >\n {renderEvent ? (\n renderEvent(item.event)\n ) : (\n <>\n <span className={styles.eventTime}>\n {timeLabel.format(item.event.start)}\n </span>\n <span className={styles.eventTitle}>{item.event.title}</span>\n </>\n )}\n </button>\n ))}\n\n {currentFraction !== null && todayIndex >= 0 && (\n <span\n className={styles.nowLine}\n style={{ top: `${currentFraction * 100}%` }}\n aria-hidden\n data-testid=\"scheduler-now\"\n />\n )}\n </div>\n </div>\n </div>\n );\n}\n\nexport type { SchedulerEvent };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Scheduler.js","names":[],"sources":["../../../src/components/Scheduler/Scheduler.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — the grid is defined by\n * anchor, days, startHour, endHour and snapMinutes; the content by events,\n * renderEvent, onEventClick and onSlotClick; the reading by locale, showCurrentTime\n * and now. The body lays out overlapping events into columns, which needs the whole\n * day's events at once.\n */\nimport { type HTMLAttributes, type ReactNode, useEffect, useMemo, useState } from \"react\";\n\nimport { cn } from \"@/utils/cn\";\n\nimport {\n dayRange,\n type DayWindow,\n fractionOfWindow,\n hourMarks,\n isSameDay,\n layoutAllDay,\n layoutEvents,\n type SchedulerEvent,\n} from \"./scheduler-layout\";\nimport styles from \"./Scheduler.module.css\";\nimport { dateTimeFormat } from \"@/utils/intl-cache\";\n\nexport interface SchedulerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n /** Events to place. Instants are read in the browser's local time. */\n events: readonly SchedulerEvent[];\n /** Any day within the range to show. Default: today. */\n anchor?: Date;\n /** How many consecutive days to render. `1` is a day view, `7` a week. Default `7`. */\n days?: number;\n /** First visible hour, `0`–`23`. Default `8`. */\n startHour?: number;\n /** Last visible hour, `1`–`24`. Default `20`. */\n endHour?: number;\n /** Minutes a click on empty space snaps to. Default `30`. */\n snapMinutes?: number;\n /** Called when an event is activated by click, `Enter` or `Space`. */\n onEventClick?: (event: SchedulerEvent) => void;\n /** Called with the snapped instant when empty space is clicked. */\n onSlotClick?: (start: Date) => void;\n /** Render an event's contents. Defaults to its title and start time. */\n renderEvent?: (event: SchedulerEvent) => ReactNode;\n /** Locale for the day and hour labels. Default `\"pt-BR\"`. */\n locale?: string;\n /** Draw the current-time line. Default `true`. */\n showCurrentTime?: boolean;\n /** Fixed \"now\" for the indicator. Default: the real clock, ticking each minute. */\n now?: Date;\n}\n\n/** Minutes between ticks of the current-time indicator. */\nconst TICK_MS = 60_000;\n\n/**\n * An agenda: events placed on a time grid across consecutive days.\n *\n * `Calendar` is a date *picker* — it answers \"which day?\". This answers \"what is on\n * those days, and when\", which needs a different structure entirely: a vertical time\n * axis, events sized by duration, and overlapping events sitting side by side.\n *\n * That last part is the one worth naming. Overlapping events are grouped into\n * clusters of mutual overlap and every event in a cluster shares one column count,\n * so widths line up; a column is reused the moment it frees, so `9–10`, `9–10`,\n * `10–11` takes two columns and not three. The layout is pure and lives in\n * `scheduler-layout.ts`.\n *\n * Times are local. An event crossing midnight is split into both day columns, and\n * the day range is built by incrementing the calendar day, so a DST boundary does\n * not duplicate or skip a date.\n *\n * @example\n * <Scheduler\n * events={bookings}\n * days={7}\n * startHour={7}\n * endHour={21}\n * onEventClick={(e) => open(e.id)}\n * onSlotClick={(start) => createAt(start)}\n * />\n */\nexport function Scheduler({\n events,\n anchor,\n days: dayCount = 7,\n startHour = 8,\n endHour = 20,\n snapMinutes = 30,\n onEventClick,\n onSlotClick,\n renderEvent,\n locale = \"pt-BR\",\n showCurrentTime = true,\n now,\n className,\n ...rest\n}: SchedulerProps) {\n const [clock, setClock] = useState<Date>(() => now ?? new Date());\n\n /**\n * Keep the current-time line moving.\n *\n * Skipped entirely when `now` is supplied: that is the hook tests and demos use\n * to be deterministic, and a timer would fight it.\n */\n useEffect(() => {\n if (now || !showCurrentTime) return;\n const id = setInterval(() => setClock(new Date()), TICK_MS);\n return () => clearInterval(id);\n }, [now, showCurrentTime]);\n\n const reference = now ?? clock;\n\n const window = useMemo<DayWindow>(\n () => ({ startMinute: startHour * 60, endMinute: endHour * 60 }),\n [startHour, endHour],\n );\n\n /**\n * The calendar day the range starts from, as a stable string.\n *\n * `reference` ticks every minute when the current-time line is live. Depending on\n * the Date itself would re-slice the day range — and therefore relayout every\n * event — once a minute; depending on the day only recomputes at midnight.\n */\n const anchorDay = (anchor ?? reference).toDateString();\n const dayList = useMemo(() => dayRange(new Date(anchorDay), dayCount), [anchorDay, dayCount]);\n\n const placed = useMemo(\n () => layoutEvents({ events, days: dayList, window }),\n [events, dayList, window],\n );\n const allDay = useMemo(() => layoutAllDay({ events, days: dayList }), [events, dayList]);\n const marks = useMemo(() => hourMarks(window), [window]);\n\n const dayLabel = dateTimeFormat(locale, { weekday: \"short\", day: \"numeric\" });\n const timeLabel = dateTimeFormat(locale, { hour: \"2-digit\", minute: \"2-digit\" });\n\n /** Turn a click's vertical position within a day column into a snapped instant. */\n const slotFromClick = (day: Date, event: React.MouseEvent<HTMLDivElement>): Date => {\n const rect = event.currentTarget.getBoundingClientRect();\n const fraction = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0;\n const raw = window.startMinute + fraction * (window.endMinute - window.startMinute);\n const snapped = Math.round(raw / snapMinutes) * snapMinutes;\n const clamped = Math.max(window.startMinute, Math.min(window.endMinute, snapped));\n const result = new Date(day);\n result.setHours(0, clamped, 0, 0);\n return result;\n };\n\n const currentFraction = showCurrentTime ? fractionOfWindow(reference, window) : null;\n const todayIndex = dayList.findIndex((day) => isSameDay(day, reference));\n\n return (\n <div className={cn(styles.wrapper, className)} {...rest}>\n <div\n className={styles.head}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n >\n <span className={styles.gutterHead} />\n {dayList.map((day, index) => (\n <span\n key={day.toISOString()}\n className={cn(styles.dayHead, index === todayIndex && styles.today)}\n >\n {dayLabel.format(day)}\n </span>\n ))}\n </div>\n\n {allDay.length > 0 && (\n <div\n className={styles.allDayLane}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n >\n <span className={styles.gutterHead}>Dia inteiro</span>\n <div className={styles.allDayTrack}>\n {allDay.map(({ event, dayIndex, span }) => (\n <button\n key={event.id}\n type=\"button\"\n className={styles.allDayEvent}\n style={{ gridColumn: `${dayIndex + 1} / span ${span}` }}\n onClick={() => onEventClick?.(event)}\n disabled={!onEventClick}\n >\n {event.title}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/*\n * Focusable because it scrolls vertically: a scroll region that cannot be\n * focused is unreachable by keyboard, which is what `axe`'s\n * `scrollable-region-focusable` rule is about. It needs a name too, or the\n * new tab stop would announce nothing — but `group`, not `region`: a named\n * `region` is a landmark, and two schedulers on one page would then be two\n * identically-named landmarks (`landmark-unique`).\n */}\n <div\n className={styles.body}\n style={{ [\"--tempest-scheduler-days\" as string]: dayCount }}\n tabIndex={0}\n role=\"group\"\n aria-label=\"Grade de horários\"\n >\n <div className={styles.gutter}>\n {marks.map((minute) => (\n <span\n key={minute}\n className={styles.hourLabel}\n style={{\n top: `${((minute - window.startMinute) / (window.endMinute - window.startMinute)) * 100}%`,\n }}\n >\n {timeLabel.format(\n new Date(2026, 0, 1, Math.floor(minute / 60), minute % 60),\n )}\n </span>\n ))}\n </div>\n\n {/*\n * Not `role=\"grid\"`: that requires `row` children, and the events are\n * siblings of the day columns inside one CSS grid — a `row` wrapper\n * would stop the columns being grid items and collapse the layout.\n * A labelled group per day is the honest structure anyway: a screen\n * reader tabs the event buttons and the group name supplies the day.\n */}\n <div className={styles.grid} aria-label=\"Agenda\">\n {dayList.map((day, index) => (\n <div\n key={day.toISOString()}\n className={cn(\n styles.dayColumn,\n index === todayIndex && styles.todayColumn,\n )}\n role=\"group\"\n aria-label={dayLabel.format(day)}\n onClick={\n onSlotClick\n ? (event) => {\n // Only empty space creates: a click that\n // landed on an event is that event's.\n if (event.target !== event.currentTarget) return;\n onSlotClick(slotFromClick(day, event));\n }\n : undefined\n }\n >\n {marks.map((minute) => (\n <span\n key={minute}\n className={styles.hourLine}\n style={{\n top: `${((minute - window.startMinute) / (window.endMinute - window.startMinute)) * 100}%`,\n }}\n />\n ))}\n </div>\n ))}\n\n {placed.map((item) => (\n <button\n key={`${item.event.id}-${item.dayIndex}`}\n type=\"button\"\n className={styles.event}\n style={{\n gridColumn: `${item.dayIndex + 1} / span 1`,\n top: `${item.top * 100}%`,\n height: `${item.height * 100}%`,\n left: `${(item.column / item.columns) * 100}%`,\n width: `${(1 / item.columns) * 100}%`,\n }}\n onClick={() => onEventClick?.(item.event)}\n disabled={!onEventClick}\n title={`${item.event.title} — ${timeLabel.format(item.event.start)}`}\n >\n {renderEvent ? (\n renderEvent(item.event)\n ) : (\n <>\n <span className={styles.eventTime}>\n {timeLabel.format(item.event.start)}\n </span>\n <span className={styles.eventTitle}>{item.event.title}</span>\n </>\n )}\n </button>\n ))}\n\n {currentFraction !== null && todayIndex >= 0 && (\n <span\n className={styles.nowLine}\n style={{ top: `${currentFraction * 100}%` }}\n aria-hidden\n data-testid=\"scheduler-now\"\n />\n )}\n </div>\n </div>\n </div>\n );\n}\n\nexport type { SchedulerEvent };\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("./Toast.module.cjs");let
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Portal/portal-host.cjs"),n=require("./Toast.module.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("react-dom");var o=(0,r.createContext)(null);function s(){let e=(0,r.useContext)(o);if(!e)throw Error(`useToast must be used inside a <ToastProvider>`);return e}function c({children:e,defaultDuration:t=4e3,position:n=`top-right`}){let[a,s]=(0,r.useState)([]),c=(0,r.useRef)(0),l=(0,r.useCallback)(e=>{s(t=>t.filter(t=>t.id!==e))},[]),d=(0,r.useCallback)(e=>{let n=e.id??`toast-${++c.current}`,r={id:n,title:e.title,description:e.description,variant:e.variant??`info`,duration:e.duration??t};return s(e=>[...e,r]),n},[t]),f=(0,r.useMemo)(()=>({show:d,dismiss:l,success:(e,t)=>d({...t,description:e,variant:`success`}),error:(e,t)=>d({...t,description:e,variant:`error`}),warning:(e,t)=>d({...t,description:e,variant:`warning`}),info:(e,t)=>d({...t,description:e,variant:`info`})}),[d,l]);return(0,i.jsxs)(o.Provider,{value:f,children:[e,(0,i.jsx)(u,{toasts:a,onDismiss:l,position:n})]})}function l(e){switch(e){case`top-left`:return n.default.positionTopLeft;case`top-center`:return n.default.positionTopCenter;case`bottom-right`:return n.default.positionBottomRight;case`bottom-left`:return n.default.positionBottomLeft;case`bottom-center`:return n.default.positionBottomCenter;default:return n.default.positionTopRight}}function u({toasts:r,onDismiss:o,position:s}){let c=t.usePortalHost();return c?(0,a.createPortal)((0,i.jsx)(`div`,{className:e.cn(n.default.container,l(s)),"aria-live":`polite`,"aria-atomic":`false`,children:r.map(e=>(0,i.jsx)(d,{toast:e,onDismiss:o},e.id))}),c):null}function d({toast:t,onDismiss:a}){return(0,r.useEffect)(()=>{if(!t.duration)return;let e=setTimeout(()=>a(t.id),t.duration);return()=>clearTimeout(e)},[t.id,t.duration,a]),(0,i.jsxs)(`div`,{className:e.cn(n.default.toast,n.default[t.variant]),children:[(0,i.jsxs)(`div`,{children:[t.title&&(0,i.jsx)(`p`,{className:n.default.title,children:t.title}),t.description&&(0,i.jsx)(`p`,{className:n.default.description,children:t.description})]}),(0,i.jsx)(`button`,{type:`button`,className:n.default.close,"aria-label":`Fechar notificação`,onClick:()=>a(t.id),children:(0,i.jsx)(f,{})})]})}function f(){return(0,i.jsx)(`svg`,{width:`14`,height:`14`,viewBox:`0 0 24 24`,fill:`none`,children:(0,i.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.ToastProvider=c,exports.useToast=s;
|
|
2
2
|
//# sourceMappingURL=ToastProvider.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToastProvider.cjs","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.cjs","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,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":";;;;;;AAoDA,IAAM,IAAe,EAA+B,IAAI;AAOxD,SAAgB,IAAqB;CACjC,IAAM,IAAM,EAAW,CAAY;CACnC,IAAI,CAAC,GAAK,MAAU,MAAM,gDAAgD;CAC1E,OAAO;AACX;AAgBA,SAAgB,EAAc,EAC1B,aACA,qBAAkB,KAClB,cAAW,eACQ;CACnB,IAAM,CAAC,GAAQ,KAAa,EAAuB,CAAC,CAAC,GAC/C,IAAU,EAAe,CAAC,GAE1B,IAAU,GAAa,MAAqB;EAC9C,GAAW,MAAY,EAAQ,QAAQ,MAAU,EAAM,OAAO,CAAE,CAAC;CACrE,GAAG,CAAC,CAAC,GAEC,IAAO,GACR,MAAkC;EAC/B,IAAM,IAAK,EAAQ,MAAM,SAAS,EAAE,EAAQ,WACtC,IAAoB;GACtB;GACA,OAAO,EAAQ;GACf,aAAa,EAAQ;GACrB,SAAS,EAAQ,WAAW;GAC5B,UAAU,EAAQ,YAAY;EAClC;EAEA,OADA,GAAW,MAAY,CAAC,GAAG,GAAS,CAAK,CAAC,GACnC;CACX,GACA,CAAC,CAAe,CACpB,GAEM,IAAM,SACD;EACH;EACA;EACA,UAAU,GAAa,MACnB,EAAK;GAAE,GAAG;GAAS;GAAa,SAAS;EAAU,CAAC;EACxD,QAAQ,GAAa,MAAY,EAAK;GAAE,GAAG;GAAS;GAAa,SAAS;EAAQ,CAAC;EACnF,UAAU,GAAa,MACnB,EAAK;GAAE,GAAG;GAAS;GAAa,SAAS;EAAU,CAAC;EACxD,OAAO,GAAa,MAAY,EAAK;GAAE,GAAG;GAAS;GAAa,SAAS;EAAO,CAAC;CACrF,IACA,CAAC,GAAM,CAAO,CAClB;CAEA,OACI,kBAAC,EAAa,UAAd;EAAuB,OAAO;EAA9B,UAAA,CACK,GACD,kBAAC,GAAD;GAAwB;GAAQ,WAAW;GAAmB;EAAW,CAAA,CACtD;;AAE/B;AAQA,SAAS,EAAc,GAAiC;CACpD,QAAQ,GAAR;EACI,KAAK,YACD,OAAO,EAAO;EAClB,KAAK,cACD,OAAO,EAAO;EAClB,KAAK,gBACD,OAAO,EAAO;EAClB,KAAK,eACD,OAAO,EAAO;EAClB,KAAK,iBACD,OAAO,EAAO;EAElB,SACI,OAAO,EAAO;CACtB;AACJ;AAgBA,SAAS,EAAe,EAAE,WAAQ,cAAW,eAA4B;CAErE,OADI,OAAO,WAAa,MAAoB,OACrC,EACH,kBAAC,OAAD;EACI,WAAW,EAAG,EAAO,WAAW,EAAc,CAAQ,CAAC;EACvD,aAAU;EACV,eAAY;EAEX,UAAA,EAAO,KAAK,MACT,kBAAC,GAAD;GAAiC;GAAkB;EAAY,GAA/C,EAAM,EAAyC,CAClE;CACA,CAAA,GACL,SAAS,IACb;AACJ;AAcA,SAAS,EAAU,EAAE,UAAO,gBAAwB;CAOhD,OANA,QAAgB;EACZ,IAAI,CAAC,EAAM,UAAU;EACrB,IAAM,IAAQ,iBAAiB,EAAU,EAAM,EAAE,GAAG,EAAM,QAAQ;EAClE,aAAa,aAAa,CAAK;CACnC,GAAG;EAAC,EAAM;EAAI,EAAM;EAAU;CAAS,CAAC,GAGpC,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,OAAO,EAAO,EAAM,QAAQ;EAAtD,UAAA,CACI,kBAAC,OAAD,EAAA,UAAA,CACK,EAAM,SAAS,kBAAC,KAAD;GAAG,WAAW,EAAO;GAAQ,UAAA,EAAM;EAAS,CAAA,GAC3D,EAAM,eAAe,kBAAC,KAAD;GAAG,WAAW,EAAO;GAAc,UAAA,EAAM;EAAe,CAAA,CAC7E,EAAA,CAAA,GACL,kBAAC,UAAD;GACI,MAAK;GACL,WAAW,EAAO;GAClB,cAAW;GACX,eAAe,EAAU,EAAM,EAAE;GAEjC,UAAA,kBAAC,GAAD,CAAY,CAAA;EACR,CAAA,CACP;;AAEb;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
|
|
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
|