tempest-react-sdk 0.38.3 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/_virtual/_rolldown/runtime.cjs +1 -1
- package/dist/access/access-control-context.cjs.map +1 -1
- package/dist/access/use-can.cjs.map +1 -1
- package/dist/app/AppProviders.cjs.map +1 -1
- package/dist/app/AppProviders.js.map +1 -1
- package/dist/audio/use-audio-recorder.cjs.map +1 -1
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-microphone.cjs.map +1 -1
- package/dist/auth/create-auth-store.cjs.map +1 -1
- package/dist/auth/lazy-with-retry.cjs.map +1 -1
- package/dist/auth/use-passkey.cjs.map +1 -1
- package/dist/br/BrazilMap.cjs +1 -1
- package/dist/br/BrazilMap.cjs.map +1 -1
- package/dist/br/BrazilMap.js +2 -2
- package/dist/br/BrazilMap.js.map +1 -1
- package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
- package/dist/br/BrazilStateCitySelect.js.map +1 -1
- package/dist/br/BrazilStateMap.cjs +1 -1
- package/dist/br/BrazilStateMap.cjs.map +1 -1
- package/dist/br/BrazilStateMap.js +2 -2
- package/dist/br/BrazilStateMap.js.map +1 -1
- package/dist/br/MapLegend.cjs.map +1 -1
- package/dist/br/MapLegend.js.map +1 -1
- package/dist/br/MapMarkers.cjs.map +1 -1
- package/dist/br/MapMarkers.js.map +1 -1
- package/dist/br/MunicipalitySearch.cjs.map +1 -1
- package/dist/br/MunicipalitySearch.js.map +1 -1
- package/dist/br/PixQRCode.cjs.map +1 -1
- package/dist/br/PixQRCode.js.map +1 -1
- package/dist/br/use-map-hover.cjs.map +1 -1
- package/dist/br/use-map-zoom.cjs.map +1 -1
- package/dist/capture/media-recorder.cjs +1 -1
- package/dist/capture/media-recorder.js +2 -2
- package/dist/capture/use-barcode-scanner.cjs +1 -1
- package/dist/capture/use-barcode-scanner.cjs.map +1 -1
- package/dist/capture/use-barcode-scanner.js +2 -2
- package/dist/capture/use-barcode-scanner.js.map +1 -1
- package/dist/capture/use-screen-capture.cjs.map +1 -1
- package/dist/capture/use-speech-recognition.cjs.map +1 -1
- package/dist/capture/use-torch.cjs.map +1 -1
- package/dist/capture/use-video-recorder.cjs.map +1 -1
- package/dist/charts/AreaChart.cjs.map +1 -1
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.cjs.map +1 -1
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/LineChart.cjs.map +1 -1
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/PieChart.cjs.map +1 -1
- package/dist/charts/PieChart.js.map +1 -1
- package/dist/charts/RadarChart.cjs.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.cjs.map +1 -1
- package/dist/components/AIChat/AIChat.js.map +1 -1
- package/dist/components/AIChat/AIChatComposer.cjs.map +1 -1
- package/dist/components/AIChat/AIChatComposer.js.map +1 -1
- package/dist/components/AIChat/AIChatTurn.cjs.map +1 -1
- package/dist/components/AIChat/AIChatTurn.js.map +1 -1
- package/dist/components/Accordion/Accordion.cjs.map +1 -1
- package/dist/components/Accordion/Accordion.js.map +1 -1
- package/dist/components/Alert/Alert.cjs.map +1 -1
- package/dist/components/Alert/Alert.js.map +1 -1
- package/dist/components/AppBar/AppBar.cjs.map +1 -1
- package/dist/components/AppBar/AppBar.js.map +1 -1
- package/dist/components/AppShell/AppShell.cjs.map +1 -1
- package/dist/components/AppShell/AppShell.js.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.cjs.map +1 -1
- package/dist/components/AudioPlayer/AudioPlayer.js.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.cjs.map +1 -1
- package/dist/components/AudioRecorder/AudioRecorder.js.map +1 -1
- package/dist/components/Avatar/Avatar.cjs.map +1 -1
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -1
- package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -1
- package/dist/components/Badge/Badge.cjs.map +1 -1
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Banner/Banner.cjs.map +1 -1
- package/dist/components/Banner/Banner.js.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.cjs.map +1 -1
- package/dist/components/BarcodeScanner/BarcodeScanner.js.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
- package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
- package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs.map +1 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/components/Button/Button.cjs.map +1 -1
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Calendar/Calendar.cjs +1 -1
- package/dist/components/Calendar/Calendar.cjs.map +1 -1
- package/dist/components/Calendar/Calendar.js +38 -41
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Card/Card.cjs.map +1 -1
- package/dist/components/Card/Card.js.map +1 -1
- package/dist/components/Carousel/Carousel.cjs.map +1 -1
- package/dist/components/Carousel/Carousel.js.map +1 -1
- package/dist/components/Chat/Chat.cjs.map +1 -1
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/Chat/ChatComposer.cjs.map +1 -1
- package/dist/components/Chat/ChatComposer.js.map +1 -1
- package/dist/components/Chat/chat-groups.cjs +1 -1
- package/dist/components/Chat/chat-groups.cjs.map +1 -1
- package/dist/components/Chat/chat-groups.js +2 -2
- package/dist/components/Chat/chat-groups.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.cjs.map +1 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/ChipInput/ChipInput.cjs.map +1 -1
- package/dist/components/ChipInput/ChipInput.js.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.cjs.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.js.map +1 -1
- package/dist/components/Collapsible/Collapsible.cjs.map +1 -1
- package/dist/components/Collapsible/Collapsible.js.map +1 -1
- package/dist/components/Combobox/Combobox.cjs.map +1 -1
- package/dist/components/Combobox/Combobox.js.map +1 -1
- package/dist/components/Command/Command.cjs.map +1 -1
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.cjs.map +1 -1
- package/dist/components/ConfirmDialog/ConfirmDialog.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/components/CopyButton/CopyButton.cjs.map +1 -1
- package/dist/components/CopyButton/CopyButton.js.map +1 -1
- package/dist/components/DataList/DataList.cjs.map +1 -1
- package/dist/components/DataList/DataList.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +2 -2
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/EditableCell.cjs.map +1 -1
- package/dist/components/DataTable/EditableCell.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.cjs.map +1 -1
- package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
- package/dist/components/DescriptionList/DescriptionList.cjs.map +1 -1
- package/dist/components/DescriptionList/DescriptionList.js.map +1 -1
- package/dist/components/Divider/Divider.cjs.map +1 -1
- package/dist/components/Divider/Divider.js.map +1 -1
- package/dist/components/Drawer/Drawer.cjs.map +1 -1
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/Dropzone/Dropzone.cjs.map +1 -1
- package/dist/components/Dropzone/Dropzone.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.cjs.map +1 -1
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/ErrorState/ErrorState.cjs.map +1 -1
- package/dist/components/ErrorState/ErrorState.js.map +1 -1
- package/dist/components/FileUpload/FileUpload.cjs +1 -1
- package/dist/components/FileUpload/FileUpload.cjs.map +1 -1
- package/dist/components/FileUpload/FileUpload.js +1 -1
- package/dist/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/components/FilterBar/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar/FilterBar.js.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.cjs.map +1 -1
- package/dist/components/FloatingActionButton/FloatingActionButton.js.map +1 -1
- package/dist/components/Form/Form.cjs.map +1 -1
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.cjs.map +1 -1
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/Image/Image.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.cjs +1 -1
- package/dist/components/ImageCropper/ImageCropper.cjs.map +1 -1
- package/dist/components/ImageCropper/ImageCropper.js +1 -1
- package/dist/components/ImageCropper/ImageCropper.js.map +1 -1
- package/dist/components/Input/Input.cjs.map +1 -1
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
- package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
- package/dist/components/InstallButton/InstallButton.js.map +1 -1
- package/dist/components/Kanban/Kanban.cjs.map +1 -1
- package/dist/components/Kanban/Kanban.js.map +1 -1
- package/dist/components/Label/Label.cjs.map +1 -1
- package/dist/components/Label/Label.js.map +1 -1
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -1
- package/dist/components/Lightbox/Lightbox.js.map +1 -1
- package/dist/components/ListTile/ListTile.cjs.map +1 -1
- package/dist/components/ListTile/ListTile.js.map +1 -1
- package/dist/components/Markdown/Markdown.cjs +1 -1
- package/dist/components/Markdown/Markdown.cjs.map +1 -1
- package/dist/components/Markdown/Markdown.js +2 -2
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Masonry/Masonry.cjs.map +1 -1
- package/dist/components/Masonry/Masonry.js.map +1 -1
- package/dist/components/Menubar/Menubar.cjs.map +1 -1
- package/dist/components/Menubar/Menubar.js.map +1 -1
- package/dist/components/Modal/Modal.cjs.map +1 -1
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/ModalsManager/ModalsManager.cjs.map +1 -1
- package/dist/components/ModalsManager/ModalsManager.js.map +1 -1
- package/dist/components/Money/Money.cjs.map +1 -1
- package/dist/components/Money/Money.js.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/components/NProgress/NProgress.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.cjs.map +1 -1
- package/dist/components/Navbar/Navbar.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenu.js.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.cjs.map +1 -1
- package/dist/components/NavigationRail/NavigationRail.js.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -1
- package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -1
- package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.cjs.map +1 -1
- package/dist/components/OfflineIndicator/OfflineIndicator.js.map +1 -1
- package/dist/components/Page/Page.cjs.map +1 -1
- package/dist/components/Page/Page.js.map +1 -1
- package/dist/components/Pagination/Pagination.cjs.map +1 -1
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.cjs.map +1 -1
- package/dist/components/PasswordInput/PasswordInput.js.map +1 -1
- package/dist/components/PinInput/PinInput.cjs.map +1 -1
- package/dist/components/PinInput/PinInput.js.map +1 -1
- package/dist/components/Popover/Popover.cjs.map +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Portal/Portal.cjs.map +1 -1
- package/dist/components/Progress/Progress.cjs.map +1 -1
- package/dist/components/Progress/Progress.js.map +1 -1
- package/dist/components/QRCode/QRCode.cjs.map +1 -1
- package/dist/components/QRCode/QRCode.js.map +1 -1
- package/dist/components/Radio/Radio.cjs.map +1 -1
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.cjs.map +1 -1
- package/dist/components/RangeSlider/RangeSlider.js.map +1 -1
- package/dist/components/RatingStars/RatingStars.cjs.map +1 -1
- package/dist/components/RatingStars/RatingStars.js.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
- package/dist/components/RelativeTime/RelativeTime.cjs.map +1 -1
- package/dist/components/RelativeTime/RelativeTime.js.map +1 -1
- package/dist/components/Resizable/Resizable.cjs.map +1 -1
- package/dist/components/Resizable/Resizable.js.map +1 -1
- package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
- package/dist/components/Scheduler/Scheduler.js.map +1 -1
- package/dist/components/ScrollArea/ScrollArea.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.cjs.map +1 -1
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.cjs.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/Select/Select.cjs.map +1 -1
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.cjs.map +1 -1
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -1
- package/dist/components/Slider/Slider.cjs.map +1 -1
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/Sparkline/Sparkline.cjs.map +1 -1
- package/dist/components/Sparkline/Sparkline.js.map +1 -1
- package/dist/components/Spinner/Spinner.cjs.map +1 -1
- package/dist/components/Spinner/Spinner.js.map +1 -1
- package/dist/components/Stat/Stat.cjs.map +1 -1
- package/dist/components/Stat/Stat.js.map +1 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/StepperInput/StepperInput.cjs.map +1 -1
- package/dist/components/StepperInput/StepperInput.js.map +1 -1
- package/dist/components/Switch/Switch.cjs.map +1 -1
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.cjs.map +1 -1
- package/dist/components/SyncStatusBadge/SyncStatusBadge.js.map +1 -1
- package/dist/components/Table/Table.cjs.map +1 -1
- package/dist/components/Table/Table.js.map +1 -1
- package/dist/components/Tabs/Tabs.cjs.map +1 -1
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Tag/Tag.cjs.map +1 -1
- package/dist/components/Tag/Tag.js.map +1 -1
- package/dist/components/Textarea/Textarea.cjs.map +1 -1
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.cjs.map +1 -1
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/Timeline/Timeline.cjs.map +1 -1
- package/dist/components/Timeline/Timeline.js.map +1 -1
- package/dist/components/Toast/ToastProvider.cjs.map +1 -1
- package/dist/components/Toast/ToastProvider.js.map +1 -1
- package/dist/components/Toggle/Toggle.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.cjs.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tour/Tour.cjs.map +1 -1
- package/dist/components/Tour/Tour.js.map +1 -1
- package/dist/components/Transfer/Transfer.cjs.map +1 -1
- package/dist/components/Transfer/Transfer.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs +1 -1
- package/dist/components/TreeView/TreeView.cjs.map +1 -1
- package/dist/components/TreeView/TreeView.js +1 -4
- package/dist/components/TreeView/TreeView.js.map +1 -1
- package/dist/components/UpdatePrompt/UpdatePrompt.cjs.map +1 -1
- package/dist/components/UpdatePrompt/UpdatePrompt.js.map +1 -1
- package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
- package/dist/components/VirtualList/VirtualList.js.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
- package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
- package/dist/components/VisuallyHidden/VisuallyHidden.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.cjs.map +1 -1
- package/dist/components/Wizard/Wizard.js.map +1 -1
- package/dist/data/data-provider-context.cjs.map +1 -1
- package/dist/data/use-resource.cjs.map +1 -1
- package/dist/editor/RichTextEditor.cjs.map +1 -1
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/error-boundary/use-error-handler.cjs.map +1 -1
- package/dist/feature-flags/FeatureFlagsProvider.cjs.map +1 -1
- package/dist/forms/FormField.cjs.map +1 -1
- package/dist/forms/masked-inputs.cjs.map +1 -1
- package/dist/forms/use-viacep.cjs.map +1 -1
- package/dist/forms/use-zod-form.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.cjs.map +1 -1
- package/dist/geo/TrajectoryMap.js.map +1 -1
- package/dist/geo/leaflet-map.cjs.map +1 -1
- package/dist/geo/leaflet-map.js.map +1 -1
- package/dist/geo/use-position-tracker.cjs.map +1 -1
- package/dist/hooks/use-announce.cjs.map +1 -1
- package/dist/hooks/use-async.cjs.map +1 -1
- package/dist/hooks/use-before-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-breakpoint.cjs.map +1 -1
- package/dist/hooks/use-click-outside.cjs.map +1 -1
- package/dist/hooks/use-client-filter.cjs.map +1 -1
- package/dist/hooks/use-clipboard.cjs.map +1 -1
- package/dist/hooks/use-counter.cjs.map +1 -1
- package/dist/hooks/use-debounce.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-disclosure.cjs.map +1 -1
- package/dist/hooks/use-document-visibility.cjs.map +1 -1
- package/dist/hooks/use-event-listener.cjs.map +1 -1
- package/dist/hooks/use-geolocation.cjs.map +1 -1
- package/dist/hooks/use-hover.cjs.map +1 -1
- package/dist/hooks/use-idle.cjs.map +1 -1
- package/dist/hooks/use-install-prompt.cjs +1 -1
- package/dist/hooks/use-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-install-prompt.js +1 -1
- package/dist/hooks/use-install-prompt.js.map +1 -1
- package/dist/hooks/use-intersection-observer.cjs.map +1 -1
- package/dist/hooks/use-interval.cjs.map +1 -1
- package/dist/hooks/use-is-first-render.cjs.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.cjs +1 -1
- package/dist/hooks/use-keyboard-shortcut.cjs.map +1 -1
- package/dist/hooks/use-keyboard-shortcut.js +1 -1
- package/dist/hooks/use-keyboard-shortcut.js.map +1 -1
- package/dist/hooks/use-list-state.cjs.map +1 -1
- package/dist/hooks/use-local-storage.cjs.map +1 -1
- package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
- package/dist/hooks/use-long-press.cjs.map +1 -1
- package/dist/hooks/use-map.cjs.map +1 -1
- package/dist/hooks/use-media-devices.cjs.map +1 -1
- package/dist/hooks/use-media-permission.cjs.map +1 -1
- package/dist/hooks/use-media-query.cjs.map +1 -1
- package/dist/hooks/use-object-url.cjs.map +1 -1
- package/dist/hooks/use-online.cjs.map +1 -1
- package/dist/hooks/use-pagination.cjs.map +1 -1
- package/dist/hooks/use-previous.cjs.map +1 -1
- package/dist/hooks/use-queue.cjs.map +1 -1
- package/dist/hooks/use-resize-observer.cjs.map +1 -1
- package/dist/hooks/use-scroll-overflow.cjs.map +1 -1
- package/dist/hooks/use-service-worker-update.cjs.map +1 -1
- package/dist/hooks/use-set.cjs.map +1 -1
- package/dist/hooks/use-sortable.cjs +1 -1
- package/dist/hooks/use-sortable.cjs.map +1 -1
- package/dist/hooks/use-sortable.js +0 -2
- package/dist/hooks/use-sortable.js.map +1 -1
- package/dist/hooks/use-stable-callback.cjs.map +1 -1
- package/dist/hooks/use-storage-estimate.cjs.map +1 -1
- package/dist/hooks/use-throttle.cjs.map +1 -1
- package/dist/hooks/use-timeout.cjs.map +1 -1
- package/dist/hooks/use-toggle.cjs.map +1 -1
- package/dist/hooks/use-window-size.cjs.map +1 -1
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +7 -5
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/resumable-upload.cjs +1 -1
- package/dist/http/resumable-upload.cjs.map +1 -1
- package/dist/http/resumable-upload.js +1 -1
- package/dist/http/resumable-upload.js.map +1 -1
- package/dist/http/use-poll.cjs.map +1 -1
- package/dist/i18n/I18nProvider.cjs.map +1 -1
- package/dist/icons/Icon.cjs.map +1 -1
- package/dist/icons/IconProvider.cjs.map +1 -1
- package/dist/icons/icon-context.cjs.map +1 -1
- package/dist/icons/use-icon.cjs.map +1 -1
- package/dist/imaging/use-image-processing.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.cjs.map +1 -1
- package/dist/oauth/GoogleSignIn.js.map +1 -1
- package/dist/oauth/use-oauth-callback.cjs.map +1 -1
- package/dist/offline/use-offline-sync.cjs.map +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/query/QueryProvider.cjs.map +1 -1
- package/dist/query/constants.cjs +1 -1
- package/dist/query/constants.cjs.map +1 -1
- package/dist/query/constants.js +10 -10
- package/dist/query/constants.js.map +1 -1
- package/dist/query/create-query-keys.cjs +1 -1
- package/dist/query/create-query-keys.cjs.map +1 -1
- package/dist/query/create-query-keys.js +1 -1
- package/dist/query/create-query-keys.js.map +1 -1
- package/dist/query/offline-persistence.cjs.map +1 -1
- package/dist/query/use-cursor-query.cjs.map +1 -1
- package/dist/query/use-offline-mutation.cjs.map +1 -1
- package/dist/query/use-paginated-query.cjs.map +1 -1
- package/dist/router/AppRouter.cjs +1 -1
- package/dist/router/AppRouter.cjs.map +1 -1
- package/dist/router/AppRouter.js +7 -2
- package/dist/router/AppRouter.js.map +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/store/create-store.cjs.map +1 -1
- package/dist/tabular/use-tabular-predictor.cjs.map +1 -1
- package/dist/telemetry/TelemetryProvider.cjs.map +1 -1
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/color.cjs +1 -1
- package/dist/theme/color.cjs.map +1 -1
- package/dist/theme/color.js +1 -1
- package/dist/theme/color.js.map +1 -1
- package/dist/utils/cn.cjs +1 -1
- package/dist/utils/cn.cjs.map +1 -1
- package/dist/utils/cn.js +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/objects.cjs +1 -1
- package/dist/utils/objects.cjs.map +1 -1
- package/dist/utils/objects.js +1 -1
- package/dist/utils/objects.js.map +1 -1
- package/dist/utils/xlsx.cjs +1 -1
- package/dist/utils/xlsx.cjs.map +1 -1
- package/dist/utils/xlsx.js +3 -3
- package/dist/utils/xlsx.js.map +1 -1
- package/dist/vision/core/canvas.cjs +1 -1
- package/dist/vision/core/canvas.cjs.map +1 -1
- package/dist/vision/core/canvas.js +4 -4
- package/dist/vision/core/canvas.js.map +1 -1
- package/dist/vision/core/exceptions.cjs +1 -1
- package/dist/vision/core/exceptions.cjs.map +1 -1
- package/dist/vision/core/exceptions.js +2 -2
- package/dist/vision/core/exceptions.js.map +1 -1
- package/dist/vision/core/metadata.cjs +2 -2
- package/dist/vision/core/metadata.cjs.map +1 -1
- package/dist/vision/core/metadata.js +11 -8
- package/dist/vision/core/metadata.js.map +1 -1
- package/dist/vision/core/session.cjs +1 -1
- package/dist/vision/core/session.cjs.map +1 -1
- package/dist/vision/core/session.js +8 -5
- package/dist/vision/core/session.js.map +1 -1
- package/dist/vision/fusion.cjs +2 -0
- package/dist/vision/fusion.cjs.map +1 -0
- package/dist/vision/fusion.js +42 -0
- package/dist/vision/fusion.js.map +1 -0
- package/dist/vision/index.cjs +1 -1
- package/dist/vision/index.cjs.map +1 -1
- package/dist/vision/index.js +23 -20
- package/dist/vision/index.js.map +1 -1
- package/dist/vision/io/image.cjs +1 -1
- package/dist/vision/io/image.cjs.map +1 -1
- package/dist/vision/io/image.js +4 -2
- package/dist/vision/io/image.js.map +1 -1
- package/dist/vision/labels.cjs +1 -1
- package/dist/vision/labels.cjs.map +1 -1
- package/dist/vision/labels.js +7 -4
- package/dist/vision/labels.js.map +1 -1
- package/dist/vision/postprocess/detection.cjs +1 -1
- package/dist/vision/postprocess/detection.cjs.map +1 -1
- package/dist/vision/postprocess/detection.js +3 -10
- package/dist/vision/postprocess/detection.js.map +1 -1
- package/dist/vision/postprocess/segmentation.cjs +1 -1
- package/dist/vision/postprocess/segmentation.cjs.map +1 -1
- package/dist/vision/postprocess/segmentation.js +11 -15
- package/dist/vision/postprocess/segmentation.js.map +1 -1
- package/dist/vision/preprocess/image.cjs +1 -1
- package/dist/vision/preprocess/image.cjs.map +1 -1
- package/dist/vision/preprocess/image.js +4 -2
- package/dist/vision/preprocess/image.js.map +1 -1
- package/dist/vision/preprocess/pipeline.cjs +2 -0
- package/dist/vision/preprocess/pipeline.cjs.map +1 -0
- package/dist/vision/preprocess/pipeline.js +61 -0
- package/dist/vision/preprocess/pipeline.js.map +1 -0
- package/dist/vision/results.cjs +1 -1
- package/dist/vision/results.cjs.map +1 -1
- package/dist/vision/results.js +23 -2
- package/dist/vision/results.js.map +1 -1
- package/dist/vision/tasks/base.cjs +1 -1
- package/dist/vision/tasks/base.cjs.map +1 -1
- package/dist/vision/tasks/base.js +11 -2
- package/dist/vision/tasks/base.js.map +1 -1
- package/dist/vision/tasks/classifier.cjs +1 -1
- package/dist/vision/tasks/classifier.cjs.map +1 -1
- package/dist/vision/tasks/classifier.js +2 -2
- package/dist/vision/tasks/classifier.js.map +1 -1
- package/dist/vision/tasks/detectClassify.cjs +2 -0
- package/dist/vision/tasks/detectClassify.cjs.map +1 -0
- package/dist/vision/tasks/detectClassify.js +222 -0
- package/dist/vision/tasks/detectClassify.js.map +1 -0
- package/dist/vision/tasks/detector.cjs +1 -1
- package/dist/vision/tasks/detector.cjs.map +1 -1
- package/dist/vision/tasks/detector.js +54 -29
- package/dist/vision/tasks/detector.js.map +1 -1
- package/dist/vision/tasks/segmenter.cjs +1 -1
- package/dist/vision/tasks/segmenter.cjs.map +1 -1
- package/dist/vision/tasks/segmenter.js +53 -28
- package/dist/vision/tasks/segmenter.js.map +1 -1
- package/dist/vision/types.cjs.map +1 -1
- package/dist/vision/types.js.map +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-live-luminance.cjs.map +1 -1
- package/dist/vision.cjs +1 -1
- package/dist/vision.d.ts +1517 -962
- package/dist/vision.js +25 -22
- package/dist/vite/create-vite-config.cjs +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js +3 -3
- package/dist/vite/create-vite-config.js.map +1 -1
- package/dist/vite/tempest-icons.cjs.map +1 -1
- package/dist/vite/tempest-pwa-dev-sw.cjs.map +1 -1
- package/dist/vite/tempest-pwa-icons.cjs.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 +5 -3
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +2 -2
- package/template/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VirtualTable.js","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.js","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":";;;;;;AA8EA,SAAS,EAAW,GAAmD;CACnE,IAAI,MAAU,SAAS,OAAO,EAAO;CACrC,IAAI,MAAU,UAAU,OAAO,EAAO;AAE1C;AAmCA,SAAgB,EAAgB,EAC5B,SACA,YACA,cACA,WACA,cAAW,GACX,WACA,gBACA,eACA,kBAAe,+BACf,YACA,kBACA,cACA,GAAG,KACkB;CACrB,IAAM,IAAY,EAAuB,IAAI,GACvC,CAAC,GAAW,KAAgB,EAAiB,CAAC,GAC9C,CAAC,GAAU,KAAe,EAAiB,CAAC,GAC5C,CAAC,GAAM,KAAW,EAAqC,KAAe,IAAI;CAYhF,AAVA,QAAgB;EACZ,IAAM,IAAU,EAAU;EAG1B,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,GAEL,QAAgB;EACZ,IAAM,IAAU,EAAU;EACtB,CAAC,KAAW,KAAiB,SACjC,EAAQ,YAAY,KAAK,IAAI,GAAG,KAAK,IAAI,GAAe,EAAK,SAAS,CAAC,CAAC,IAAI;CAChF,GAAG;EAAC;EAAe;EAAW,EAAK;CAAM,CAAC;CAE1C,IAAM,IAAS,QAAmB;EAC9B,IAAI,CAAC,GAAM,OAAO;EAClB,IAAM,IAAS,EAAK,cAAc,QAAQ,IAAI;EAC9C,OAAO,CAAC,GAAG,CAAI,CAAC,CAAC,MAAM,GAAG,MAAM,EAAc,EAAE,EAAK,MAAM,EAAE,EAAK,IAAI,IAAI,CAAM;CACpF,GAAG,CAAC,GAAM,CAAI,CAAC,GAET,IAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,IAAY,CAAS,IAAI,CAAQ,GAChE,IAAa,KAAK,KAAK,IAAW,CAAS,IAAI,IAAW,GAC1D,IAAM,KAAK,IAAI,EAAO,QAAQ,IAAQ,CAAU,GAChD,IAAQ,EAAO,MAAM,GAAO,CAAG,GAW/B,IAAa,GAAa,MAAuB;EACnD,GAAS,MACD,CAAC,KAAW,EAAQ,QAAQ,IAAY;GAAE;GAAK,WAAW;EAAM,IAChE,EAAQ,cAAc,QAAc;GAAE;GAAK,WAAW;EAAO,IAC1D,IACV;EACD,IAAM,IAAU,EAAU;EAE1B,AADI,MAAS,EAAQ,YAAY,IACjC,EAAa,CAAC;CAClB,GAAG,CAAC,CAAC;CAEL,OACI,kBAAC,OAAD;EACI,KAAK;EACL,WAAW,EAAG,EAAO,QAAQ,CAAS;EACtC,OAAO,EAAE,UAAO;EAChB,WAAW,MAAU,EAAa,EAAM,cAAc,SAAS;EAC/D,GAAI;EAEJ,UAAA,kBAAC,SAAD;GAAO,WAAW,EAAO;GAAO,iBAAe,EAAO;GAAtD,UAAA;IACK,IAAU,kBAAC,WAAD;KAAS,WAAW,EAAO;KAAU,UAAA;IAAiB,CAAA,IAAI;IACrE,kBAAC,SAAD;KAAO,WAAW,EAAO;KACrB,UAAA,kBAAC,MAAD,EAAA,UACK,EAAQ,KAAK,MAAW;MACrB,IAAM,IAAW,GAAM,QAAQ,EAAO;MACtC,OACI,kBAAC,MAAD;OAEI,OAAM;OACN,WAAW,EAAG,EAAO,IAAI,EAAW,EAAO,KAAK,CAAC;OACjD,OAAO,EAAE,OAAO,EAAO,MAAM;OAC7B,aACI,IACM,GAAM,cAAc,QAChB,cACA,eACJ,EAAO,WACL,SACA,KAAA;OAGX,UAAA,EAAO,WACJ,kBAAC,UAAD;QACI,MAAK;QACL,WAAW,EAAO;QAClB,eAAe,EAAW,EAAO,GAAG;QAHxC,UAAA,CAKK,EAAO,QACR,kBAAC,QAAD;SAAM,WAAW,EAAO;SAAe,eAAA;SAClC,UAAA,IACK,GAAM,cAAc,QAChB,MACA,MACJ;QACJ,CAAA,CACF;OAER,CAAA,IAAA,EAAO;MAEX,GAhCK,OAAO,EAAO,GAAG,CAgCtB;KAEZ,CAAC,EACD,CAAA;IACD,CAAA;IACP,kBAAC,SAAD,EAAA,UACK,EAAO,WAAW,IACf,kBAAC,MAAD,EAAA,UACI,kBAAC,MAAD;KAAI,WAAW,EAAO;KAAU,SAAS,EAAQ;KAC5C,UAAA;IACD,CAAA,EACJ,CAAA,IAEJ,kBAAA,GAAA,EAAA,UAAA;KACK,IAAQ,KAAK,kBAAC,MAAD;MAAI,eAAA;MAAY,OAAO,EAAE,QAAQ,IAAQ,EAAU;KAAI,CAAA;KACpE,EAAM,KAAK,GAAK,MAAW;MACxB,IAAM,IAAQ,IAAQ;MACtB,OACI,kBAAC,MAAD;OAEI,iBAAe,IAAQ;OACvB,WAAW,EAAG,EAAO,IAAI,KAAc,EAAO,SAAS;OACvD,OAAO,EAAE,QAAQ,EAAU;OAC3B,UAAU,IAAa,IAAI,KAAA;OAC3B,SACI,UAAmB,EAAW,GAAK,CAAK,IAAI,KAAA;OAEhD,WACI,KACO,MAAU;QACP,CACI,EAAM,QAAQ,WACd,EAAM,QAAQ,SAEd,EAAM,eAAe,GACrB,EAAW,GAAK,CAAK;OAE7B,IACA,KAAA;OAGT,UAAA,EAAQ,KAAK,MACV,kBAAC,MAAD;QAEI,WAAW,EAAG,EAAO,IAAI,EAAW,EAAO,KAAK,CAAC;QACjD,OAAO,EAAE,OAAO,EAAO,MAAM;QAE5B,UAAA,EAAO,SACF,EAAO,OAAO,GAAK,CAAK,IACtB,EAAI,EAAO,QAAsB;OACzC,GAPK,OAAO,EAAO,GAAG,CAOtB,CACP;MACD,GAjCK,IAAS,EAAO,GAAK,CAAK,IAAI,CAiCnC;KAEZ,CAAC;KACA,IAAM,EAAO,UACV,kBAAC,MAAD;MACI,eAAA;MACA,OAAO,EAAE,SAAS,EAAO,SAAS,KAAO,EAAU;KACtD,CAAA;IAEP,EAAA,CAAA,EAEH,CAAA;GACJ;;CACN,CAAA;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisuallyHidden.cjs","names":[],"sources":["../../../src/components/VisuallyHidden/VisuallyHidden.tsx"],"sourcesContent":["import type { HTMLAttributes, JSX } from \"react\";\nimport { createElement } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VisuallyHidden.module.css\";\n\nexport interface VisuallyHiddenProps extends HTMLAttributes<HTMLElement> {\n /** Intrinsic element to render. Defaults to \"span\". */\n as?: keyof JSX.IntrinsicElements;\n}\n\n/**\n * Render content that is hidden visually but remains available to screen\n * readers — the standard \"sr-only\" pattern. Useful for accessible labels on\n * icon-only controls.\n *\n * @example\n * <button><Icon /><VisuallyHidden>Close</VisuallyHidden></button>\n */\nexport function VisuallyHidden({\n as = \"span\",\n className,\n children,\n ...props\n}: VisuallyHiddenProps) {\n return createElement(as, { className: cn(styles.hidden, className), ...props }, children);\n}\n"],"mappings":"sGAkBA,SAAgB,EAAe,CAC3B,KAAK,OACL,YACA,WACA,GAAG,GACiB,CACpB,OAAA,
|
|
1
|
+
{"version":3,"file":"VisuallyHidden.cjs","names":[],"sources":["../../../src/components/VisuallyHidden/VisuallyHidden.tsx"],"sourcesContent":["import type { HTMLAttributes, JSX } from \"react\";\nimport { createElement } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./VisuallyHidden.module.css\";\n\nexport interface VisuallyHiddenProps extends HTMLAttributes<HTMLElement> {\n /** Intrinsic element to render. Defaults to \"span\". */\n as?: keyof JSX.IntrinsicElements;\n}\n\n/**\n * Render content that is hidden visually but remains available to screen\n * readers — the standard \"sr-only\" pattern. Useful for accessible labels on\n * icon-only controls.\n *\n * @example\n * <button><Icon /><VisuallyHidden>Close</VisuallyHidden></button>\n */\nexport function VisuallyHidden({\n as = \"span\",\n className,\n children,\n ...props\n}: VisuallyHiddenProps) {\n return createElement(as, { className: cn(styles.hidden, className), ...props }, children);\n}\n"],"mappings":"sGAkBA,SAAgB,EAAe,CAC3B,KAAK,OACL,YACA,WACA,GAAG,GACiB,CACpB,OAAA,EAAO,EAAA,cAAA,CAAc,EAAI,CAAE,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EAAG,GAAG,CAAM,EAAG,CAAQ,CAC5F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Wizard.cjs","names":[],"sources":["../../../src/components/Wizard/Wizard.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — steps plus the\n * controlled-or-not pair (activeIndex, defaultActiveIndex, onStepChange,\n * onComplete), the four strings it renders (nextLabel, backLabel, finishLabel,\n * optionalLabel) and the two behaviours (clickableSteps, renderActions). The labels\n * are props because a wizard's buttons are the most translated text in an app.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport { Stepper } from \"../Stepper\";\nimport styles from \"./Wizard.module.css\";\n\n/** One step of the flow. */\nexport interface WizardStep {\n /** Stable identifier. */\n id: string;\n /** Step label shown in the indicator. */\n label: string;\n /** Optional description under the label. */\n description?: string;\n /** Step body. A function receives the flow controls, for a \"skip\" link inside the form. */\n content: ReactNode | ((controls: WizardControls) => ReactNode);\n /**\n * Gate for leaving this step forward. Return `false` (or a rejected/`false`\n * promise) to keep the user here — typically `() => form.trigger()`.\n * Async is supported: the Next button shows a pending state while it runs.\n */\n validate?: () => boolean | Promise<boolean>;\n /** Marks the step as optional, so `onComplete` can ignore it. */\n optional?: boolean;\n}\n\n/** Flow controls handed to a step body and to `renderActions`. */\nexport interface WizardControls {\n /** Zero-based index of the current step. */\n activeIndex: number;\n /** The current step. */\n step: WizardStep;\n /** `true` while a `validate` promise is pending. */\n validating: boolean;\n isFirst: boolean;\n isLast: boolean;\n /** Run the current step's `validate` and advance when it passes. */\n next: () => Promise<void>;\n /** Go back one step. No validation — going back never blocks. */\n back: () => void;\n /** Jump to an index. Forward jumps validate every step in between. */\n goTo: (index: number) => Promise<void>;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Controlled active index. */\n activeIndex?: number;\n /** Uncontrolled initial index. Default `0`. */\n defaultActiveIndex?: number;\n onStepChange?: (index: number, step: WizardStep) => void;\n /** Called when the last step passes validation. */\n onComplete?: () => void | Promise<void>;\n /** Label of the advance button. Default `\"Next\"`. */\n nextLabel?: string;\n /** Label of the back button. Default `\"Back\"`. */\n backLabel?: string;\n /** Label of the button on the last step. Default `\"Finish\"`. */\n finishLabel?: string;\n /**\n * Suffix appended to an optional step's description in the indicator.\n * Default `\"(optional)\"` — override it to localize, since the SDK ships no\n * translation for component-internal copy.\n */\n optionalLabel?: string;\n /**\n * Allow clicking the indicator to jump. Default `false` — a wizard exists\n * because order matters, and a free jump skips the gates.\n */\n clickableSteps?: boolean;\n /** Replace the default button row. */\n renderActions?: (controls: WizardControls) => ReactNode;\n className?: string;\n}\n\n/**\n * Multi-step flow: step indicator, one body at a time, and navigation that\n * respects per-step validation.\n *\n * `Stepper` draws the indicator; this owns the part every app was rewriting — the\n * active index, the async gate before advancing, the disabled/pending buttons and\n * the completion call.\n *\n * Only the active step's body is mounted. Uncommitted input in a step you leave is\n * therefore lost unless the state lives outside (react-hook-form's `FormProvider`,\n * a store, a parent `useState`) — which is the right place for it anyway, since\n * the last step usually needs to submit everything at once.\n *\n * @example\n * ```tsx\n * const form = useZodForm(schema);\n *\n * <FormProvider {...form}>\n * <Wizard\n * steps={[\n * {\n * id: \"dados\",\n * label: \"Dados\",\n * validate: () => form.trigger([\"nome\", \"email\"]),\n * content: (\n * <>\n * <FormField name=\"nome\" label=\"Nome\"><Input /></FormField>\n * <FormField name=\"email\" label=\"E-mail\"><Input type=\"email\" /></FormField>\n * </>\n * ),\n * },\n * { id: \"revisao\", label: \"Revisão\", content: <Review /> },\n * ]}\n * onComplete={form.handleSubmit(onSubmit)}\n * />\n * </FormProvider>\n * ```\n */\nexport function Wizard({\n steps,\n activeIndex,\n defaultActiveIndex = 0,\n onStepChange,\n onComplete,\n nextLabel = \"Next\",\n backLabel = \"Back\",\n finishLabel = \"Finish\",\n optionalLabel = \"(optional)\",\n clickableSteps = false,\n renderActions,\n className,\n}: WizardProps) {\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [validating, setValidating] = useState(false);\n\n const current = Math.min(isControlled ? activeIndex : internalIndex, steps.length - 1);\n const step = steps[current];\n\n const moveTo = useCallback(\n (index: number): void => {\n if (!isControlled) setInternalIndex(index);\n onStepChange?.(index, steps[index]);\n },\n [isControlled, onStepChange, steps],\n );\n\n /**\n * Run a step's gate. A gate that throws counts as \"not allowed\": a `validate`\n * wired to a network check should not strand the user on a half-advanced flow\n * when the request fails.\n */\n const runValidate = useCallback(async (candidate: WizardStep): Promise<boolean> => {\n if (!candidate.validate) return true;\n setValidating(true);\n try {\n return await candidate.validate();\n } catch {\n return false;\n } finally {\n setValidating(false);\n }\n }, []);\n\n const next = useCallback(async (): Promise<void> => {\n if (!(await runValidate(step))) return;\n if (current === steps.length - 1) {\n await onComplete?.();\n return;\n }\n moveTo(current + 1);\n }, [current, moveTo, onComplete, runValidate, step, steps.length]);\n\n const back = useCallback((): void => {\n if (current > 0) moveTo(current - 1);\n }, [current, moveTo]);\n\n const goTo = useCallback(\n async (index: number): Promise<void> => {\n const target = Math.max(0, Math.min(index, steps.length - 1));\n if (target === current) return;\n if (target < current) {\n moveTo(target);\n return;\n }\n for (let i = current; i < target; i += 1) {\n if (!(await runValidate(steps[i]))) return;\n }\n moveTo(target);\n },\n [current, moveTo, runValidate, steps],\n );\n\n const controls = useMemo<WizardControls>(\n () => ({\n activeIndex: current,\n step,\n validating,\n isFirst: current === 0,\n isLast: current === steps.length - 1,\n next,\n back,\n goTo,\n }),\n [back, current, goTo, next, step, steps.length, validating],\n );\n\n const indicatorSteps = useMemo(\n () =>\n steps.map((item) => ({\n label: item.label,\n description: item.optional\n ? `${item.description ?? \"\"} ${optionalLabel}`.trim()\n : item.description,\n })),\n [optionalLabel, steps],\n );\n\n return (\n <div className={cn(styles.wizard, className)}>\n <Stepper\n steps={indicatorSteps}\n current={current}\n onStepClick={clickableSteps ? (index) => void goTo(index) : undefined}\n />\n\n <div className={styles.body} role=\"group\" aria-label={step.label}>\n {typeof step.content === \"function\" ? step.content(controls) : step.content}\n </div>\n\n {renderActions ? (\n renderActions(controls)\n ) : (\n <div className={styles.actions}>\n <Button\n variant=\"secondary\"\n onClick={back}\n disabled={controls.isFirst || validating}\n >\n {backLabel}\n </Button>\n <Button onClick={() => void next()} loading={validating}>\n {controls.isLast ? finishLabel : nextLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Wizard.cjs","names":[],"sources":["../../../src/components/Wizard/Wizard.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — steps plus the\n * controlled-or-not pair (activeIndex, defaultActiveIndex, onStepChange,\n * onComplete), the four strings it renders (nextLabel, backLabel, finishLabel,\n * optionalLabel) and the two behaviours (clickableSteps, renderActions). The labels\n * are props because a wizard's buttons are the most translated text in an app.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport { Stepper } from \"../Stepper\";\nimport styles from \"./Wizard.module.css\";\n\n/** One step of the flow. */\nexport interface WizardStep {\n /** Stable identifier. */\n id: string;\n /** Step label shown in the indicator. */\n label: string;\n /** Optional description under the label. */\n description?: string;\n /** Step body. A function receives the flow controls, for a \"skip\" link inside the form. */\n content: ReactNode | ((controls: WizardControls) => ReactNode);\n /**\n * Gate for leaving this step forward. Return `false` (or a rejected/`false`\n * promise) to keep the user here — typically `() => form.trigger()`.\n * Async is supported: the Next button shows a pending state while it runs.\n */\n validate?: () => boolean | Promise<boolean>;\n /** Marks the step as optional, so `onComplete` can ignore it. */\n optional?: boolean;\n}\n\n/** Flow controls handed to a step body and to `renderActions`. */\nexport interface WizardControls {\n /** Zero-based index of the current step. */\n activeIndex: number;\n /** The current step. */\n step: WizardStep;\n /** `true` while a `validate` promise is pending. */\n validating: boolean;\n isFirst: boolean;\n isLast: boolean;\n /** Run the current step's `validate` and advance when it passes. */\n next: () => Promise<void>;\n /** Go back one step. No validation — going back never blocks. */\n back: () => void;\n /** Jump to an index. Forward jumps validate every step in between. */\n goTo: (index: number) => Promise<void>;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Controlled active index. */\n activeIndex?: number;\n /** Uncontrolled initial index. Default `0`. */\n defaultActiveIndex?: number;\n onStepChange?: (index: number, step: WizardStep) => void;\n /** Called when the last step passes validation. */\n onComplete?: () => void | Promise<void>;\n /** Label of the advance button. Default `\"Next\"`. */\n nextLabel?: string;\n /** Label of the back button. Default `\"Back\"`. */\n backLabel?: string;\n /** Label of the button on the last step. Default `\"Finish\"`. */\n finishLabel?: string;\n /**\n * Suffix appended to an optional step's description in the indicator.\n * Default `\"(optional)\"` — override it to localize, since the SDK ships no\n * translation for component-internal copy.\n */\n optionalLabel?: string;\n /**\n * Allow clicking the indicator to jump. Default `false` — a wizard exists\n * because order matters, and a free jump skips the gates.\n */\n clickableSteps?: boolean;\n /** Replace the default button row. */\n renderActions?: (controls: WizardControls) => ReactNode;\n className?: string;\n}\n\n/**\n * Multi-step flow: step indicator, one body at a time, and navigation that\n * respects per-step validation.\n *\n * `Stepper` draws the indicator; this owns the part every app was rewriting — the\n * active index, the async gate before advancing, the disabled/pending buttons and\n * the completion call.\n *\n * Only the active step's body is mounted. Uncommitted input in a step you leave is\n * therefore lost unless the state lives outside (react-hook-form's `FormProvider`,\n * a store, a parent `useState`) — which is the right place for it anyway, since\n * the last step usually needs to submit everything at once.\n *\n * @example\n * ```tsx\n * const form = useZodForm(schema);\n *\n * <FormProvider {...form}>\n * <Wizard\n * steps={[\n * {\n * id: \"dados\",\n * label: \"Dados\",\n * validate: () => form.trigger([\"nome\", \"email\"]),\n * content: (\n * <>\n * <FormField name=\"nome\" label=\"Nome\"><Input /></FormField>\n * <FormField name=\"email\" label=\"E-mail\"><Input type=\"email\" /></FormField>\n * </>\n * ),\n * },\n * { id: \"revisao\", label: \"Revisão\", content: <Review /> },\n * ]}\n * onComplete={form.handleSubmit(onSubmit)}\n * />\n * </FormProvider>\n * ```\n */\nexport function Wizard({\n steps,\n activeIndex,\n defaultActiveIndex = 0,\n onStepChange,\n onComplete,\n nextLabel = \"Next\",\n backLabel = \"Back\",\n finishLabel = \"Finish\",\n optionalLabel = \"(optional)\",\n clickableSteps = false,\n renderActions,\n className,\n}: WizardProps) {\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [validating, setValidating] = useState(false);\n\n const current = Math.min(isControlled ? activeIndex : internalIndex, steps.length - 1);\n const step = steps[current];\n\n const moveTo = useCallback(\n (index: number): void => {\n if (!isControlled) setInternalIndex(index);\n onStepChange?.(index, steps[index]);\n },\n [isControlled, onStepChange, steps],\n );\n\n /**\n * Run a step's gate. A gate that throws counts as \"not allowed\": a `validate`\n * wired to a network check should not strand the user on a half-advanced flow\n * when the request fails.\n */\n const runValidate = useCallback(async (candidate: WizardStep): Promise<boolean> => {\n if (!candidate.validate) return true;\n setValidating(true);\n try {\n return await candidate.validate();\n } catch {\n return false;\n } finally {\n setValidating(false);\n }\n }, []);\n\n const next = useCallback(async (): Promise<void> => {\n if (!(await runValidate(step))) return;\n if (current === steps.length - 1) {\n await onComplete?.();\n return;\n }\n moveTo(current + 1);\n }, [current, moveTo, onComplete, runValidate, step, steps.length]);\n\n const back = useCallback((): void => {\n if (current > 0) moveTo(current - 1);\n }, [current, moveTo]);\n\n const goTo = useCallback(\n async (index: number): Promise<void> => {\n const target = Math.max(0, Math.min(index, steps.length - 1));\n if (target === current) return;\n if (target < current) {\n moveTo(target);\n return;\n }\n for (let i = current; i < target; i += 1) {\n if (!(await runValidate(steps[i]))) return;\n }\n moveTo(target);\n },\n [current, moveTo, runValidate, steps],\n );\n\n const controls = useMemo<WizardControls>(\n () => ({\n activeIndex: current,\n step,\n validating,\n isFirst: current === 0,\n isLast: current === steps.length - 1,\n next,\n back,\n goTo,\n }),\n [back, current, goTo, next, step, steps.length, validating],\n );\n\n const indicatorSteps = useMemo(\n () =>\n steps.map((item) => ({\n label: item.label,\n description: item.optional\n ? `${item.description ?? \"\"} ${optionalLabel}`.trim()\n : item.description,\n })),\n [optionalLabel, steps],\n );\n\n return (\n <div className={cn(styles.wizard, className)}>\n <Stepper\n steps={indicatorSteps}\n current={current}\n onStepClick={clickableSteps ? (index) => void goTo(index) : undefined}\n />\n\n <div className={styles.body} role=\"group\" aria-label={step.label}>\n {typeof step.content === \"function\" ? step.content(controls) : step.content}\n </div>\n\n {renderActions ? (\n renderActions(controls)\n ) : (\n <div className={styles.actions}>\n <Button\n variant=\"secondary\"\n onClick={back}\n disabled={controls.isFirst || validating}\n >\n {backLabel}\n </Button>\n <Button onClick={() => void next()} loading={validating}>\n {controls.isLast ? finishLabel : nextLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Wizard.js","names":[],"sources":["../../../src/components/Wizard/Wizard.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — steps plus the\n * controlled-or-not pair (activeIndex, defaultActiveIndex, onStepChange,\n * onComplete), the four strings it renders (nextLabel, backLabel, finishLabel,\n * optionalLabel) and the two behaviours (clickableSteps, renderActions). The labels\n * are props because a wizard's buttons are the most translated text in an app.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport { Stepper } from \"../Stepper\";\nimport styles from \"./Wizard.module.css\";\n\n/** One step of the flow. */\nexport interface WizardStep {\n /** Stable identifier. */\n id: string;\n /** Step label shown in the indicator. */\n label: string;\n /** Optional description under the label. */\n description?: string;\n /** Step body. A function receives the flow controls, for a \"skip\" link inside the form. */\n content: ReactNode | ((controls: WizardControls) => ReactNode);\n /**\n * Gate for leaving this step forward. Return `false` (or a rejected/`false`\n * promise) to keep the user here — typically `() => form.trigger()`.\n * Async is supported: the Next button shows a pending state while it runs.\n */\n validate?: () => boolean | Promise<boolean>;\n /** Marks the step as optional, so `onComplete` can ignore it. */\n optional?: boolean;\n}\n\n/** Flow controls handed to a step body and to `renderActions`. */\nexport interface WizardControls {\n /** Zero-based index of the current step. */\n activeIndex: number;\n /** The current step. */\n step: WizardStep;\n /** `true` while a `validate` promise is pending. */\n validating: boolean;\n isFirst: boolean;\n isLast: boolean;\n /** Run the current step's `validate` and advance when it passes. */\n next: () => Promise<void>;\n /** Go back one step. No validation — going back never blocks. */\n back: () => void;\n /** Jump to an index. Forward jumps validate every step in between. */\n goTo: (index: number) => Promise<void>;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Controlled active index. */\n activeIndex?: number;\n /** Uncontrolled initial index. Default `0`. */\n defaultActiveIndex?: number;\n onStepChange?: (index: number, step: WizardStep) => void;\n /** Called when the last step passes validation. */\n onComplete?: () => void | Promise<void>;\n /** Label of the advance button. Default `\"Next\"`. */\n nextLabel?: string;\n /** Label of the back button. Default `\"Back\"`. */\n backLabel?: string;\n /** Label of the button on the last step. Default `\"Finish\"`. */\n finishLabel?: string;\n /**\n * Suffix appended to an optional step's description in the indicator.\n * Default `\"(optional)\"` — override it to localize, since the SDK ships no\n * translation for component-internal copy.\n */\n optionalLabel?: string;\n /**\n * Allow clicking the indicator to jump. Default `false` — a wizard exists\n * because order matters, and a free jump skips the gates.\n */\n clickableSteps?: boolean;\n /** Replace the default button row. */\n renderActions?: (controls: WizardControls) => ReactNode;\n className?: string;\n}\n\n/**\n * Multi-step flow: step indicator, one body at a time, and navigation that\n * respects per-step validation.\n *\n * `Stepper` draws the indicator; this owns the part every app was rewriting — the\n * active index, the async gate before advancing, the disabled/pending buttons and\n * the completion call.\n *\n * Only the active step's body is mounted. Uncommitted input in a step you leave is\n * therefore lost unless the state lives outside (react-hook-form's `FormProvider`,\n * a store, a parent `useState`) — which is the right place for it anyway, since\n * the last step usually needs to submit everything at once.\n *\n * @example\n * ```tsx\n * const form = useZodForm(schema);\n *\n * <FormProvider {...form}>\n * <Wizard\n * steps={[\n * {\n * id: \"dados\",\n * label: \"Dados\",\n * validate: () => form.trigger([\"nome\", \"email\"]),\n * content: (\n * <>\n * <FormField name=\"nome\" label=\"Nome\"><Input /></FormField>\n * <FormField name=\"email\" label=\"E-mail\"><Input type=\"email\" /></FormField>\n * </>\n * ),\n * },\n * { id: \"revisao\", label: \"Revisão\", content: <Review /> },\n * ]}\n * onComplete={form.handleSubmit(onSubmit)}\n * />\n * </FormProvider>\n * ```\n */\nexport function Wizard({\n steps,\n activeIndex,\n defaultActiveIndex = 0,\n onStepChange,\n onComplete,\n nextLabel = \"Next\",\n backLabel = \"Back\",\n finishLabel = \"Finish\",\n optionalLabel = \"(optional)\",\n clickableSteps = false,\n renderActions,\n className,\n}: WizardProps) {\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [validating, setValidating] = useState(false);\n\n const current = Math.min(isControlled ? activeIndex : internalIndex, steps.length - 1);\n const step = steps[current];\n\n const moveTo = useCallback(\n (index: number): void => {\n if (!isControlled) setInternalIndex(index);\n onStepChange?.(index, steps[index]);\n },\n [isControlled, onStepChange, steps],\n );\n\n /**\n * Run a step's gate. A gate that throws counts as \"not allowed\": a `validate`\n * wired to a network check should not strand the user on a half-advanced flow\n * when the request fails.\n */\n const runValidate = useCallback(async (candidate: WizardStep): Promise<boolean> => {\n if (!candidate.validate) return true;\n setValidating(true);\n try {\n return await candidate.validate();\n } catch {\n return false;\n } finally {\n setValidating(false);\n }\n }, []);\n\n const next = useCallback(async (): Promise<void> => {\n if (!(await runValidate(step))) return;\n if (current === steps.length - 1) {\n await onComplete?.();\n return;\n }\n moveTo(current + 1);\n }, [current, moveTo, onComplete, runValidate, step, steps.length]);\n\n const back = useCallback((): void => {\n if (current > 0) moveTo(current - 1);\n }, [current, moveTo]);\n\n const goTo = useCallback(\n async (index: number): Promise<void> => {\n const target = Math.max(0, Math.min(index, steps.length - 1));\n if (target === current) return;\n if (target < current) {\n moveTo(target);\n return;\n }\n for (let i = current; i < target; i += 1) {\n if (!(await runValidate(steps[i]))) return;\n }\n moveTo(target);\n },\n [current, moveTo, runValidate, steps],\n );\n\n const controls = useMemo<WizardControls>(\n () => ({\n activeIndex: current,\n step,\n validating,\n isFirst: current === 0,\n isLast: current === steps.length - 1,\n next,\n back,\n goTo,\n }),\n [back, current, goTo, next, step, steps.length, validating],\n );\n\n const indicatorSteps = useMemo(\n () =>\n steps.map((item) => ({\n label: item.label,\n description: item.optional\n ? `${item.description ?? \"\"} ${optionalLabel}`.trim()\n : item.description,\n })),\n [optionalLabel, steps],\n );\n\n return (\n <div className={cn(styles.wizard, className)}>\n <Stepper\n steps={indicatorSteps}\n current={current}\n onStepClick={clickableSteps ? (index) => void goTo(index) : undefined}\n />\n\n <div className={styles.body} role=\"group\" aria-label={step.label}>\n {typeof step.content === \"function\" ? step.content(controls) : step.content}\n </div>\n\n {renderActions ? (\n renderActions(controls)\n ) : (\n <div className={styles.actions}>\n <Button\n variant=\"secondary\"\n onClick={back}\n disabled={controls.isFirst || validating}\n >\n {backLabel}\n </Button>\n <Button onClick={() => void next()} loading={validating}>\n {controls.isLast ? finishLabel : nextLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAyHA,SAAgB,EAAO,EACnB,UACA,gBACA,wBAAqB,GACrB,iBACA,eACA,eAAY,QACZ,eAAY,QACZ,iBAAc,UACd,mBAAgB,cAChB,oBAAiB,IACjB,kBACA,gBACY;CACZ,IAAM,IAAe,MAAgB,KAAA,GAC/B,CAAC,GAAe,KAAoB,EAAS,CAAkB,GAC/D,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,IAAU,KAAK,IAAI,IAAe,IAAc,GAAe,EAAM,SAAS,CAAC,GAC/E,IAAO,EAAM,IAEb,IAAS,GACV,MAAwB;EAErB,AADK,KAAc,EAAiB,CAAK,GACzC,IAAe,GAAO,EAAM,EAAM;CACtC,GACA;EAAC;EAAc;EAAc;CAAK,CACtC,GAOM,IAAc,EAAY,OAAO,MAA4C;EAC/E,IAAI,CAAC,EAAU,UAAU,OAAO;EAChC,EAAc,EAAI;EAClB,IAAI;GACA,OAAO,MAAM,EAAU,SAAS;EACpC,QAAQ;GACJ,OAAO;EACX,UAAU;GACN,EAAc,EAAK;EACvB;CACJ,GAAG,CAAC,CAAC,GAEC,IAAO,EAAY,YAA2B;EAC1C,UAAM,EAAY,CAAI,GAC5B;OAAI,MAAY,EAAM,SAAS,GAAG;IAC9B,MAAM,IAAa;IACnB;GACJ;GACA,EAAO,IAAU,CAAC;EADlB;CAEJ,GAAG;EAAC;EAAS;EAAQ;EAAY;EAAa;EAAM,EAAM;CAAM,CAAC,GAE3D,IAAO,QAAwB;EACjC,AAAI,IAAU,KAAG,EAAO,IAAU,CAAC;CACvC,GAAG,CAAC,GAAS,CAAM,CAAC,GAEd,IAAO,EACT,OAAO,MAAiC;EACpC,IAAM,IAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAO,EAAM,SAAS,CAAC,CAAC;EACxD,UAAW,GACf;OAAI,IAAS,GAAS;IAClB,EAAO,CAAM;IACb;GACJ;GACA,KAAK,IAAI,IAAI,GAAS,IAAI,GAAQ,KAAK,GACnC,IAAI,CAAE,MAAM,EAAY,EAAM,EAAE,GAAI;GAExC,EAAO,CAAM;EAJb;CAKJ,GACA;EAAC;EAAS;EAAQ;EAAa;CAAK,CACxC,GAEM,IAAW,SACN;EACH,aAAa;EACb;EACA;EACA,SAAS,MAAY;EACrB,QAAQ,MAAY,EAAM,SAAS;EACnC;EACA;EACA;CACJ,IACA;EAAC;EAAM;EAAS;EAAM;EAAM;EAAM,EAAM;EAAQ;CAAU,CAC9D,GAEM,IAAiB,QAEf,EAAM,KAAK,OAAU;EACjB,OAAO,EAAK;EACZ,aAAa,EAAK,WACZ,GAAG,EAAK,eAAe,GAAG,GAAG,IAAgB,KAAK,IAClD,EAAK;CACf,EAAE,GACN,CAAC,GAAe,CAAK,CACzB;CAEA,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,QAAQ,CAAS;YAA3C;GACI,kBAAC,GAAD;IACI,OAAO;IACE;IACT,aAAa,KAAkB,MAAU,KAAK,EAAK,CAAK,IAAI,KAAA;GAC/D,CAAA;GAED,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAM,MAAK;IAAQ,cAAY,EAAK;cACtD,OAAO,EAAK,WAAY,aAAa,EAAK,QAAQ,CAAQ,IAAI,EAAK;GACnE,CAAA;GAEJ,IACG,EAAc,CAAQ,IAEtB,kBAAC,OAAD;IAAK,WAAW,EAAO;cAAvB,CACI,kBAAC,GAAD;KACI,SAAQ;KACR,SAAS;KACT,UAAU,EAAS,WAAW;eAE7B;IACG,CAAA,GACR,kBAAC,GAAD;KAAQ,eAAe,KAAK,EAAK;KAAG,SAAS;eACxC,EAAS,SAAS,IAAc;IAC7B,CAAA,CACP;;EAER;;AAEb"}
|
|
1
|
+
{"version":3,"file":"Wizard.js","names":[],"sources":["../../../src/components/Wizard/Wizard.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, props-count, function-lines — steps plus the\n * controlled-or-not pair (activeIndex, defaultActiveIndex, onStepChange,\n * onComplete), the four strings it renders (nextLabel, backLabel, finishLabel,\n * optionalLabel) and the two behaviours (clickableSteps, renderActions). The labels\n * are props because a wizard's buttons are the most translated text in an app.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"../Button\";\nimport { Stepper } from \"../Stepper\";\nimport styles from \"./Wizard.module.css\";\n\n/** One step of the flow. */\nexport interface WizardStep {\n /** Stable identifier. */\n id: string;\n /** Step label shown in the indicator. */\n label: string;\n /** Optional description under the label. */\n description?: string;\n /** Step body. A function receives the flow controls, for a \"skip\" link inside the form. */\n content: ReactNode | ((controls: WizardControls) => ReactNode);\n /**\n * Gate for leaving this step forward. Return `false` (or a rejected/`false`\n * promise) to keep the user here — typically `() => form.trigger()`.\n * Async is supported: the Next button shows a pending state while it runs.\n */\n validate?: () => boolean | Promise<boolean>;\n /** Marks the step as optional, so `onComplete` can ignore it. */\n optional?: boolean;\n}\n\n/** Flow controls handed to a step body and to `renderActions`. */\nexport interface WizardControls {\n /** Zero-based index of the current step. */\n activeIndex: number;\n /** The current step. */\n step: WizardStep;\n /** `true` while a `validate` promise is pending. */\n validating: boolean;\n isFirst: boolean;\n isLast: boolean;\n /** Run the current step's `validate` and advance when it passes. */\n next: () => Promise<void>;\n /** Go back one step. No validation — going back never blocks. */\n back: () => void;\n /** Jump to an index. Forward jumps validate every step in between. */\n goTo: (index: number) => Promise<void>;\n}\n\nexport interface WizardProps {\n steps: WizardStep[];\n /** Controlled active index. */\n activeIndex?: number;\n /** Uncontrolled initial index. Default `0`. */\n defaultActiveIndex?: number;\n onStepChange?: (index: number, step: WizardStep) => void;\n /** Called when the last step passes validation. */\n onComplete?: () => void | Promise<void>;\n /** Label of the advance button. Default `\"Next\"`. */\n nextLabel?: string;\n /** Label of the back button. Default `\"Back\"`. */\n backLabel?: string;\n /** Label of the button on the last step. Default `\"Finish\"`. */\n finishLabel?: string;\n /**\n * Suffix appended to an optional step's description in the indicator.\n * Default `\"(optional)\"` — override it to localize, since the SDK ships no\n * translation for component-internal copy.\n */\n optionalLabel?: string;\n /**\n * Allow clicking the indicator to jump. Default `false` — a wizard exists\n * because order matters, and a free jump skips the gates.\n */\n clickableSteps?: boolean;\n /** Replace the default button row. */\n renderActions?: (controls: WizardControls) => ReactNode;\n className?: string;\n}\n\n/**\n * Multi-step flow: step indicator, one body at a time, and navigation that\n * respects per-step validation.\n *\n * `Stepper` draws the indicator; this owns the part every app was rewriting — the\n * active index, the async gate before advancing, the disabled/pending buttons and\n * the completion call.\n *\n * Only the active step's body is mounted. Uncommitted input in a step you leave is\n * therefore lost unless the state lives outside (react-hook-form's `FormProvider`,\n * a store, a parent `useState`) — which is the right place for it anyway, since\n * the last step usually needs to submit everything at once.\n *\n * @example\n * ```tsx\n * const form = useZodForm(schema);\n *\n * <FormProvider {...form}>\n * <Wizard\n * steps={[\n * {\n * id: \"dados\",\n * label: \"Dados\",\n * validate: () => form.trigger([\"nome\", \"email\"]),\n * content: (\n * <>\n * <FormField name=\"nome\" label=\"Nome\"><Input /></FormField>\n * <FormField name=\"email\" label=\"E-mail\"><Input type=\"email\" /></FormField>\n * </>\n * ),\n * },\n * { id: \"revisao\", label: \"Revisão\", content: <Review /> },\n * ]}\n * onComplete={form.handleSubmit(onSubmit)}\n * />\n * </FormProvider>\n * ```\n */\nexport function Wizard({\n steps,\n activeIndex,\n defaultActiveIndex = 0,\n onStepChange,\n onComplete,\n nextLabel = \"Next\",\n backLabel = \"Back\",\n finishLabel = \"Finish\",\n optionalLabel = \"(optional)\",\n clickableSteps = false,\n renderActions,\n className,\n}: WizardProps) {\n const isControlled = activeIndex !== undefined;\n const [internalIndex, setInternalIndex] = useState(defaultActiveIndex);\n const [validating, setValidating] = useState(false);\n\n const current = Math.min(isControlled ? activeIndex : internalIndex, steps.length - 1);\n const step = steps[current];\n\n const moveTo = useCallback(\n (index: number): void => {\n if (!isControlled) setInternalIndex(index);\n onStepChange?.(index, steps[index]);\n },\n [isControlled, onStepChange, steps],\n );\n\n /**\n * Run a step's gate. A gate that throws counts as \"not allowed\": a `validate`\n * wired to a network check should not strand the user on a half-advanced flow\n * when the request fails.\n */\n const runValidate = useCallback(async (candidate: WizardStep): Promise<boolean> => {\n if (!candidate.validate) return true;\n setValidating(true);\n try {\n return await candidate.validate();\n } catch {\n return false;\n } finally {\n setValidating(false);\n }\n }, []);\n\n const next = useCallback(async (): Promise<void> => {\n if (!(await runValidate(step))) return;\n if (current === steps.length - 1) {\n await onComplete?.();\n return;\n }\n moveTo(current + 1);\n }, [current, moveTo, onComplete, runValidate, step, steps.length]);\n\n const back = useCallback((): void => {\n if (current > 0) moveTo(current - 1);\n }, [current, moveTo]);\n\n const goTo = useCallback(\n async (index: number): Promise<void> => {\n const target = Math.max(0, Math.min(index, steps.length - 1));\n if (target === current) return;\n if (target < current) {\n moveTo(target);\n return;\n }\n for (let i = current; i < target; i += 1) {\n if (!(await runValidate(steps[i]))) return;\n }\n moveTo(target);\n },\n [current, moveTo, runValidate, steps],\n );\n\n const controls = useMemo<WizardControls>(\n () => ({\n activeIndex: current,\n step,\n validating,\n isFirst: current === 0,\n isLast: current === steps.length - 1,\n next,\n back,\n goTo,\n }),\n [back, current, goTo, next, step, steps.length, validating],\n );\n\n const indicatorSteps = useMemo(\n () =>\n steps.map((item) => ({\n label: item.label,\n description: item.optional\n ? `${item.description ?? \"\"} ${optionalLabel}`.trim()\n : item.description,\n })),\n [optionalLabel, steps],\n );\n\n return (\n <div className={cn(styles.wizard, className)}>\n <Stepper\n steps={indicatorSteps}\n current={current}\n onStepClick={clickableSteps ? (index) => void goTo(index) : undefined}\n />\n\n <div className={styles.body} role=\"group\" aria-label={step.label}>\n {typeof step.content === \"function\" ? step.content(controls) : step.content}\n </div>\n\n {renderActions ? (\n renderActions(controls)\n ) : (\n <div className={styles.actions}>\n <Button\n variant=\"secondary\"\n onClick={back}\n disabled={controls.isFirst || validating}\n >\n {backLabel}\n </Button>\n <Button onClick={() => void next()} loading={validating}>\n {controls.isLast ? finishLabel : nextLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAyHA,SAAgB,EAAO,EACnB,UACA,gBACA,wBAAqB,GACrB,iBACA,eACA,eAAY,QACZ,eAAY,QACZ,iBAAc,UACd,mBAAgB,cAChB,oBAAiB,IACjB,kBACA,gBACY;CACZ,IAAM,IAAe,MAAgB,KAAA,GAC/B,CAAC,GAAe,KAAoB,EAAS,CAAkB,GAC/D,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,IAAU,KAAK,IAAI,IAAe,IAAc,GAAe,EAAM,SAAS,CAAC,GAC/E,IAAO,EAAM,IAEb,IAAS,GACV,MAAwB;EAErB,AADK,KAAc,EAAiB,CAAK,GACzC,IAAe,GAAO,EAAM,EAAM;CACtC,GACA;EAAC;EAAc;EAAc;CAAK,CACtC,GAOM,IAAc,EAAY,OAAO,MAA4C;EAC/E,IAAI,CAAC,EAAU,UAAU,OAAO;EAChC,EAAc,EAAI;EAClB,IAAI;GACA,OAAO,MAAM,EAAU,SAAS;EACpC,QAAQ;GACJ,OAAO;EACX,UAAU;GACN,EAAc,EAAK;EACvB;CACJ,GAAG,CAAC,CAAC,GAEC,IAAO,EAAY,YAA2B;EAC1C,UAAM,EAAY,CAAI,GAC5B;OAAI,MAAY,EAAM,SAAS,GAAG;IAC9B,MAAM,IAAa;IACnB;GACJ;GACA,EAAO,IAAU,CAAC;EADlB;CAEJ,GAAG;EAAC;EAAS;EAAQ;EAAY;EAAa;EAAM,EAAM;CAAM,CAAC,GAE3D,IAAO,QAAwB;EACjC,AAAI,IAAU,KAAG,EAAO,IAAU,CAAC;CACvC,GAAG,CAAC,GAAS,CAAM,CAAC,GAEd,IAAO,EACT,OAAO,MAAiC;EACpC,IAAM,IAAS,KAAK,IAAI,GAAG,KAAK,IAAI,GAAO,EAAM,SAAS,CAAC,CAAC;EACxD,UAAW,GACf;OAAI,IAAS,GAAS;IAClB,EAAO,CAAM;IACb;GACJ;GACA,KAAK,IAAI,IAAI,GAAS,IAAI,GAAQ,KAAK,GACnC,IAAI,CAAE,MAAM,EAAY,EAAM,EAAE,GAAI;GAExC,EAAO,CAAM;EAJb;CAKJ,GACA;EAAC;EAAS;EAAQ;EAAa;CAAK,CACxC,GAEM,IAAW,SACN;EACH,aAAa;EACb;EACA;EACA,SAAS,MAAY;EACrB,QAAQ,MAAY,EAAM,SAAS;EACnC;EACA;EACA;CACJ,IACA;EAAC;EAAM;EAAS;EAAM;EAAM;EAAM,EAAM;EAAQ;CAAU,CAC9D,GAEM,IAAiB,QAEf,EAAM,KAAK,OAAU;EACjB,OAAO,EAAK;EACZ,aAAa,EAAK,WACZ,GAAG,EAAK,eAAe,GAAG,GAAG,IAAgB,KAAK,IAClD,EAAK;CACf,EAAE,GACN,CAAC,GAAe,CAAK,CACzB;CAEA,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,QAAQ,CAAS;EAA3C,UAAA;GACI,kBAAC,GAAD;IACI,OAAO;IACE;IACT,aAAa,KAAkB,MAAU,KAAK,EAAK,CAAK,IAAI,KAAA;GAC/D,CAAA;GAED,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAM,MAAK;IAAQ,cAAY,EAAK;IACtD,UAAA,OAAO,EAAK,WAAY,aAAa,EAAK,QAAQ,CAAQ,IAAI,EAAK;GACnE,CAAA;GAEJ,IACG,EAAc,CAAQ,IAEtB,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAvB,UAAA,CACI,kBAAC,GAAD;KACI,SAAQ;KACR,SAAS;KACT,UAAU,EAAS,WAAW;KAE7B,UAAA;IACG,CAAA,GACR,kBAAC,GAAD;KAAQ,eAAe,KAAK,EAAK;KAAG,SAAS;KACxC,UAAA,EAAS,SAAS,IAAc;IAC7B,CAAA,CACP;;EAER;;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-provider-context.cjs","names":[],"sources":["../../src/data/data-provider-context.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport type { DataProvider } from \"./create-data-provider\";\n\nconst DataProviderContext = createContext<DataProvider | null>(null);\n\n/** Props for {@link TempestDataProvider}. */\nexport interface TempestDataProviderProps {\n /** The data provider to expose to descendant resource hooks. */\n provider: DataProvider;\n /** The subtree that consumes the provider. */\n children: ReactNode;\n}\n\n/**\n * Inject a {@link DataProvider} into the React tree so the resource hooks\n * (`useList`, `useOne`, `useCreate`, …) can resolve it via {@link useDataProvider}.\n *\n * @example\n * const dataProvider = createDataProvider(apiClient);\n * <TempestDataProvider provider={dataProvider}>\n * <App />\n * </TempestDataProvider>\n */\nexport function TempestDataProvider({ provider, children }: TempestDataProviderProps) {\n return <DataProviderContext.Provider value={provider}>{children}</DataProviderContext.Provider>;\n}\n\n/**\n * Read the {@link DataProvider} from context.\n *\n * @returns The data provider supplied to the nearest {@link TempestDataProvider}.\n * @throws {Error} When used outside a {@link TempestDataProvider}.\n */\nexport function useDataProvider(): DataProvider {\n const provider = useContext(DataProviderContext);\n if (!provider) {\n throw new Error(\"useDataProvider must be used within a <TempestDataProvider>\");\n }\n return provider;\n}\n"],"mappings":"sDAKA,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"data-provider-context.cjs","names":[],"sources":["../../src/data/data-provider-context.tsx"],"sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport type { DataProvider } from \"./create-data-provider\";\n\nconst DataProviderContext = createContext<DataProvider | null>(null);\n\n/** Props for {@link TempestDataProvider}. */\nexport interface TempestDataProviderProps {\n /** The data provider to expose to descendant resource hooks. */\n provider: DataProvider;\n /** The subtree that consumes the provider. */\n children: ReactNode;\n}\n\n/**\n * Inject a {@link DataProvider} into the React tree so the resource hooks\n * (`useList`, `useOne`, `useCreate`, …) can resolve it via {@link useDataProvider}.\n *\n * @example\n * const dataProvider = createDataProvider(apiClient);\n * <TempestDataProvider provider={dataProvider}>\n * <App />\n * </TempestDataProvider>\n */\nexport function TempestDataProvider({ provider, children }: TempestDataProviderProps) {\n return <DataProviderContext.Provider value={provider}>{children}</DataProviderContext.Provider>;\n}\n\n/**\n * Read the {@link DataProvider} from context.\n *\n * @returns The data provider supplied to the nearest {@link TempestDataProvider}.\n * @throws {Error} When used outside a {@link TempestDataProvider}.\n */\nexport function useDataProvider(): DataProvider {\n const provider = useContext(DataProviderContext);\n if (!provider) {\n throw new Error(\"useDataProvider must be used within a <TempestDataProvider>\");\n }\n return provider;\n}\n"],"mappings":"sDAKA,IAAM,GAAA,EAAsB,EAAA,cAAA,CAAmC,IAAI,EAoBnE,SAAgB,EAAoB,CAAE,WAAU,YAAsC,CAClF,OAAO,EAAA,EAAA,IAAA,CAAC,EAAoB,SAArB,CAA8B,MAAO,EAAW,UAAuC,CAAA,CAClG,CAQA,SAAgB,GAAgC,CAC5C,IAAM,GAAA,EAAW,EAAA,WAAA,CAAW,CAAmB,EAC/C,GAAI,CAAC,EACD,MAAU,MAAM,6DAA6D,EAEjF,OAAO,CACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-resource.cjs","names":[],"sources":["../../src/data/use-resource.ts"],"sourcesContent":["import { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n UseMutationOptions,\n UseMutationResult,\n UseQueryOptions,\n UseQueryResult,\n} from \"@tanstack/react-query\";\n\nimport type { OffsetPage } from \"@/query/pagination\";\n\nimport type { GetListParams } from \"./create-data-provider\";\nimport { useDataProvider } from \"./data-provider-context\";\n\n/** Extra TanStack Query options for {@link useList} (key + fn are provided). */\nexport type UseListOptions<T> = Omit<\n UseQueryOptions<OffsetPage<T>, Error, OffsetPage<T>>,\n \"queryKey\" | \"queryFn\"\n>;\n\n/** Extra TanStack Query options for {@link useOne} (key + fn + enabled are provided). */\nexport type UseOneOptions<T> = Omit<\n UseQueryOptions<T, Error, T>,\n \"queryKey\" | \"queryFn\" | \"enabled\"\n> & { enabled?: boolean };\n\n/**\n * Build the cache key for a resource list query.\n *\n * @param resource - The resource name.\n * @param params - The list params (page/sort/filters).\n * @returns A stable TanStack Query key.\n */\nexport function listQueryKey(resource: string, params?: GetListParams): unknown[] {\n return [\"data\", resource, \"list\", params];\n}\n\n/**\n * Build the cache key for a single-record query.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns A stable TanStack Query key.\n */\nexport function oneQueryKey(resource: string, id: string | number | null | undefined): unknown[] {\n return [\"data\", resource, \"one\", id];\n}\n\n/**\n * Query a paginated list of a resource through the active {@link useDataProvider}.\n *\n * @param resource - The resource name.\n * @param params - Pagination, sort and filter parameters.\n * @param options - Extra TanStack Query options.\n * @returns The TanStack Query result for the offset page.\n */\nexport function useList<T>(\n resource: string,\n params?: GetListParams,\n options?: UseListOptions<T>,\n): UseQueryResult<OffsetPage<T>, Error> {\n const provider = useDataProvider();\n return useQuery<OffsetPage<T>, Error>({\n queryKey: listQueryKey(resource, params),\n queryFn: () => provider.getList<T>(resource, params),\n ...options,\n });\n}\n\n/**\n * Query a single record by id through the active {@link useDataProvider}.\n *\n * The query is disabled while `id` is `null`/`undefined`.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @param options - Extra TanStack Query options.\n * @returns The TanStack Query result for the record.\n */\nexport function useOne<T>(\n resource: string,\n id: string | number | null | undefined,\n options?: UseOneOptions<T>,\n): UseQueryResult<T, Error> {\n const provider = useDataProvider();\n const { enabled, ...rest } = options ?? {};\n return useQuery<T, Error>({\n queryKey: oneQueryKey(resource, id),\n queryFn: () => provider.getOne<T>(resource, id as string | number),\n enabled: id != null && (enabled ?? true),\n ...rest,\n });\n}\n\n/** Options for the create mutation (mutationFn + onSuccess are provided). */\nexport type UseCreateOptions<T> = Omit<UseMutationOptions<T, Error, unknown>, \"mutationFn\">;\n\n/**\n * Create a record and invalidate the resource list cache on success.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate(data)` creates the record.\n */\nexport function useCreate<T>(\n resource: string,\n options?: UseCreateOptions<T>,\n): UseMutationResult<T, Error, unknown> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, unknown>({\n mutationFn: (data: unknown) => provider.create<T>(resource, data),\n onSuccess: (...args) => {\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n\n/** Variables accepted by the update mutation. */\nexport interface UpdateVariables {\n /** The record id to update. */\n id: string | number;\n /** The update payload. */\n data: unknown;\n}\n\n/** Options for the update mutation (mutationFn + onSuccess are provided). */\nexport type UseUpdateOptions<T> = Omit<UseMutationOptions<T, Error, UpdateVariables>, \"mutationFn\">;\n\n/**\n * Update a record and invalidate both the list and the single-record caches.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate({ id, data })` updates the record.\n */\nexport function useUpdate<T>(\n resource: string,\n options?: UseUpdateOptions<T>,\n): UseMutationResult<T, Error, UpdateVariables> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, UpdateVariables>({\n mutationFn: ({ id, data }: UpdateVariables) => provider.update<T>(resource, id, data),\n onSuccess: (...args) => {\n const [, variables] = args;\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n void queryClient.invalidateQueries({ queryKey: oneQueryKey(resource, variables.id) });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n\n/** Options for the delete mutation (mutationFn + onSuccess are provided). */\nexport type UseDeleteOptions<T> = Omit<UseMutationOptions<T, Error, string | number>, \"mutationFn\">;\n\n/**\n * Delete a record by id and invalidate the resource list cache on success.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate(id)` deletes the record.\n */\nexport function useDelete<T>(\n resource: string,\n options?: UseDeleteOptions<T>,\n): UseMutationResult<T, Error, string | number> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, string | number>({\n mutationFn: (id: string | number) => provider.deleteOne<T>(resource, id),\n onSuccess: (...args) => {\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n"],"mappings":"sFAgCA,SAAgB,EAAa,EAAkB,EAAmC,CAC9E,MAAO,CAAC,OAAQ,EAAU,OAAQ,CAAM,CAC5C,CASA,SAAgB,EAAY,EAAkB,EAAmD,CAC7F,MAAO,CAAC,OAAQ,EAAU,MAAO,CAAE,CACvC,CAUA,SAAgB,EACZ,EACA,EACA,EACoC,CACpC,IAAM,EAAW,EAAA,gBAAgB,EACjC,OAAA,
|
|
1
|
+
{"version":3,"file":"use-resource.cjs","names":[],"sources":["../../src/data/use-resource.ts"],"sourcesContent":["import { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n UseMutationOptions,\n UseMutationResult,\n UseQueryOptions,\n UseQueryResult,\n} from \"@tanstack/react-query\";\n\nimport type { OffsetPage } from \"@/query/pagination\";\n\nimport type { GetListParams } from \"./create-data-provider\";\nimport { useDataProvider } from \"./data-provider-context\";\n\n/** Extra TanStack Query options for {@link useList} (key + fn are provided). */\nexport type UseListOptions<T> = Omit<\n UseQueryOptions<OffsetPage<T>, Error, OffsetPage<T>>,\n \"queryKey\" | \"queryFn\"\n>;\n\n/** Extra TanStack Query options for {@link useOne} (key + fn + enabled are provided). */\nexport type UseOneOptions<T> = Omit<\n UseQueryOptions<T, Error, T>,\n \"queryKey\" | \"queryFn\" | \"enabled\"\n> & { enabled?: boolean };\n\n/**\n * Build the cache key for a resource list query.\n *\n * @param resource - The resource name.\n * @param params - The list params (page/sort/filters).\n * @returns A stable TanStack Query key.\n */\nexport function listQueryKey(resource: string, params?: GetListParams): unknown[] {\n return [\"data\", resource, \"list\", params];\n}\n\n/**\n * Build the cache key for a single-record query.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @returns A stable TanStack Query key.\n */\nexport function oneQueryKey(resource: string, id: string | number | null | undefined): unknown[] {\n return [\"data\", resource, \"one\", id];\n}\n\n/**\n * Query a paginated list of a resource through the active {@link useDataProvider}.\n *\n * @param resource - The resource name.\n * @param params - Pagination, sort and filter parameters.\n * @param options - Extra TanStack Query options.\n * @returns The TanStack Query result for the offset page.\n */\nexport function useList<T>(\n resource: string,\n params?: GetListParams,\n options?: UseListOptions<T>,\n): UseQueryResult<OffsetPage<T>, Error> {\n const provider = useDataProvider();\n return useQuery<OffsetPage<T>, Error>({\n queryKey: listQueryKey(resource, params),\n queryFn: () => provider.getList<T>(resource, params),\n ...options,\n });\n}\n\n/**\n * Query a single record by id through the active {@link useDataProvider}.\n *\n * The query is disabled while `id` is `null`/`undefined`.\n *\n * @param resource - The resource name.\n * @param id - The record id.\n * @param options - Extra TanStack Query options.\n * @returns The TanStack Query result for the record.\n */\nexport function useOne<T>(\n resource: string,\n id: string | number | null | undefined,\n options?: UseOneOptions<T>,\n): UseQueryResult<T, Error> {\n const provider = useDataProvider();\n const { enabled, ...rest } = options ?? {};\n return useQuery<T, Error>({\n queryKey: oneQueryKey(resource, id),\n queryFn: () => provider.getOne<T>(resource, id as string | number),\n enabled: id != null && (enabled ?? true),\n ...rest,\n });\n}\n\n/** Options for the create mutation (mutationFn + onSuccess are provided). */\nexport type UseCreateOptions<T> = Omit<UseMutationOptions<T, Error, unknown>, \"mutationFn\">;\n\n/**\n * Create a record and invalidate the resource list cache on success.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate(data)` creates the record.\n */\nexport function useCreate<T>(\n resource: string,\n options?: UseCreateOptions<T>,\n): UseMutationResult<T, Error, unknown> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, unknown>({\n mutationFn: (data: unknown) => provider.create<T>(resource, data),\n onSuccess: (...args) => {\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n\n/** Variables accepted by the update mutation. */\nexport interface UpdateVariables {\n /** The record id to update. */\n id: string | number;\n /** The update payload. */\n data: unknown;\n}\n\n/** Options for the update mutation (mutationFn + onSuccess are provided). */\nexport type UseUpdateOptions<T> = Omit<UseMutationOptions<T, Error, UpdateVariables>, \"mutationFn\">;\n\n/**\n * Update a record and invalidate both the list and the single-record caches.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate({ id, data })` updates the record.\n */\nexport function useUpdate<T>(\n resource: string,\n options?: UseUpdateOptions<T>,\n): UseMutationResult<T, Error, UpdateVariables> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, UpdateVariables>({\n mutationFn: ({ id, data }: UpdateVariables) => provider.update<T>(resource, id, data),\n onSuccess: (...args) => {\n const [, variables] = args;\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n void queryClient.invalidateQueries({ queryKey: oneQueryKey(resource, variables.id) });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n\n/** Options for the delete mutation (mutationFn + onSuccess are provided). */\nexport type UseDeleteOptions<T> = Omit<UseMutationOptions<T, Error, string | number>, \"mutationFn\">;\n\n/**\n * Delete a record by id and invalidate the resource list cache on success.\n *\n * @param resource - The resource name.\n * @param options - Extra TanStack Mutation options.\n * @returns The mutation; `mutate(id)` deletes the record.\n */\nexport function useDelete<T>(\n resource: string,\n options?: UseDeleteOptions<T>,\n): UseMutationResult<T, Error, string | number> {\n const provider = useDataProvider();\n const queryClient = useQueryClient();\n return useMutation<T, Error, string | number>({\n mutationFn: (id: string | number) => provider.deleteOne<T>(resource, id),\n onSuccess: (...args) => {\n void queryClient.invalidateQueries({ queryKey: [\"data\", resource, \"list\"] });\n options?.onSuccess?.(...args);\n },\n ...options,\n });\n}\n"],"mappings":"sFAgCA,SAAgB,EAAa,EAAkB,EAAmC,CAC9E,MAAO,CAAC,OAAQ,EAAU,OAAQ,CAAM,CAC5C,CASA,SAAgB,EAAY,EAAkB,EAAmD,CAC7F,MAAO,CAAC,OAAQ,EAAU,MAAO,CAAE,CACvC,CAUA,SAAgB,EACZ,EACA,EACA,EACoC,CACpC,IAAM,EAAW,EAAA,gBAAgB,EACjC,OAAA,EAAO,EAAA,SAAA,CAA+B,CAClC,SAAU,EAAa,EAAU,CAAM,EACvC,YAAe,EAAS,QAAW,EAAU,CAAM,EACnD,GAAG,CACP,CAAC,CACL,CAYA,SAAgB,EACZ,EACA,EACA,EACwB,CACxB,IAAM,EAAW,EAAA,gBAAgB,EAC3B,CAAE,UAAS,GAAG,GAAS,GAAW,CAAC,EACzC,OAAA,EAAO,EAAA,SAAA,CAAmB,CACtB,SAAU,EAAY,EAAU,CAAE,EAClC,YAAe,EAAS,OAAU,EAAU,CAAqB,EACjE,QAAS,GAAM,OAAS,GAAW,IACnC,GAAG,CACP,CAAC,CACL,CAYA,SAAgB,EACZ,EACA,EACoC,CACpC,IAAM,EAAW,EAAA,gBAAgB,EAC3B,GAAA,EAAc,EAAA,eAAA,CAAe,EACnC,OAAA,EAAO,EAAA,YAAA,CAA+B,CAClC,WAAa,GAAkB,EAAS,OAAU,EAAU,CAAI,EAChE,WAAY,GAAG,IAAS,CACpB,EAAiB,kBAAkB,CAAE,SAAU,CAAC,OAAQ,EAAU,MAAM,CAAE,CAAC,EAC3E,GAAS,YAAY,GAAG,CAAI,CAChC,EACA,GAAG,CACP,CAAC,CACL,CAoBA,SAAgB,EACZ,EACA,EAC4C,CAC5C,IAAM,EAAW,EAAA,gBAAgB,EAC3B,GAAA,EAAc,EAAA,eAAA,CAAe,EACnC,OAAA,EAAO,EAAA,YAAA,CAAuC,CAC1C,YAAa,CAAE,KAAI,UAA4B,EAAS,OAAU,EAAU,EAAI,CAAI,EACpF,WAAY,GAAG,IAAS,CACpB,GAAM,EAAG,GAAa,EACtB,EAAiB,kBAAkB,CAAE,SAAU,CAAC,OAAQ,EAAU,MAAM,CAAE,CAAC,EAC3E,EAAiB,kBAAkB,CAAE,SAAU,EAAY,EAAU,EAAU,EAAE,CAAE,CAAC,EACpF,GAAS,YAAY,GAAG,CAAI,CAChC,EACA,GAAG,CACP,CAAC,CACL,CAYA,SAAgB,EACZ,EACA,EAC4C,CAC5C,IAAM,EAAW,EAAA,gBAAgB,EAC3B,GAAA,EAAc,EAAA,eAAA,CAAe,EACnC,OAAA,EAAO,EAAA,YAAA,CAAuC,CAC1C,WAAa,GAAwB,EAAS,UAAa,EAAU,CAAE,EACvE,WAAY,GAAG,IAAS,CACpB,EAAiB,kBAAkB,CAAE,SAAU,CAAC,OAAQ,EAAU,MAAM,CAAE,CAAC,EAC3E,GAAS,YAAY,GAAG,CAAI,CAChC,EACA,GAAG,CACP,CAAC,CACL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextEditor.cjs","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RichTextEditor.cjs","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RichTextEditor.js","names":[],"sources":["../../src/editor/RichTextEditor.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body builds the tiptap editor,\n * its toolbar state and the change plumbing: every toolbar button reads\n * `editor.isActive`, so the toolbar cannot be lifted out without passing the editor\n * instance back down.\n */\nimport { useEffect } from \"react\";\nimport { EditorContent, useEditor, useEditorState } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport {\n Bold,\n Code,\n Heading1,\n Heading2,\n Italic,\n List,\n ListOrdered,\n Quote,\n Redo2,\n Strikethrough,\n Undo2,\n} from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./RichTextEditor.module.css\";\n\nexport interface RichTextEditorProps {\n /** Current editor content as an HTML string (controlled). */\n value: string;\n /** Called with the updated HTML whenever the document changes. */\n onChange: (html: string) => void;\n /** Placeholder text shown when the editor is empty. */\n placeholder?: string;\n /** Whether the content is editable. Defaults to `true`. */\n editable?: boolean;\n /** Whether to render the formatting toolbar. Defaults to `true`. */\n toolbar?: boolean;\n /** Extra class names applied to the wrapper element. */\n className?: string;\n}\n\n/**\n * RichTextEditor — a controlled WYSIWYG editor built on tiptap v3.\n *\n * Renders an optional formatting toolbar (bold, italic, strike, code, headings,\n * lists, blockquote, undo/redo) above a ProseMirror-backed editable area. The\n * document is controlled via the `value` (HTML) / `onChange` pair: external\n * changes to `value` are synced into the editor without re-emitting updates.\n *\n * The toolbar's pressed states and the undo/redo `disabled` flags come from\n * `useEditorState`, which subscribes to exactly those values. `useEditor` does\n * not re-render on transactions (the tiptap v3 default), so reading `isActive`\n * and `can()` directly in the render body left every button stale — toggling\n * bold with a collapsed cursor arms the mark without changing the document, so no\n * update event ever brought React back to recompute the state. Subscribing\n * re-renders only when one of the tracked values actually flips, which is also\n * cheaper than re-rendering on every keystroke.\n *\n * Two effects keep the instance aligned with the props: one pushes an external\n * `value` change into the document with `emitUpdate: false` (so syncing never\n * re-emits `onChange`), the other mirrors the `editable` flag.\n */\nexport function RichTextEditor({\n value,\n onChange,\n placeholder,\n editable = true,\n toolbar = true,\n className,\n}: RichTextEditorProps) {\n const editor = useEditor({\n extensions: [StarterKit],\n content: value,\n editable,\n onUpdate: ({ editor: instance }) => onChange(instance.getHTML()),\n });\n\n useEffect(() => {\n if (!editor) return;\n if (value !== editor.getHTML()) {\n editor.commands.setContent(value, { emitUpdate: false });\n }\n }, [editor, value]);\n\n useEffect(() => {\n if (!editor) return;\n editor.setEditable(editable);\n }, [editor, editable]);\n\n const toolbarState = useEditorState({\n editor,\n selector: ({ editor: instance }) => ({\n bold: instance.isActive(\"bold\"),\n italic: instance.isActive(\"italic\"),\n strike: instance.isActive(\"strike\"),\n code: instance.isActive(\"code\"),\n heading1: instance.isActive(\"heading\", { level: 1 }),\n heading2: instance.isActive(\"heading\", { level: 2 }),\n bulletList: instance.isActive(\"bulletList\"),\n orderedList: instance.isActive(\"orderedList\"),\n blockquote: instance.isActive(\"blockquote\"),\n canUndo: instance.can().chain().focus().undo().run(),\n canRedo: instance.can().chain().focus().redo().run(),\n }),\n });\n\n if (!editor || !toolbarState) return null;\n\n const canUndo = toolbarState.canUndo;\n const canRedo = toolbarState.canRedo;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n {toolbar && (\n <div className={styles.toolbar} role=\"toolbar\" aria-label=\"Formatação de texto\">\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bold && styles.active)}\n aria-label=\"Negrito\"\n aria-pressed={toolbarState.bold}\n onClick={() => editor.chain().focus().toggleBold().run()}\n >\n <Bold size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.italic && styles.active)}\n aria-label=\"Itálico\"\n aria-pressed={toolbarState.italic}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n >\n <Italic size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.strike && styles.active)}\n aria-label=\"Tachado\"\n aria-pressed={toolbarState.strike}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n >\n <Strikethrough size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.code && styles.active)}\n aria-label=\"Código\"\n aria-pressed={toolbarState.code}\n onClick={() => editor.chain().focus().toggleCode().run()}\n >\n <Code size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading1 && styles.active)}\n aria-label=\"Título 1\"\n aria-pressed={toolbarState.heading1}\n onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n >\n <Heading1 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.heading2 && styles.active)}\n aria-label=\"Título 2\"\n aria-pressed={toolbarState.heading2}\n onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n >\n <Heading2 size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.bulletList && styles.active)}\n aria-label=\"Lista com marcadores\"\n aria-pressed={toolbarState.bulletList}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <List size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.orderedList && styles.active)}\n aria-label=\"Lista numerada\"\n aria-pressed={toolbarState.orderedList}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <ListOrdered size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={cn(styles.button, toolbarState.blockquote && styles.active)}\n aria-label=\"Citação\"\n aria-pressed={toolbarState.blockquote}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n >\n <Quote size={16} aria-hidden />\n </button>\n <span className={styles.separator} aria-hidden />\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Desfazer\"\n disabled={!canUndo}\n onClick={() => editor.chain().focus().undo().run()}\n >\n <Undo2 size={16} aria-hidden />\n </button>\n <button\n type=\"button\"\n className={styles.button}\n aria-label=\"Refazer\"\n disabled={!canRedo}\n onClick={() => editor.chain().focus().redo().run()}\n >\n <Redo2 size={16} aria-hidden />\n </button>\n </div>\n )}\n <EditorContent\n editor={editor}\n className={styles.content}\n data-placeholder={placeholder}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-error-handler.cjs","names":[],"sources":["../../src/error-boundary/use-error-handler.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\n/**\n * Hook that propagates errors to the nearest {@link ErrorBoundary}.\n *\n * React error boundaries only catch errors thrown during render. Async errors\n * (failed mutations, websocket failures, etc.) need to be re-thrown in a\n * render pass — that is what `throwError` does.\n *\n * @example\n * const throwError = useErrorHandler();\n * useEffect(() => {\n * stream.on(\"error\", throwError);\n * }, []);\n */\nexport function useErrorHandler(): (error: unknown) => void {\n const [, setState] = useState<Error | null>(null);\n return useCallback((error: unknown) => {\n setState(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n }, []);\n}\n"],"mappings":"uBAeA,SAAgB,GAA4C,CACxD,GAAM,EAAG,IAAA,
|
|
1
|
+
{"version":3,"file":"use-error-handler.cjs","names":[],"sources":["../../src/error-boundary/use-error-handler.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\n/**\n * Hook that propagates errors to the nearest {@link ErrorBoundary}.\n *\n * React error boundaries only catch errors thrown during render. Async errors\n * (failed mutations, websocket failures, etc.) need to be re-thrown in a\n * render pass — that is what `throwError` does.\n *\n * @example\n * const throwError = useErrorHandler();\n * useEffect(() => {\n * stream.on(\"error\", throwError);\n * }, []);\n */\nexport function useErrorHandler(): (error: unknown) => void {\n const [, setState] = useState<Error | null>(null);\n return useCallback((error: unknown) => {\n setState(() => {\n throw error instanceof Error ? error : new Error(String(error));\n });\n }, []);\n}\n"],"mappings":"uBAeA,SAAgB,GAA4C,CACxD,GAAM,EAAG,IAAA,EAAY,EAAA,SAAA,CAAuB,IAAI,EAChD,OAAA,EAAO,EAAA,YAAA,CAAa,GAAmB,CACnC,MAAe,CACX,MAAM,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,CAClE,CAAC,CACL,EAAG,CAAC,CAAC,CACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureFlagsProvider.cjs","names":[],"sources":["../../src/feature-flags/FeatureFlagsProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { FeatureFlagsAdapter, FlagValue } from \"./types\";\n\nconst FeatureFlagsContext = createContext<FeatureFlagsAdapter | null>(null);\n\nexport interface FeatureFlagsProviderProps {\n adapter: FeatureFlagsAdapter;\n children: ReactNode;\n}\n\n/** Inject a feature flags adapter into the tree. */\nexport function FeatureFlagsProvider({ adapter, children }: FeatureFlagsProviderProps) {\n const value = useMemo(() => adapter, [adapter]);\n useEffect(() => undefined, [value]);\n return <FeatureFlagsContext.Provider value={value}>{children}</FeatureFlagsContext.Provider>;\n}\n\nfunction useAdapter(): FeatureFlagsAdapter {\n const ctx = useContext(FeatureFlagsContext);\n if (!ctx) throw new Error(\"useFeatureFlag requires <FeatureFlagsProvider>\");\n return ctx;\n}\n\n/**\n * Read a boolean flag and re-render when the adapter fires `onChange`.\n *\n * @example\n * const showNewFeed = useFeatureFlag(\"new-feed\", false);\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): boolean => adapter.isEnabled(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n\n/**\n * Read a typed flag value (string / number / boolean / null) and re-render\n * on change.\n */\nexport function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): T => adapter.get<T>(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"FeatureFlagsProvider.cjs","names":[],"sources":["../../src/feature-flags/FeatureFlagsProvider.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { FeatureFlagsAdapter, FlagValue } from \"./types\";\n\nconst FeatureFlagsContext = createContext<FeatureFlagsAdapter | null>(null);\n\nexport interface FeatureFlagsProviderProps {\n adapter: FeatureFlagsAdapter;\n children: ReactNode;\n}\n\n/** Inject a feature flags adapter into the tree. */\nexport function FeatureFlagsProvider({ adapter, children }: FeatureFlagsProviderProps) {\n const value = useMemo(() => adapter, [adapter]);\n useEffect(() => undefined, [value]);\n return <FeatureFlagsContext.Provider value={value}>{children}</FeatureFlagsContext.Provider>;\n}\n\nfunction useAdapter(): FeatureFlagsAdapter {\n const ctx = useContext(FeatureFlagsContext);\n if (!ctx) throw new Error(\"useFeatureFlag requires <FeatureFlagsProvider>\");\n return ctx;\n}\n\n/**\n * Read a boolean flag and re-render when the adapter fires `onChange`.\n *\n * @example\n * const showNewFeed = useFeatureFlag(\"new-feed\", false);\n */\nexport function useFeatureFlag(key: string, defaultValue = false): boolean {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): boolean => adapter.isEnabled(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n\n/**\n * Read a typed flag value (string / number / boolean / null) and re-render\n * on change.\n */\nexport function useFlagValue<T extends FlagValue = FlagValue>(key: string, defaultValue?: T): T {\n const adapter = useAdapter();\n const subscribe = (listener: () => void): (() => void) =>\n adapter.onChange ? adapter.onChange(listener) : () => undefined;\n const get = (): T => adapter.get<T>(key, defaultValue);\n return useSyncExternalStore(subscribe, get, get);\n}\n"],"mappings":"sDAIA,IAAM,GAAA,EAAsB,EAAA,cAAA,CAA0C,IAAI,EAQ1E,SAAgB,EAAqB,CAAE,UAAS,YAAuC,CACnF,IAAM,GAAA,EAAQ,EAAA,QAAA,KAAc,EAAS,CAAC,CAAO,CAAC,EAE9C,OADA,EAAA,EAAA,UAAA,KAAgB,IAAA,GAAW,CAAC,CAAK,CAAC,GAC3B,EAAA,EAAA,IAAA,CAAC,EAAoB,SAArB,CAAqC,QAAQ,UAAuC,CAAA,CAC/F,CAEA,SAAS,GAAkC,CACvC,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,CAAmB,EAC1C,GAAI,CAAC,EAAK,MAAU,MAAM,gDAAgD,EAC1E,OAAO,CACX,CAQA,SAAgB,EAAe,EAAa,EAAe,GAAgB,CACvE,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAqB,EAAQ,UAAU,EAAK,CAAY,EAC9D,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAW,EAAK,CAAG,CACnD,CAMA,SAAgB,EAA8C,EAAa,EAAqB,CAC5F,IAAM,EAAU,EAAW,EACrB,EAAa,GACf,EAAQ,SAAW,EAAQ,SAAS,CAAQ,MAAU,IAAA,GACpD,MAAe,EAAQ,IAAO,EAAK,CAAY,EACrD,OAAA,EAAO,EAAA,qBAAA,CAAqB,EAAW,EAAK,CAAG,CACnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormField.cjs","names":[],"sources":["../../src/forms/FormField.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — FormFieldChildProps is not a component's props — it\n * is the contract handed to whatever control the field wraps, and it is exactly what\n * react-hook-form's register returns (name, value, onChange, onBlur, ref) plus what\n * the field adds around it (error, label, helperText, required, id). Trimming it\n * breaks the wiring it exists to describe.\n */\nimport { cloneElement, isValidElement } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n Controller,\n useFormContext,\n type Control,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\n\nexport interface FormFieldChildProps {\n name: string;\n value: unknown;\n onChange: (...args: unknown[]) => void;\n onBlur: () => void;\n ref: ControllerRenderProps[\"ref\"];\n error?: string;\n \"aria-invalid\"?: boolean;\n \"aria-describedby\"?: string;\n label?: ReactNode;\n helperText?: ReactNode;\n required?: boolean;\n id?: string;\n}\n\nexport interface FormFieldProps<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n> {\n /** Field name (dot path supported, e.g. `\"address.city\"`). */\n name: TName;\n /** Field label rendered by the wrapped control. */\n label?: ReactNode;\n /** Helper text rendered by the wrapped control when there is no error. */\n helperText?: ReactNode;\n /** When `true`, marks the control as required (visual hint + native attribute). */\n required?: boolean;\n /**\n * Explicit `control` from `useForm()` / `useZodForm()`. Optional when a\n * `FormProvider` is in the tree.\n */\n control?: Control<TValues>;\n /**\n * The control to render. Receives `{ value, onChange, onBlur, ref, error, ... }`\n * via `cloneElement`. Pass `<Input />`, `<Select />`, masked inputs, etc.\n */\n children: ReactElement;\n}\n\n/**\n * Glue between `react-hook-form` `Controller` and the SDK's controlled\n * components. Wraps any control that accepts `{ value, onChange, label,\n * error }` and routes RHF state into it — eliminating the per-field\n * `<Controller render={...} />` boilerplate.\n *\n * @example\n * const form = useZodForm(schema);\n * <FormProvider {...form}>\n * <Form>\n * <FormField name=\"email\" label=\"Email\" required>\n * <Input type=\"email\" />\n * </FormField>\n * <FormField name=\"cep\" label=\"CEP\">\n * <CEPInput />\n * </FormField>\n * </Form>\n * </FormProvider>;\n */\nexport function FormField<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n>({ name, label, helperText, required, control, children }: FormFieldProps<TValues, TName>) {\n const context = useFormContext<TValues>();\n const resolvedControl = control ?? context?.control;\n if (!resolvedControl) {\n throw new Error(\n \"FormField requires either a `control` prop or a <FormProvider> in the tree.\",\n );\n }\n\n return (\n <Controller\n name={name}\n control={resolvedControl}\n render={({ field, fieldState }) => {\n if (!isValidElement(children)) return children;\n const errorMessage = fieldState.error?.message;\n return cloneElement(children as ReactElement<FormFieldChildProps>, {\n name: field.name,\n value: field.value,\n onChange: field.onChange,\n onBlur: field.onBlur,\n ref: field.ref,\n label,\n helperText: errorMessage ? undefined : helperText,\n error: errorMessage,\n required,\n \"aria-invalid\": !!errorMessage,\n });\n }}\n />\n );\n}\n"],"mappings":"mFA4EA,SAAgB,EAGd,CAAE,OAAM,QAAO,aAAY,WAAU,UAAS,YAA4C,CACxF,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"FormField.cjs","names":[],"sources":["../../src/forms/FormField.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — FormFieldChildProps is not a component's props — it\n * is the contract handed to whatever control the field wraps, and it is exactly what\n * react-hook-form's register returns (name, value, onChange, onBlur, ref) plus what\n * the field adds around it (error, label, helperText, required, id). Trimming it\n * breaks the wiring it exists to describe.\n */\nimport { cloneElement, isValidElement } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport {\n Controller,\n useFormContext,\n type Control,\n type ControllerRenderProps,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\n\nexport interface FormFieldChildProps {\n name: string;\n value: unknown;\n onChange: (...args: unknown[]) => void;\n onBlur: () => void;\n ref: ControllerRenderProps[\"ref\"];\n error?: string;\n \"aria-invalid\"?: boolean;\n \"aria-describedby\"?: string;\n label?: ReactNode;\n helperText?: ReactNode;\n required?: boolean;\n id?: string;\n}\n\nexport interface FormFieldProps<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n> {\n /** Field name (dot path supported, e.g. `\"address.city\"`). */\n name: TName;\n /** Field label rendered by the wrapped control. */\n label?: ReactNode;\n /** Helper text rendered by the wrapped control when there is no error. */\n helperText?: ReactNode;\n /** When `true`, marks the control as required (visual hint + native attribute). */\n required?: boolean;\n /**\n * Explicit `control` from `useForm()` / `useZodForm()`. Optional when a\n * `FormProvider` is in the tree.\n */\n control?: Control<TValues>;\n /**\n * The control to render. Receives `{ value, onChange, onBlur, ref, error, ... }`\n * via `cloneElement`. Pass `<Input />`, `<Select />`, masked inputs, etc.\n */\n children: ReactElement;\n}\n\n/**\n * Glue between `react-hook-form` `Controller` and the SDK's controlled\n * components. Wraps any control that accepts `{ value, onChange, label,\n * error }` and routes RHF state into it — eliminating the per-field\n * `<Controller render={...} />` boilerplate.\n *\n * @example\n * const form = useZodForm(schema);\n * <FormProvider {...form}>\n * <Form>\n * <FormField name=\"email\" label=\"Email\" required>\n * <Input type=\"email\" />\n * </FormField>\n * <FormField name=\"cep\" label=\"CEP\">\n * <CEPInput />\n * </FormField>\n * </Form>\n * </FormProvider>;\n */\nexport function FormField<\n TValues extends FieldValues = FieldValues,\n TName extends FieldPath<TValues> = FieldPath<TValues>,\n>({ name, label, helperText, required, control, children }: FormFieldProps<TValues, TName>) {\n const context = useFormContext<TValues>();\n const resolvedControl = control ?? context?.control;\n if (!resolvedControl) {\n throw new Error(\n \"FormField requires either a `control` prop or a <FormProvider> in the tree.\",\n );\n }\n\n return (\n <Controller\n name={name}\n control={resolvedControl}\n render={({ field, fieldState }) => {\n if (!isValidElement(children)) return children;\n const errorMessage = fieldState.error?.message;\n return cloneElement(children as ReactElement<FormFieldChildProps>, {\n name: field.name,\n value: field.value,\n onChange: field.onChange,\n onBlur: field.onBlur,\n ref: field.ref,\n label,\n helperText: errorMessage ? undefined : helperText,\n error: errorMessage,\n required,\n \"aria-invalid\": !!errorMessage,\n });\n }}\n />\n );\n}\n"],"mappings":"mFA4EA,SAAgB,EAGd,CAAE,OAAM,QAAO,aAAY,WAAU,UAAS,YAA4C,CACxF,IAAM,GAAA,EAAU,EAAA,eAAA,CAAwB,EAClC,EAAkB,GAAW,GAAS,QAC5C,GAAI,CAAC,EACD,MAAU,MACN,6EACJ,EAGJ,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CACU,OACN,QAAS,EACT,QAAS,CAAE,QAAO,gBAAiB,CAC/B,GAAI,EAAA,EAAC,EAAA,eAAA,CAAe,CAAQ,EAAG,OAAO,EACtC,IAAM,EAAe,EAAW,OAAO,QACvC,OAAA,EAAO,EAAA,aAAA,CAAa,EAA+C,CAC/D,KAAM,EAAM,KACZ,MAAO,EAAM,MACb,SAAU,EAAM,SAChB,OAAQ,EAAM,OACd,IAAK,EAAM,IACX,QACA,WAAY,EAAe,IAAA,GAAY,EACvC,MAAO,EACP,WACA,eAAgB,CAAC,CAAC,CACtB,CAAC,CACL,CACH,CAAA,CAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"masked-inputs.cjs","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value: string;\n onChange: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":"yKAWA,SAAS,EACL,EACA,EAAgE,UAClE,CACE,OAAA,
|
|
1
|
+
{"version":3,"file":"masked-inputs.cjs","names":[],"sources":["../../src/forms/masked-inputs.tsx"],"sourcesContent":["import { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input, type InputProps } from \"@/components/Input\";\nimport { formatCEP, formatCNPJ } from \"./br-validators\";\nimport { formatCPF, formatPhone } from \"@/utils/format\";\n\ntype MaskedFieldProps = Omit<InputProps, \"value\" | \"onChange\"> & {\n value: string;\n onChange: (value: string) => void;\n};\n\nfunction maskedInput(\n mask: (input: string) => string,\n inputMode: InputHTMLAttributes<HTMLInputElement>[\"inputMode\"] = \"numeric\",\n) {\n return forwardRef<HTMLInputElement, MaskedFieldProps>(function MaskedInput(\n { value, onChange, ...props },\n ref,\n ) {\n return (\n <Input\n {...props}\n ref={ref}\n value={mask(value ?? \"\")}\n inputMode={inputMode}\n onChange={(event) => onChange(mask(event.target.value))}\n />\n );\n });\n}\n\nexport const CPFInput = maskedInput(formatCPF);\nexport const CNPJInput = maskedInput(formatCNPJ);\nexport const PhoneInput = maskedInput(formatPhone, \"tel\");\nexport const CEPInput = maskedInput(formatCEP);\n\nexport interface MoneyInputProps extends Omit<InputProps, \"value\" | \"onChange\" | \"type\"> {\n /** Cents (integer). Internally treated as 1/100 of the currency unit. */\n value: number;\n onChange: (cents: number) => void;\n /** Currency code for `Intl.NumberFormat`. Default: `\"BRL\"`. */\n currency?: string;\n /** Locale for `Intl.NumberFormat`. Default: `\"pt-BR\"`. */\n locale?: string;\n}\n\nfunction formatCents(cents: number, locale: string, currency: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n}\n\nfunction parseCents(text: string): number {\n const digits = text.replace(/\\D/g, \"\");\n if (!digits) return 0;\n return Number.parseInt(digits, 10);\n}\n\n/**\n * Currency-masked input. Stores the value as an integer number of cents to\n * avoid floating-point error. Suitable for `react-hook-form` once you adapt\n * the field to expose cents.\n */\nexport const MoneyInput = forwardRef<HTMLInputElement, MoneyInputProps>(function MoneyInput(\n { value, onChange, currency = \"BRL\", locale = \"pt-BR\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type=\"text\"\n inputMode=\"numeric\"\n value={formatCents(value || 0, locale, currency)}\n onChange={(event) => onChange(parseCents(event.target.value))}\n />\n );\n});\n"],"mappings":"yKAWA,SAAS,EACL,EACA,EAAgE,UAClE,CACE,OAAA,EAAO,EAAA,WAAA,CAA+C,SAClD,CAAE,QAAO,WAAU,GAAG,GACtB,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,MAAO,EAAK,GAAS,EAAE,EACZ,YACX,SAAW,GAAU,EAAS,EAAK,EAAM,OAAO,KAAK,CAAC,CACzD,CAAA,CAET,CAAC,CACL,CAEA,IAAa,EAAW,EAAY,EAAA,SAAS,EAChC,EAAY,EAAY,EAAA,UAAU,EAClC,EAAa,EAAY,EAAA,YAAa,KAAK,EAC3C,EAAW,EAAY,EAAA,SAAS,EAY7C,SAAS,EAAY,EAAe,EAAgB,EAA0B,CAC1E,OAAO,IAAI,KAAK,aAAa,EAAQ,CAAE,MAAO,WAAY,UAAS,CAAC,CAAC,CAAC,OAAO,EAAQ,GAAG,CAC5F,CAEA,SAAS,EAAW,EAAsB,CACtC,IAAM,EAAS,EAAK,QAAQ,MAAO,EAAE,EAErC,OADK,EACE,OAAO,SAAS,EAAQ,EAAE,EADb,CAExB,CAOA,IAAa,GAAA,EAAa,EAAA,WAAA,CAA8C,SACpE,CAAE,QAAO,WAAU,WAAW,MAAO,SAAS,QAAS,GAAG,GAC1D,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,KAAK,OACL,UAAU,UACV,MAAO,EAAY,GAAS,EAAG,EAAQ,CAAQ,EAC/C,SAAW,GAAU,EAAS,EAAW,EAAM,OAAO,KAAK,CAAC,CAC/D,CAAA,CAET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viacep.cjs","names":[],"sources":["../../src/forms/use-viacep.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface ViaCEPResult {\n cep: string;\n logradouro: string;\n complemento: string;\n bairro: string;\n localidade: string;\n uf: string;\n ibge?: string;\n gia?: string;\n ddd?: string;\n siafi?: string;\n}\n\nexport interface UseViaCEPResult {\n loading: boolean;\n error: string | null;\n data: ViaCEPResult | null;\n /** Fetch the given CEP (8 digits or masked). Sets `data`/`error`. Returns the result. */\n lookup: (cep: string) => Promise<ViaCEPResult | null>;\n reset: () => void;\n}\n\n/**\n * React hook for the public ViaCEP service (`viacep.com.br`). No backend\n * required. Returns address fields when the CEP exists, or sets `error` for\n * invalid CEPs.\n */\nexport function useViaCEP(): UseViaCEPResult {\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [data, setData] = useState<ViaCEPResult | null>(null);\n\n const lookup = useCallback(async (cep: string): Promise<ViaCEPResult | null> => {\n const digits = cep.replace(/\\D/g, \"\");\n if (digits.length !== 8) {\n setError(\"CEP inválido.\");\n return null;\n }\n setLoading(true);\n setError(null);\n try {\n const response = await fetch(`https://viacep.com.br/ws/${digits}/json/`);\n const json = (await response.json()) as ViaCEPResult & { erro?: boolean };\n if (\"erro\" in json && json.erro) {\n setError(\"CEP não encontrado.\");\n setData(null);\n return null;\n }\n setData(json);\n return json;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return null;\n } finally {\n setLoading(false);\n }\n }, []);\n\n const reset = useCallback(() => {\n setLoading(false);\n setError(null);\n setData(null);\n }, []);\n\n return { loading, error, data, lookup, reset };\n}\n"],"mappings":"uBA6BA,SAAgB,GAA6B,CACzC,GAAM,CAAC,EAAS,IAAA,
|
|
1
|
+
{"version":3,"file":"use-viacep.cjs","names":[],"sources":["../../src/forms/use-viacep.ts"],"sourcesContent":["import { useCallback, useState } from \"react\";\n\nexport interface ViaCEPResult {\n cep: string;\n logradouro: string;\n complemento: string;\n bairro: string;\n localidade: string;\n uf: string;\n ibge?: string;\n gia?: string;\n ddd?: string;\n siafi?: string;\n}\n\nexport interface UseViaCEPResult {\n loading: boolean;\n error: string | null;\n data: ViaCEPResult | null;\n /** Fetch the given CEP (8 digits or masked). Sets `data`/`error`. Returns the result. */\n lookup: (cep: string) => Promise<ViaCEPResult | null>;\n reset: () => void;\n}\n\n/**\n * React hook for the public ViaCEP service (`viacep.com.br`). No backend\n * required. Returns address fields when the CEP exists, or sets `error` for\n * invalid CEPs.\n */\nexport function useViaCEP(): UseViaCEPResult {\n const [loading, setLoading] = useState<boolean>(false);\n const [error, setError] = useState<string | null>(null);\n const [data, setData] = useState<ViaCEPResult | null>(null);\n\n const lookup = useCallback(async (cep: string): Promise<ViaCEPResult | null> => {\n const digits = cep.replace(/\\D/g, \"\");\n if (digits.length !== 8) {\n setError(\"CEP inválido.\");\n return null;\n }\n setLoading(true);\n setError(null);\n try {\n const response = await fetch(`https://viacep.com.br/ws/${digits}/json/`);\n const json = (await response.json()) as ViaCEPResult & { erro?: boolean };\n if (\"erro\" in json && json.erro) {\n setError(\"CEP não encontrado.\");\n setData(null);\n return null;\n }\n setData(json);\n return json;\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n return null;\n } finally {\n setLoading(false);\n }\n }, []);\n\n const reset = useCallback(() => {\n setLoading(false);\n setError(null);\n setData(null);\n }, []);\n\n return { loading, error, data, lookup, reset };\n}\n"],"mappings":"uBA6BA,SAAgB,GAA6B,CACzC,GAAM,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAkB,EAAK,EAC/C,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAwB,IAAI,EAChD,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAA8B,IAAI,EAkC1D,MAAO,CAAE,UAAS,QAAO,OAAM,QAAA,EAhChB,EAAA,YAAA,CAAY,KAAO,IAA8C,CAC5E,IAAM,EAAS,EAAI,QAAQ,MAAO,EAAE,EACpC,GAAI,EAAO,SAAW,EAElB,OADA,EAAS,eAAe,EACjB,KAEX,EAAW,EAAI,EACf,EAAS,IAAI,EACb,GAAI,CAEA,IAAM,EAAQ,MAAM,MADG,MAAM,4BAA4B,EAAO,OAAO,EAAA,CAC1C,KAAK,EAOlC,MANI,SAAU,GAAQ,EAAK,MACvB,EAAS,qBAAqB,EAC9B,EAAQ,IAAI,EACL,OAEX,EAAQ,CAAI,EACL,EACX,OAAS,EAAK,CAEV,OADA,EAAS,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,CAAC,EAClD,IACX,QAAU,CACN,EAAW,EAAK,CACpB,CACJ,EAAG,CAAC,CAQ2B,EAAQ,OAAA,EANzB,EAAA,YAAA,KAAkB,CAC5B,EAAW,EAAK,EAChB,EAAS,IAAI,EACb,EAAQ,IAAI,CAChB,EAAG,CAAC,CAEmC,CAAM,CACjD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-zod-form.cjs","names":[],"sources":["../../src/forms/use-zod-form.ts"],"sourcesContent":["import { useForm } from \"react-hook-form\";\nimport type { FieldValues, UseFormProps, UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { zodResolver } from \"./zod-resolver\";\n\n/**\n * Convenience wrapper around `react-hook-form`'s `useForm` that wires a zod\n * resolver and infers the form's value type from the schema.\n *\n * Both `react-hook-form` and `zod` are optional peer dependencies — install\n * them when your app uses forms.\n *\n * @example\n * const form = useZodForm(loginSchema, { defaultValues: { email: \"\" } });\n * form.register(\"email\");\n */\nexport function useZodForm<\n TSchema extends z.ZodTypeAny,\n TValues extends FieldValues = z.infer<TSchema> & FieldValues,\n>(schema: TSchema, options: Omit<UseFormProps<TValues>, \"resolver\"> = {}): UseFormReturn<TValues> {\n return useForm<TValues>({\n ...options,\n resolver: zodResolver(schema) as unknown as UseFormProps<TValues>[\"resolver\"],\n });\n}\n"],"mappings":"uEAgBA,SAAgB,EAGd,EAAiB,EAAmD,CAAC,EAA2B,CAC9F,OAAA,
|
|
1
|
+
{"version":3,"file":"use-zod-form.cjs","names":[],"sources":["../../src/forms/use-zod-form.ts"],"sourcesContent":["import { useForm } from \"react-hook-form\";\nimport type { FieldValues, UseFormProps, UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\nimport { zodResolver } from \"./zod-resolver\";\n\n/**\n * Convenience wrapper around `react-hook-form`'s `useForm` that wires a zod\n * resolver and infers the form's value type from the schema.\n *\n * Both `react-hook-form` and `zod` are optional peer dependencies — install\n * them when your app uses forms.\n *\n * @example\n * const form = useZodForm(loginSchema, { defaultValues: { email: \"\" } });\n * form.register(\"email\");\n */\nexport function useZodForm<\n TSchema extends z.ZodTypeAny,\n TValues extends FieldValues = z.infer<TSchema> & FieldValues,\n>(schema: TSchema, options: Omit<UseFormProps<TValues>, \"resolver\"> = {}): UseFormReturn<TValues> {\n return useForm<TValues>({\n ...options,\n resolver: zodResolver(schema) as unknown as UseFormProps<TValues>[\"resolver\"],\n });\n}\n"],"mappings":"uEAgBA,SAAgB,EAGd,EAAiB,EAAmD,CAAC,EAA2B,CAC9F,OAAA,EAAO,EAAA,QAAA,CAAiB,CACpB,GAAG,EACH,SAAU,EAAA,YAAY,CAAM,CAChC,CAAC,CACL"}
|