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":"EditableCell.cjs","names":[],"sources":["../../../src/components/DataTable/EditableCell.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an editable cell has to be announced\n * as well as rendered: columnLabel and rowNumber build the accessible name, labels\n * holds the button copy, and error/errorId wire the message to the input. The rest\n * is the edit lifecycle (editing, refocus, saving, onOpen, onCommit, onCancel) that\n * DataTable drives from outside.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport type { CellCommitMove, DataTableEditLabels } from \"./edit-labels\";\nimport styles from \"./DataTable.module.css\";\n\ninterface CellEditorProps {\n /** Text the editor opens with. */\n initial: string;\n /** `<input type>` for the editor. */\n type: string;\n /** Accessible name. */\n label: string;\n /** Id of the message element, when there is one. */\n describedBy?: string;\n /** Whether the current draft was rejected by validation. */\n invalid: boolean;\n /** Commit the draft. The parent decides whether the editor closes. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Discard the draft. */\n onCancel: () => void;\n}\n\n/**\n * The `<input>` half of an editable cell.\n *\n * A separate component so it **mounts fresh** every time a cell opens: the draft\n * then starts from the current value through `useState`'s initialiser, with no\n * effect syncing props into state and no render where the draft is stale.\n *\n * `Tab` is intercepted rather than left to the browser. Default tab order would\n * walk into the next row's trigger button, which is one keystroke away from the\n * next *editor* — and in a table the reader is editing, moving cell to cell is the\n * expected behaviour. `Escape` discards; `Enter` commits and closes; blur commits,\n * because clicking away from a half-typed cell and losing the typing is a data-loss\n * bug users report as \"the table ate my edit\".\n *\n * `autoFocus` is safe here in a way it is not on page load: the editor only exists\n * because the user just clicked the cell or pressed `Tab` into it, so focus is\n * following the interaction rather than stealing it.\n *\n * The `settled` flag stops a keyboard commit from being repeated by the blur that\n * follows it. Typing clears the flag again, because after a commit the parent\n * rejected — the only case where the editor is still open and focused — the draft is\n * live once more, and dropping *that* edit on blur would be the same data loss the\n * blur-commits rule exists to prevent.\n */\nfunction CellEditor({\n initial,\n type,\n label,\n describedBy,\n invalid,\n onCommit,\n onCancel,\n}: CellEditorProps) {\n const [draft, setDraft] = useState<string>(initial);\n const settled = useRef(false);\n\n function finish(move: CellCommitMove): void {\n settled.current = true;\n onCommit(draft, move);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finish(\"none\");\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n settled.current = true;\n onCancel();\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n finish(event.shiftKey ? \"previous\" : \"next\");\n }\n }\n\n return (\n <input\n autoFocus\n className={cn(styles.cellEditor, invalid && styles.cellEditorInvalid)}\n type={type}\n value={draft}\n aria-label={label}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n onChange={(event) => {\n settled.current = false;\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n if (settled.current) return;\n finish(\"none\");\n }}\n />\n );\n}\n\nexport interface EditableCellProps {\n /** Rendered value, used when the cell is closed. */\n children: ReactNode;\n /** Plain-text value, for the editor's initial draft and the trigger's name. */\n text: string;\n /** Header text of this column, for accessible names. */\n columnLabel: string;\n /** 1-based row number on the current page. */\n rowNumber: number;\n /** `<input type>` for the editor. */\n inputType: string;\n /** Whether this cell owns the open editor. */\n editing: boolean;\n /** Return focus to the trigger when the editor closes (Enter/Escape, not Tab). */\n refocus: boolean;\n /** A save is in flight for this cell. */\n saving: boolean;\n /** Validation or save error to surface, or null. */\n error: string | null;\n /** Stable id used for `aria-describedby` on the input and the trigger. */\n errorId: string;\n /** Copy for the affordances. */\n labels: DataTableEditLabels;\n /** Open the editor. */\n onOpen: () => void;\n /** Commit a draft. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Close without committing. */\n onCancel: () => void;\n}\n\n/**\n * One cell of a {@link DataTable} column marked `editable`.\n *\n * Closed, it is a button carrying the value — a plain `<td>` with a click handler\n * would be invisible to a keyboard and unnamed to a screen reader, and a\n * `tabIndex`-only cell announces no role. Open, it is an `<input>`.\n *\n * The button's name comes from its **contents**: a visually hidden \"Editar {coluna}:\"\n * in front of whatever the column rendered. An `aria-label` built from the raw value\n * would read \"850000\" over a cell showing `R$ 8.500,00`, which fails WCAG 2.5.3 (Label\n * in Name) and leaves voice control unable to address the cell by what it says.\n *\n * The error message is a `role=\"alert\"` tied to the input (and to the trigger once\n * the editor closes) through `aria-describedby`, so a rejected save is announced and\n * then still reachable — an optimistic update that rolls back silently leaves the\n * user believing the edit stuck.\n */\nexport function EditableCell({\n children,\n text,\n columnLabel,\n rowNumber,\n inputType,\n editing,\n refocus,\n saving,\n error,\n errorId,\n labels,\n onOpen,\n onCommit,\n onCancel,\n}: EditableCellProps) {\n const trigger = useRef<HTMLButtonElement | null>(null);\n const wasEditing = useRef(false);\n\n useEffect(() => {\n if (!editing && wasEditing.current && refocus) trigger.current?.focus();\n wasEditing.current = editing;\n }, [editing, refocus]);\n\n return (\n <span className={styles.cellWrap}>\n {editing ? (\n <CellEditor\n initial={text}\n type={inputType}\n label={labels.editor(columnLabel, rowNumber)}\n describedBy={error ? errorId : undefined}\n invalid={error !== null}\n onCommit={onCommit}\n onCancel={onCancel}\n />\n ) : (\n <button\n ref={trigger}\n type=\"button\"\n className={cn(styles.cellButton, error && styles.cellButtonInvalid)}\n aria-describedby={error ? errorId : undefined}\n aria-busy={saving || undefined}\n onClick={onOpen}\n >\n <VisuallyHidden>{labels.editCell(columnLabel)}</VisuallyHidden> {children}\n </button>\n )}\n {error && (\n <span className={styles.cellError} id={errorId} role=\"alert\">\n {error}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"kLAuDA,SAAS,EAAW,CAChB,UACA,OACA,QACA,cACA,UACA,WACA,YACgB,CAChB,GAAM,CAAC,EAAO,IAAA,
|
|
1
|
+
{"version":3,"file":"EditableCell.cjs","names":[],"sources":["../../../src/components/DataTable/EditableCell.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an editable cell has to be announced\n * as well as rendered: columnLabel and rowNumber build the accessible name, labels\n * holds the button copy, and error/errorId wire the message to the input. The rest\n * is the edit lifecycle (editing, refocus, saving, onOpen, onCommit, onCancel) that\n * DataTable drives from outside.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport type { CellCommitMove, DataTableEditLabels } from \"./edit-labels\";\nimport styles from \"./DataTable.module.css\";\n\ninterface CellEditorProps {\n /** Text the editor opens with. */\n initial: string;\n /** `<input type>` for the editor. */\n type: string;\n /** Accessible name. */\n label: string;\n /** Id of the message element, when there is one. */\n describedBy?: string;\n /** Whether the current draft was rejected by validation. */\n invalid: boolean;\n /** Commit the draft. The parent decides whether the editor closes. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Discard the draft. */\n onCancel: () => void;\n}\n\n/**\n * The `<input>` half of an editable cell.\n *\n * A separate component so it **mounts fresh** every time a cell opens: the draft\n * then starts from the current value through `useState`'s initialiser, with no\n * effect syncing props into state and no render where the draft is stale.\n *\n * `Tab` is intercepted rather than left to the browser. Default tab order would\n * walk into the next row's trigger button, which is one keystroke away from the\n * next *editor* — and in a table the reader is editing, moving cell to cell is the\n * expected behaviour. `Escape` discards; `Enter` commits and closes; blur commits,\n * because clicking away from a half-typed cell and losing the typing is a data-loss\n * bug users report as \"the table ate my edit\".\n *\n * `autoFocus` is safe here in a way it is not on page load: the editor only exists\n * because the user just clicked the cell or pressed `Tab` into it, so focus is\n * following the interaction rather than stealing it.\n *\n * The `settled` flag stops a keyboard commit from being repeated by the blur that\n * follows it. Typing clears the flag again, because after a commit the parent\n * rejected — the only case where the editor is still open and focused — the draft is\n * live once more, and dropping *that* edit on blur would be the same data loss the\n * blur-commits rule exists to prevent.\n */\nfunction CellEditor({\n initial,\n type,\n label,\n describedBy,\n invalid,\n onCommit,\n onCancel,\n}: CellEditorProps) {\n const [draft, setDraft] = useState<string>(initial);\n const settled = useRef(false);\n\n function finish(move: CellCommitMove): void {\n settled.current = true;\n onCommit(draft, move);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finish(\"none\");\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n settled.current = true;\n onCancel();\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n finish(event.shiftKey ? \"previous\" : \"next\");\n }\n }\n\n return (\n <input\n autoFocus\n className={cn(styles.cellEditor, invalid && styles.cellEditorInvalid)}\n type={type}\n value={draft}\n aria-label={label}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n onChange={(event) => {\n settled.current = false;\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n if (settled.current) return;\n finish(\"none\");\n }}\n />\n );\n}\n\nexport interface EditableCellProps {\n /** Rendered value, used when the cell is closed. */\n children: ReactNode;\n /** Plain-text value, for the editor's initial draft and the trigger's name. */\n text: string;\n /** Header text of this column, for accessible names. */\n columnLabel: string;\n /** 1-based row number on the current page. */\n rowNumber: number;\n /** `<input type>` for the editor. */\n inputType: string;\n /** Whether this cell owns the open editor. */\n editing: boolean;\n /** Return focus to the trigger when the editor closes (Enter/Escape, not Tab). */\n refocus: boolean;\n /** A save is in flight for this cell. */\n saving: boolean;\n /** Validation or save error to surface, or null. */\n error: string | null;\n /** Stable id used for `aria-describedby` on the input and the trigger. */\n errorId: string;\n /** Copy for the affordances. */\n labels: DataTableEditLabels;\n /** Open the editor. */\n onOpen: () => void;\n /** Commit a draft. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Close without committing. */\n onCancel: () => void;\n}\n\n/**\n * One cell of a {@link DataTable} column marked `editable`.\n *\n * Closed, it is a button carrying the value — a plain `<td>` with a click handler\n * would be invisible to a keyboard and unnamed to a screen reader, and a\n * `tabIndex`-only cell announces no role. Open, it is an `<input>`.\n *\n * The button's name comes from its **contents**: a visually hidden \"Editar {coluna}:\"\n * in front of whatever the column rendered. An `aria-label` built from the raw value\n * would read \"850000\" over a cell showing `R$ 8.500,00`, which fails WCAG 2.5.3 (Label\n * in Name) and leaves voice control unable to address the cell by what it says.\n *\n * The error message is a `role=\"alert\"` tied to the input (and to the trigger once\n * the editor closes) through `aria-describedby`, so a rejected save is announced and\n * then still reachable — an optimistic update that rolls back silently leaves the\n * user believing the edit stuck.\n */\nexport function EditableCell({\n children,\n text,\n columnLabel,\n rowNumber,\n inputType,\n editing,\n refocus,\n saving,\n error,\n errorId,\n labels,\n onOpen,\n onCommit,\n onCancel,\n}: EditableCellProps) {\n const trigger = useRef<HTMLButtonElement | null>(null);\n const wasEditing = useRef(false);\n\n useEffect(() => {\n if (!editing && wasEditing.current && refocus) trigger.current?.focus();\n wasEditing.current = editing;\n }, [editing, refocus]);\n\n return (\n <span className={styles.cellWrap}>\n {editing ? (\n <CellEditor\n initial={text}\n type={inputType}\n label={labels.editor(columnLabel, rowNumber)}\n describedBy={error ? errorId : undefined}\n invalid={error !== null}\n onCommit={onCommit}\n onCancel={onCancel}\n />\n ) : (\n <button\n ref={trigger}\n type=\"button\"\n className={cn(styles.cellButton, error && styles.cellButtonInvalid)}\n aria-describedby={error ? errorId : undefined}\n aria-busy={saving || undefined}\n onClick={onOpen}\n >\n <VisuallyHidden>{labels.editCell(columnLabel)}</VisuallyHidden> {children}\n </button>\n )}\n {error && (\n <span className={styles.cellError} id={errorId} role=\"alert\">\n {error}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"kLAuDA,SAAS,EAAW,CAChB,UACA,OACA,QACA,cACA,UACA,WACA,YACgB,CAChB,GAAM,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAiB,CAAO,EAC5C,GAAA,EAAU,EAAA,OAAA,CAAO,EAAK,EAE5B,SAAS,EAAO,EAA4B,CACxC,EAAQ,QAAU,GAClB,EAAS,EAAO,CAAI,CACxB,CAEA,SAAS,EAAc,EAA8C,CACjE,GAAI,EAAM,MAAQ,QAAS,CACvB,EAAM,eAAe,EACrB,EAAO,MAAM,EACb,MACJ,CACA,GAAI,EAAM,MAAQ,SAAU,CACxB,EAAM,eAAe,EACrB,EAAQ,QAAU,GAClB,EAAS,EACT,MACJ,CACI,EAAM,MAAQ,QACd,EAAM,eAAe,EACrB,EAAO,EAAM,SAAW,WAAa,MAAM,EAEnD,CAEA,OACI,EAAA,EAAA,IAAA,CAAC,QAAD,CACI,UAAA,GACA,UAAW,EAAA,GAAG,EAAA,QAAO,WAAY,GAAW,EAAA,QAAO,iBAAiB,EAC9D,OACN,MAAO,EACP,aAAY,EACZ,eAAc,GAAW,IAAA,GACzB,mBAAkB,EAClB,SAAW,GAAU,CACjB,EAAQ,QAAU,GAClB,EAAS,EAAM,OAAO,KAAK,CAC/B,EACA,UAAW,EACX,WAAc,CACN,EAAQ,SACZ,EAAO,MAAM,CACjB,CACH,CAAA,CAET,CAkDA,SAAgB,EAAa,CACzB,WACA,OACA,cACA,YACA,YACA,UACA,UACA,SACA,QACA,UACA,SACA,SACA,WACA,YACkB,CAClB,IAAM,GAAA,EAAU,EAAA,OAAA,CAAiC,IAAI,EAC/C,GAAA,EAAa,EAAA,OAAA,CAAO,EAAK,EAO/B,OALA,EAAA,EAAA,UAAA,KAAgB,CACR,CAAC,GAAW,EAAW,SAAW,GAAS,EAAQ,SAAS,MAAM,EACtE,EAAW,QAAU,CACzB,EAAG,CAAC,EAAS,CAAO,CAAC,GAGjB,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,SAAxB,SAAA,CACK,GACG,EAAA,EAAA,IAAA,CAAC,EAAD,CACI,QAAS,EACT,KAAM,EACN,MAAO,EAAO,OAAO,EAAa,CAAS,EAC3C,YAAa,EAAQ,EAAU,IAAA,GAC/B,QAAS,IAAU,KACT,WACA,UACb,CAAA,GAED,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,IAAK,EACL,KAAK,SACL,UAAW,EAAA,GAAG,EAAA,QAAO,WAAY,GAAS,EAAA,QAAO,iBAAiB,EAClE,mBAAkB,EAAQ,EAAU,IAAA,GACpC,YAAW,GAAU,IAAA,GACrB,QAAS,EANb,SAAA,EAQI,EAAA,EAAA,IAAA,CAAC,EAAA,eAAD,CAAA,SAAiB,EAAO,SAAS,CAAW,CAAkB,CAAA,EAAC,IAAE,CAC7D,CAEX,CAAA,EAAA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,UAAW,GAAI,EAAS,KAAK,QAChD,SAAA,CACC,CAAA,CAER,GAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditableCell.js","names":[],"sources":["../../../src/components/DataTable/EditableCell.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an editable cell has to be announced\n * as well as rendered: columnLabel and rowNumber build the accessible name, labels\n * holds the button copy, and error/errorId wire the message to the input. The rest\n * is the edit lifecycle (editing, refocus, saving, onOpen, onCommit, onCancel) that\n * DataTable drives from outside.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport type { CellCommitMove, DataTableEditLabels } from \"./edit-labels\";\nimport styles from \"./DataTable.module.css\";\n\ninterface CellEditorProps {\n /** Text the editor opens with. */\n initial: string;\n /** `<input type>` for the editor. */\n type: string;\n /** Accessible name. */\n label: string;\n /** Id of the message element, when there is one. */\n describedBy?: string;\n /** Whether the current draft was rejected by validation. */\n invalid: boolean;\n /** Commit the draft. The parent decides whether the editor closes. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Discard the draft. */\n onCancel: () => void;\n}\n\n/**\n * The `<input>` half of an editable cell.\n *\n * A separate component so it **mounts fresh** every time a cell opens: the draft\n * then starts from the current value through `useState`'s initialiser, with no\n * effect syncing props into state and no render where the draft is stale.\n *\n * `Tab` is intercepted rather than left to the browser. Default tab order would\n * walk into the next row's trigger button, which is one keystroke away from the\n * next *editor* — and in a table the reader is editing, moving cell to cell is the\n * expected behaviour. `Escape` discards; `Enter` commits and closes; blur commits,\n * because clicking away from a half-typed cell and losing the typing is a data-loss\n * bug users report as \"the table ate my edit\".\n *\n * `autoFocus` is safe here in a way it is not on page load: the editor only exists\n * because the user just clicked the cell or pressed `Tab` into it, so focus is\n * following the interaction rather than stealing it.\n *\n * The `settled` flag stops a keyboard commit from being repeated by the blur that\n * follows it. Typing clears the flag again, because after a commit the parent\n * rejected — the only case where the editor is still open and focused — the draft is\n * live once more, and dropping *that* edit on blur would be the same data loss the\n * blur-commits rule exists to prevent.\n */\nfunction CellEditor({\n initial,\n type,\n label,\n describedBy,\n invalid,\n onCommit,\n onCancel,\n}: CellEditorProps) {\n const [draft, setDraft] = useState<string>(initial);\n const settled = useRef(false);\n\n function finish(move: CellCommitMove): void {\n settled.current = true;\n onCommit(draft, move);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finish(\"none\");\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n settled.current = true;\n onCancel();\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n finish(event.shiftKey ? \"previous\" : \"next\");\n }\n }\n\n return (\n <input\n autoFocus\n className={cn(styles.cellEditor, invalid && styles.cellEditorInvalid)}\n type={type}\n value={draft}\n aria-label={label}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n onChange={(event) => {\n settled.current = false;\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n if (settled.current) return;\n finish(\"none\");\n }}\n />\n );\n}\n\nexport interface EditableCellProps {\n /** Rendered value, used when the cell is closed. */\n children: ReactNode;\n /** Plain-text value, for the editor's initial draft and the trigger's name. */\n text: string;\n /** Header text of this column, for accessible names. */\n columnLabel: string;\n /** 1-based row number on the current page. */\n rowNumber: number;\n /** `<input type>` for the editor. */\n inputType: string;\n /** Whether this cell owns the open editor. */\n editing: boolean;\n /** Return focus to the trigger when the editor closes (Enter/Escape, not Tab). */\n refocus: boolean;\n /** A save is in flight for this cell. */\n saving: boolean;\n /** Validation or save error to surface, or null. */\n error: string | null;\n /** Stable id used for `aria-describedby` on the input and the trigger. */\n errorId: string;\n /** Copy for the affordances. */\n labels: DataTableEditLabels;\n /** Open the editor. */\n onOpen: () => void;\n /** Commit a draft. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Close without committing. */\n onCancel: () => void;\n}\n\n/**\n * One cell of a {@link DataTable} column marked `editable`.\n *\n * Closed, it is a button carrying the value — a plain `<td>` with a click handler\n * would be invisible to a keyboard and unnamed to a screen reader, and a\n * `tabIndex`-only cell announces no role. Open, it is an `<input>`.\n *\n * The button's name comes from its **contents**: a visually hidden \"Editar {coluna}:\"\n * in front of whatever the column rendered. An `aria-label` built from the raw value\n * would read \"850000\" over a cell showing `R$ 8.500,00`, which fails WCAG 2.5.3 (Label\n * in Name) and leaves voice control unable to address the cell by what it says.\n *\n * The error message is a `role=\"alert\"` tied to the input (and to the trigger once\n * the editor closes) through `aria-describedby`, so a rejected save is announced and\n * then still reachable — an optimistic update that rolls back silently leaves the\n * user believing the edit stuck.\n */\nexport function EditableCell({\n children,\n text,\n columnLabel,\n rowNumber,\n inputType,\n editing,\n refocus,\n saving,\n error,\n errorId,\n labels,\n onOpen,\n onCommit,\n onCancel,\n}: EditableCellProps) {\n const trigger = useRef<HTMLButtonElement | null>(null);\n const wasEditing = useRef(false);\n\n useEffect(() => {\n if (!editing && wasEditing.current && refocus) trigger.current?.focus();\n wasEditing.current = editing;\n }, [editing, refocus]);\n\n return (\n <span className={styles.cellWrap}>\n {editing ? (\n <CellEditor\n initial={text}\n type={inputType}\n label={labels.editor(columnLabel, rowNumber)}\n describedBy={error ? errorId : undefined}\n invalid={error !== null}\n onCommit={onCommit}\n onCancel={onCancel}\n />\n ) : (\n <button\n ref={trigger}\n type=\"button\"\n className={cn(styles.cellButton, error && styles.cellButtonInvalid)}\n aria-describedby={error ? errorId : undefined}\n aria-busy={saving || undefined}\n onClick={onOpen}\n >\n <VisuallyHidden>{labels.editCell(columnLabel)}</VisuallyHidden> {children}\n </button>\n )}\n {error && (\n <span className={styles.cellError} id={errorId} role=\"alert\">\n {error}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;;AAuDA,SAAS,EAAW,EAChB,YACA,SACA,UACA,gBACA,YACA,aACA,eACgB;CAChB,IAAM,CAAC,GAAO,KAAY,EAAiB,CAAO,GAC5C,IAAU,EAAO,EAAK;CAE5B,SAAS,EAAO,GAA4B;EAExC,AADA,EAAQ,UAAU,IAClB,EAAS,GAAO,CAAI;CACxB;CAEA,SAAS,EAAc,GAA8C;EACjE,IAAI,EAAM,QAAQ,SAAS;GAEvB,AADA,EAAM,eAAe,GACrB,EAAO,MAAM;GACb;EACJ;EACA,IAAI,EAAM,QAAQ,UAAU;GAGxB,AAFA,EAAM,eAAe,GACrB,EAAQ,UAAU,IAClB,EAAS;GACT;EACJ;EACA,AAAI,EAAM,QAAQ,UACd,EAAM,eAAe,GACrB,EAAO,EAAM,WAAW,aAAa,MAAM;CAEnD;CAEA,OACI,kBAAC,SAAD;EACI,WAAA;EACA,WAAW,EAAG,EAAO,YAAY,KAAW,EAAO,iBAAiB;EAC9D;EACN,OAAO;EACP,cAAY;EACZ,gBAAc,KAAW,KAAA;EACzB,oBAAkB;EAClB,WAAW,MAAU;GAEjB,AADA,EAAQ,UAAU,IAClB,EAAS,EAAM,OAAO,KAAK;EAC/B;EACA,WAAW;EACX,cAAc;GACN,EAAQ,WACZ,EAAO,MAAM;EACjB;CACH,CAAA;AAET;AAkDA,SAAgB,EAAa,EACzB,aACA,SACA,gBACA,cACA,cACA,YACA,YACA,WACA,UACA,YACA,WACA,WACA,aACA,eACkB;CAClB,IAAM,IAAU,EAAiC,IAAI,GAC/C,IAAa,EAAO,EAAK;CAO/B,OALA,QAAgB;EAEZ,AADI,CAAC,KAAW,EAAW,WAAW,KAAS,EAAQ,SAAS,MAAM,GACtE,EAAW,UAAU;CACzB,GAAG,CAAC,GAAS,CAAO,CAAC,GAGjB,kBAAC,QAAD;EAAM,WAAW,EAAO;
|
|
1
|
+
{"version":3,"file":"EditableCell.js","names":[],"sources":["../../../src/components/DataTable/EditableCell.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — an editable cell has to be announced\n * as well as rendered: columnLabel and rowNumber build the accessible name, labels\n * holds the button copy, and error/errorId wire the message to the input. The rest\n * is the edit lifecycle (editing, refocus, saving, onOpen, onCommit, onCancel) that\n * DataTable drives from outside.\n */\nimport { useEffect, useRef, useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport type { CellCommitMove, DataTableEditLabels } from \"./edit-labels\";\nimport styles from \"./DataTable.module.css\";\n\ninterface CellEditorProps {\n /** Text the editor opens with. */\n initial: string;\n /** `<input type>` for the editor. */\n type: string;\n /** Accessible name. */\n label: string;\n /** Id of the message element, when there is one. */\n describedBy?: string;\n /** Whether the current draft was rejected by validation. */\n invalid: boolean;\n /** Commit the draft. The parent decides whether the editor closes. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Discard the draft. */\n onCancel: () => void;\n}\n\n/**\n * The `<input>` half of an editable cell.\n *\n * A separate component so it **mounts fresh** every time a cell opens: the draft\n * then starts from the current value through `useState`'s initialiser, with no\n * effect syncing props into state and no render where the draft is stale.\n *\n * `Tab` is intercepted rather than left to the browser. Default tab order would\n * walk into the next row's trigger button, which is one keystroke away from the\n * next *editor* — and in a table the reader is editing, moving cell to cell is the\n * expected behaviour. `Escape` discards; `Enter` commits and closes; blur commits,\n * because clicking away from a half-typed cell and losing the typing is a data-loss\n * bug users report as \"the table ate my edit\".\n *\n * `autoFocus` is safe here in a way it is not on page load: the editor only exists\n * because the user just clicked the cell or pressed `Tab` into it, so focus is\n * following the interaction rather than stealing it.\n *\n * The `settled` flag stops a keyboard commit from being repeated by the blur that\n * follows it. Typing clears the flag again, because after a commit the parent\n * rejected — the only case where the editor is still open and focused — the draft is\n * live once more, and dropping *that* edit on blur would be the same data loss the\n * blur-commits rule exists to prevent.\n */\nfunction CellEditor({\n initial,\n type,\n label,\n describedBy,\n invalid,\n onCommit,\n onCancel,\n}: CellEditorProps) {\n const [draft, setDraft] = useState<string>(initial);\n const settled = useRef(false);\n\n function finish(move: CellCommitMove): void {\n settled.current = true;\n onCommit(draft, move);\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Enter\") {\n event.preventDefault();\n finish(\"none\");\n return;\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n settled.current = true;\n onCancel();\n return;\n }\n if (event.key === \"Tab\") {\n event.preventDefault();\n finish(event.shiftKey ? \"previous\" : \"next\");\n }\n }\n\n return (\n <input\n autoFocus\n className={cn(styles.cellEditor, invalid && styles.cellEditorInvalid)}\n type={type}\n value={draft}\n aria-label={label}\n aria-invalid={invalid || undefined}\n aria-describedby={describedBy}\n onChange={(event) => {\n settled.current = false;\n setDraft(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n onBlur={() => {\n if (settled.current) return;\n finish(\"none\");\n }}\n />\n );\n}\n\nexport interface EditableCellProps {\n /** Rendered value, used when the cell is closed. */\n children: ReactNode;\n /** Plain-text value, for the editor's initial draft and the trigger's name. */\n text: string;\n /** Header text of this column, for accessible names. */\n columnLabel: string;\n /** 1-based row number on the current page. */\n rowNumber: number;\n /** `<input type>` for the editor. */\n inputType: string;\n /** Whether this cell owns the open editor. */\n editing: boolean;\n /** Return focus to the trigger when the editor closes (Enter/Escape, not Tab). */\n refocus: boolean;\n /** A save is in flight for this cell. */\n saving: boolean;\n /** Validation or save error to surface, or null. */\n error: string | null;\n /** Stable id used for `aria-describedby` on the input and the trigger. */\n errorId: string;\n /** Copy for the affordances. */\n labels: DataTableEditLabels;\n /** Open the editor. */\n onOpen: () => void;\n /** Commit a draft. */\n onCommit: (raw: string, move: CellCommitMove) => void;\n /** Close without committing. */\n onCancel: () => void;\n}\n\n/**\n * One cell of a {@link DataTable} column marked `editable`.\n *\n * Closed, it is a button carrying the value — a plain `<td>` with a click handler\n * would be invisible to a keyboard and unnamed to a screen reader, and a\n * `tabIndex`-only cell announces no role. Open, it is an `<input>`.\n *\n * The button's name comes from its **contents**: a visually hidden \"Editar {coluna}:\"\n * in front of whatever the column rendered. An `aria-label` built from the raw value\n * would read \"850000\" over a cell showing `R$ 8.500,00`, which fails WCAG 2.5.3 (Label\n * in Name) and leaves voice control unable to address the cell by what it says.\n *\n * The error message is a `role=\"alert\"` tied to the input (and to the trigger once\n * the editor closes) through `aria-describedby`, so a rejected save is announced and\n * then still reachable — an optimistic update that rolls back silently leaves the\n * user believing the edit stuck.\n */\nexport function EditableCell({\n children,\n text,\n columnLabel,\n rowNumber,\n inputType,\n editing,\n refocus,\n saving,\n error,\n errorId,\n labels,\n onOpen,\n onCommit,\n onCancel,\n}: EditableCellProps) {\n const trigger = useRef<HTMLButtonElement | null>(null);\n const wasEditing = useRef(false);\n\n useEffect(() => {\n if (!editing && wasEditing.current && refocus) trigger.current?.focus();\n wasEditing.current = editing;\n }, [editing, refocus]);\n\n return (\n <span className={styles.cellWrap}>\n {editing ? (\n <CellEditor\n initial={text}\n type={inputType}\n label={labels.editor(columnLabel, rowNumber)}\n describedBy={error ? errorId : undefined}\n invalid={error !== null}\n onCommit={onCommit}\n onCancel={onCancel}\n />\n ) : (\n <button\n ref={trigger}\n type=\"button\"\n className={cn(styles.cellButton, error && styles.cellButtonInvalid)}\n aria-describedby={error ? errorId : undefined}\n aria-busy={saving || undefined}\n onClick={onOpen}\n >\n <VisuallyHidden>{labels.editCell(columnLabel)}</VisuallyHidden> {children}\n </button>\n )}\n {error && (\n <span className={styles.cellError} id={errorId} role=\"alert\">\n {error}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;;AAuDA,SAAS,EAAW,EAChB,YACA,SACA,UACA,gBACA,YACA,aACA,eACgB;CAChB,IAAM,CAAC,GAAO,KAAY,EAAiB,CAAO,GAC5C,IAAU,EAAO,EAAK;CAE5B,SAAS,EAAO,GAA4B;EAExC,AADA,EAAQ,UAAU,IAClB,EAAS,GAAO,CAAI;CACxB;CAEA,SAAS,EAAc,GAA8C;EACjE,IAAI,EAAM,QAAQ,SAAS;GAEvB,AADA,EAAM,eAAe,GACrB,EAAO,MAAM;GACb;EACJ;EACA,IAAI,EAAM,QAAQ,UAAU;GAGxB,AAFA,EAAM,eAAe,GACrB,EAAQ,UAAU,IAClB,EAAS;GACT;EACJ;EACA,AAAI,EAAM,QAAQ,UACd,EAAM,eAAe,GACrB,EAAO,EAAM,WAAW,aAAa,MAAM;CAEnD;CAEA,OACI,kBAAC,SAAD;EACI,WAAA;EACA,WAAW,EAAG,EAAO,YAAY,KAAW,EAAO,iBAAiB;EAC9D;EACN,OAAO;EACP,cAAY;EACZ,gBAAc,KAAW,KAAA;EACzB,oBAAkB;EAClB,WAAW,MAAU;GAEjB,AADA,EAAQ,UAAU,IAClB,EAAS,EAAM,OAAO,KAAK;EAC/B;EACA,WAAW;EACX,cAAc;GACN,EAAQ,WACZ,EAAO,MAAM;EACjB;CACH,CAAA;AAET;AAkDA,SAAgB,EAAa,EACzB,aACA,SACA,gBACA,cACA,cACA,YACA,YACA,WACA,UACA,YACA,WACA,WACA,aACA,eACkB;CAClB,IAAM,IAAU,EAAiC,IAAI,GAC/C,IAAa,EAAO,EAAK;CAO/B,OALA,QAAgB;EAEZ,AADI,CAAC,KAAW,EAAW,WAAW,KAAS,EAAQ,SAAS,MAAM,GACtE,EAAW,UAAU;CACzB,GAAG,CAAC,GAAS,CAAO,CAAC,GAGjB,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAxB,UAAA,CACK,IACG,kBAAC,GAAD;GACI,SAAS;GACT,MAAM;GACN,OAAO,EAAO,OAAO,GAAa,CAAS;GAC3C,aAAa,IAAQ,IAAU,KAAA;GAC/B,SAAS,MAAU;GACT;GACA;EACb,CAAA,IAED,kBAAC,UAAD;GACI,KAAK;GACL,MAAK;GACL,WAAW,EAAG,EAAO,YAAY,KAAS,EAAO,iBAAiB;GAClE,oBAAkB,IAAQ,IAAU,KAAA;GACpC,aAAW,KAAU,KAAA;GACrB,SAAS;GANb,UAAA;IAQI,kBAAC,GAAD,EAAA,UAAiB,EAAO,SAAS,CAAW,EAAkB,CAAA;IAAC;IAAE;GAC7D;EAEX,CAAA,GAAA,KACG,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAW,IAAI;GAAS,MAAK;GAChD,UAAA;EACC,CAAA,CAER;;AAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.cjs","names":[],"sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — label, helperText and error are the three states of\n * one message slot that every input in this SDK shares; removing them pushes the\n * scaffolding back into the app. DatePicker adds min, max and mode on top, which are\n * the field's own semantics.\n */\nimport { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input } from \"@/components/Input\";\n\nexport interface DatePickerProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"value\" | \"onChange\" | \"size\"\n> {\n /** ISO date string (`YYYY-MM-DD`) or empty. */\n value: string;\n onChange: (value: string) => void;\n label?: string;\n helperText?: string;\n error?: string;\n /** Lower bound (`YYYY-MM-DD`). */\n min?: string;\n /** Upper bound (`YYYY-MM-DD`). */\n max?: string;\n /** Mode. `date` (default), `datetime-local`, `time`, or `month`. */\n mode?: \"date\" | \"datetime-local\" | \"time\" | \"month\";\n wrapperClassName?: string;\n}\n\n/**\n * Thin wrapper around the native `<input type=\"date\">` (or `datetime-local`,\n * `time`, `month`). For richer pickers, pair with `react-datepicker` directly\n * — this primitive keeps the SDK dep-free.\n */\nexport const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(function DatePicker(\n { value, onChange, mode = \"date\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type={mode}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n />\n );\n});\n"],"mappings":"4FAkCA,IAAa,GAAA,
|
|
1
|
+
{"version":3,"file":"DatePicker.cjs","names":[],"sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — label, helperText and error are the three states of\n * one message slot that every input in this SDK shares; removing them pushes the\n * scaffolding back into the app. DatePicker adds min, max and mode on top, which are\n * the field's own semantics.\n */\nimport { forwardRef } from \"react\";\nimport type { InputHTMLAttributes } from \"react\";\nimport { Input } from \"@/components/Input\";\n\nexport interface DatePickerProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"value\" | \"onChange\" | \"size\"\n> {\n /** ISO date string (`YYYY-MM-DD`) or empty. */\n value: string;\n onChange: (value: string) => void;\n label?: string;\n helperText?: string;\n error?: string;\n /** Lower bound (`YYYY-MM-DD`). */\n min?: string;\n /** Upper bound (`YYYY-MM-DD`). */\n max?: string;\n /** Mode. `date` (default), `datetime-local`, `time`, or `month`. */\n mode?: \"date\" | \"datetime-local\" | \"time\" | \"month\";\n wrapperClassName?: string;\n}\n\n/**\n * Thin wrapper around the native `<input type=\"date\">` (or `datetime-local`,\n * `time`, `month`). For richer pickers, pair with `react-datepicker` directly\n * — this primitive keeps the SDK dep-free.\n */\nexport const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(function DatePicker(\n { value, onChange, mode = \"date\", ...props },\n ref,\n) {\n return (\n <Input\n {...props}\n ref={ref}\n type={mode}\n value={value}\n onChange={(event) => onChange(event.target.value)}\n />\n );\n});\n"],"mappings":"4FAkCA,IAAa,GAAA,EAAa,EAAA,WAAA,CAA8C,SACpE,CAAE,QAAO,WAAU,OAAO,OAAQ,GAAG,GACrC,EACF,CACE,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,MAAD,CACI,GAAI,EACC,MACL,KAAM,EACC,QACP,SAAW,GAAU,EAAS,EAAM,OAAO,KAAK,CACnD,CAAA,CAET,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateRangePicker.cjs","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a range is two dates whose validity\n * depends on each other, so the body keeps both ends, the hovered preview end and\n * the open panel in one place; splitting the halves would mean each one reaching\n * into the other's state to clamp itself.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DateRangePicker.module.css\";\n\nexport type WeekStart = 0 | 1;\n\n/** A selected range. Either bound may be `null` while picking. */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\nexport interface DateRangePickerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled selected range. */\n value: DateRange;\n /** Called with the new range as the user picks start then end. */\n onChange: (range: DateRange) => void;\n /** How many month grids to show side by side. Default `2`. */\n numberOfMonths?: number;\n /** Initial visible month (uncontrolled). Defaults to the range start or today. */\n defaultMonth?: Date;\n minDate?: Date;\n maxDate?: Date;\n /** First column of the week — `0` Sunday (default) or `1` Monday. */\n weekStartsOn?: WeekStart;\n}\n\nconst WEEKDAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst MONTH_LABELS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\nconst DAYS_IN_GRID = 42;\n\nfunction makeDate(year: number, month: number, day: number): Date {\n return new Date(year, month, day);\n}\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction addMonths(date: Date, offset: number): Date {\n return makeDate(date.getFullYear(), date.getMonth() + offset, 1);\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nfunction isOutOfRange(date: Date, minDate?: Date, maxDate?: Date): boolean {\n const day = startOfDay(date).getTime();\n if (minDate && day < startOfDay(minDate).getTime()) return true;\n if (maxDate && day > startOfDay(maxDate).getTime()) return true;\n return false;\n}\n\n/** Inclusive-exclusive-agnostic \"strictly between a and b\" (a/b order-independent). */\nfunction isBetween(date: Date, a: Date, b: Date): boolean {\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(a).getTime(), startOfDay(b).getTime());\n const hi = Math.max(startOfDay(a).getTime(), startOfDay(b).getTime());\n return t > lo && t < hi;\n}\n\nfunction buildGrid(month: Date, weekStartsOn: WeekStart): Date[] {\n const year = month.getFullYear();\n const monthIndex = month.getMonth();\n const firstWeekday = makeDate(year, monthIndex, 1).getDay();\n const leading = (firstWeekday - weekStartsOn + 7) % 7;\n const start = makeDate(year, monthIndex, 1 - leading);\n return Array.from({ length: DAYS_IN_GRID }, (_, i) =>\n makeDate(start.getFullYear(), start.getMonth(), start.getDate() + i),\n );\n}\n\n/**\n * Date-range picker: pick a start day, then an end day, across one or more\n * month grids. The hovered day previews the range before the end is committed.\n * Pure `Date` math — no date libraries. For a single date, use `Calendar`.\n *\n * Selection: first click sets `start` (clears `end`); the next click sets `end`\n * (auto-ordered if you click earlier than the start); a third click starts over.\n */\nexport function DateRangePicker({\n value,\n onChange,\n numberOfMonths = 2,\n defaultMonth,\n minDate,\n maxDate,\n weekStartsOn = 0,\n className,\n ...props\n}: DateRangePickerProps) {\n const [visibleMonth, setVisibleMonth] = useState<Date>(() => {\n const base = defaultMonth ?? value.start ?? new Date();\n return makeDate(base.getFullYear(), base.getMonth(), 1);\n });\n const [hovered, setHovered] = useState<Date | null>(null);\n\n const today = useMemo(() => startOfDay(new Date()), []);\n const weekdayLabels = useMemo(\n () => Array.from({ length: 7 }, (_, i) => WEEKDAY_LABELS[(i + weekStartsOn) % 7]),\n [weekStartsOn],\n );\n\n const selectDate = useCallback(\n (date: Date): void => {\n if (isOutOfRange(date, minDate, maxDate)) return;\n const { start, end } = value;\n if (!start || end) {\n onChange({ start: date, end: null });\n } else if (startOfDay(date).getTime() < startOfDay(start).getTime()) {\n onChange({ start: date, end: start });\n } else {\n onChange({ start, end: date });\n }\n },\n [value, onChange, minDate, maxDate],\n );\n\n const changeMonth = useCallback((offset: number): void => {\n setVisibleMonth((current) => addMonths(current, offset));\n }, []);\n\n // Effective end used for preview highlighting (hovered day while picking).\n const previewEnd = value.start && !value.end ? hovered : value.end;\n\n function dayState(date: Date): {\n selected: boolean;\n inRange: boolean;\n isStart: boolean;\n isEnd: boolean;\n } {\n const { start } = value;\n const end = previewEnd;\n const isStart = start ? isSameDay(date, start) : false;\n const isEnd = end ? isSameDay(date, end) : false;\n const inRange = start && end ? isBetween(date, start, end) : false;\n return { selected: isStart || isEnd, inRange, isStart, isEnd };\n }\n\n return (\n <div className={cn(styles.root, className)} {...props}>\n <div className={styles.header}>\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Previous month\"\n onClick={() => changeMonth(-1)}\n >\n {\"‹\"}\n </button>\n <span className={styles.headerSpacer} aria-hidden />\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Next month\"\n onClick={() => changeMonth(1)}\n >\n {\"›\"}\n </button>\n </div>\n\n <div className={styles.months} onMouseLeave={() => setHovered(null)}>\n {Array.from({ length: Math.max(1, numberOfMonths) }, (_, monthOffset) => {\n const month = addMonths(visibleMonth, monthOffset);\n const cells = buildGrid(month, weekStartsOn);\n return (\n <div key={monthOffset} className={styles.month}>\n <div className={styles.title} aria-live=\"polite\">\n {MONTH_LABELS[month.getMonth()]} {month.getFullYear()}\n </div>\n <div className={styles.weekdays} role=\"row\">\n {weekdayLabels.map((label) => (\n <span\n key={label}\n className={styles.weekday}\n role=\"columnheader\"\n >\n {label}\n </span>\n ))}\n </div>\n <div className={styles.grid} role=\"grid\">\n {cells.map((date, index) => {\n const inMonth = date.getMonth() === month.getMonth();\n const disabled = isOutOfRange(date, minDate, maxDate);\n const { selected, inRange, isStart, isEnd } = dayState(date);\n const isToday = isSameDay(date, today);\n return (\n <button\n key={index}\n type=\"button\"\n role=\"gridcell\"\n className={cn(\n styles.day,\n !inMonth && styles.outside,\n inRange && styles.inRange,\n selected && styles.selected,\n isStart && styles.rangeStart,\n isEnd && styles.rangeEnd,\n isToday && styles.today,\n )}\n aria-label={`${MONTH_LABELS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`}\n aria-selected={selected}\n aria-current={isToday ? \"date\" : undefined}\n disabled={disabled}\n onClick={() => selectDate(date)}\n onMouseEnter={() => setHovered(date)}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateRangePicker.cjs","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a range is two dates whose validity\n * depends on each other, so the body keeps both ends, the hovered preview end and\n * the open panel in one place; splitting the halves would mean each one reaching\n * into the other's state to clamp itself.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DateRangePicker.module.css\";\n\nexport type WeekStart = 0 | 1;\n\n/** A selected range. Either bound may be `null` while picking. */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\nexport interface DateRangePickerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled selected range. */\n value: DateRange;\n /** Called with the new range as the user picks start then end. */\n onChange: (range: DateRange) => void;\n /** How many month grids to show side by side. Default `2`. */\n numberOfMonths?: number;\n /** Initial visible month (uncontrolled). Defaults to the range start or today. */\n defaultMonth?: Date;\n minDate?: Date;\n maxDate?: Date;\n /** First column of the week — `0` Sunday (default) or `1` Monday. */\n weekStartsOn?: WeekStart;\n}\n\nconst WEEKDAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst MONTH_LABELS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\nconst DAYS_IN_GRID = 42;\n\nfunction makeDate(year: number, month: number, day: number): Date {\n return new Date(year, month, day);\n}\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction addMonths(date: Date, offset: number): Date {\n return makeDate(date.getFullYear(), date.getMonth() + offset, 1);\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nfunction isOutOfRange(date: Date, minDate?: Date, maxDate?: Date): boolean {\n const day = startOfDay(date).getTime();\n if (minDate && day < startOfDay(minDate).getTime()) return true;\n if (maxDate && day > startOfDay(maxDate).getTime()) return true;\n return false;\n}\n\n/** Inclusive-exclusive-agnostic \"strictly between a and b\" (a/b order-independent). */\nfunction isBetween(date: Date, a: Date, b: Date): boolean {\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(a).getTime(), startOfDay(b).getTime());\n const hi = Math.max(startOfDay(a).getTime(), startOfDay(b).getTime());\n return t > lo && t < hi;\n}\n\nfunction buildGrid(month: Date, weekStartsOn: WeekStart): Date[] {\n const year = month.getFullYear();\n const monthIndex = month.getMonth();\n const firstWeekday = makeDate(year, monthIndex, 1).getDay();\n const leading = (firstWeekday - weekStartsOn + 7) % 7;\n const start = makeDate(year, monthIndex, 1 - leading);\n return Array.from({ length: DAYS_IN_GRID }, (_, i) =>\n makeDate(start.getFullYear(), start.getMonth(), start.getDate() + i),\n );\n}\n\n/**\n * Date-range picker: pick a start day, then an end day, across one or more\n * month grids. The hovered day previews the range before the end is committed.\n * Pure `Date` math — no date libraries. For a single date, use `Calendar`.\n *\n * Selection: first click sets `start` (clears `end`); the next click sets `end`\n * (auto-ordered if you click earlier than the start); a third click starts over.\n */\nexport function DateRangePicker({\n value,\n onChange,\n numberOfMonths = 2,\n defaultMonth,\n minDate,\n maxDate,\n weekStartsOn = 0,\n className,\n ...props\n}: DateRangePickerProps) {\n const [visibleMonth, setVisibleMonth] = useState<Date>(() => {\n const base = defaultMonth ?? value.start ?? new Date();\n return makeDate(base.getFullYear(), base.getMonth(), 1);\n });\n const [hovered, setHovered] = useState<Date | null>(null);\n\n const today = useMemo(() => startOfDay(new Date()), []);\n const weekdayLabels = useMemo(\n () => Array.from({ length: 7 }, (_, i) => WEEKDAY_LABELS[(i + weekStartsOn) % 7]),\n [weekStartsOn],\n );\n\n const selectDate = useCallback(\n (date: Date): void => {\n if (isOutOfRange(date, minDate, maxDate)) return;\n const { start, end } = value;\n if (!start || end) {\n onChange({ start: date, end: null });\n } else if (startOfDay(date).getTime() < startOfDay(start).getTime()) {\n onChange({ start: date, end: start });\n } else {\n onChange({ start, end: date });\n }\n },\n [value, onChange, minDate, maxDate],\n );\n\n const changeMonth = useCallback((offset: number): void => {\n setVisibleMonth((current) => addMonths(current, offset));\n }, []);\n\n // Effective end used for preview highlighting (hovered day while picking).\n const previewEnd = value.start && !value.end ? hovered : value.end;\n\n function dayState(date: Date): {\n selected: boolean;\n inRange: boolean;\n isStart: boolean;\n isEnd: boolean;\n } {\n const { start } = value;\n const end = previewEnd;\n const isStart = start ? isSameDay(date, start) : false;\n const isEnd = end ? isSameDay(date, end) : false;\n const inRange = start && end ? isBetween(date, start, end) : false;\n return { selected: isStart || isEnd, inRange, isStart, isEnd };\n }\n\n return (\n <div className={cn(styles.root, className)} {...props}>\n <div className={styles.header}>\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Previous month\"\n onClick={() => changeMonth(-1)}\n >\n {\"‹\"}\n </button>\n <span className={styles.headerSpacer} aria-hidden />\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Next month\"\n onClick={() => changeMonth(1)}\n >\n {\"›\"}\n </button>\n </div>\n\n <div className={styles.months} onMouseLeave={() => setHovered(null)}>\n {Array.from({ length: Math.max(1, numberOfMonths) }, (_, monthOffset) => {\n const month = addMonths(visibleMonth, monthOffset);\n const cells = buildGrid(month, weekStartsOn);\n return (\n <div key={monthOffset} className={styles.month}>\n <div className={styles.title} aria-live=\"polite\">\n {MONTH_LABELS[month.getMonth()]} {month.getFullYear()}\n </div>\n <div className={styles.weekdays} role=\"row\">\n {weekdayLabels.map((label) => (\n <span\n key={label}\n className={styles.weekday}\n role=\"columnheader\"\n >\n {label}\n </span>\n ))}\n </div>\n <div className={styles.grid} role=\"grid\">\n {cells.map((date, index) => {\n const inMonth = date.getMonth() === month.getMonth();\n const disabled = isOutOfRange(date, minDate, maxDate);\n const { selected, inRange, isStart, isEnd } = dayState(date);\n const isToday = isSameDay(date, today);\n return (\n <button\n key={index}\n type=\"button\"\n role=\"gridcell\"\n className={cn(\n styles.day,\n !inMonth && styles.outside,\n inRange && styles.inRange,\n selected && styles.selected,\n isStart && styles.rangeStart,\n isEnd && styles.rangeEnd,\n isToday && styles.today,\n )}\n aria-label={`${MONTH_LABELS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`}\n aria-selected={selected}\n aria-current={isToday ? \"date\" : undefined}\n disabled={disabled}\n onClick={() => selectDate(date)}\n onMouseEnter={() => setHovered(date)}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateRangePicker.js","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a range is two dates whose validity\n * depends on each other, so the body keeps both ends, the hovered preview end and\n * the open panel in one place; splitting the halves would mean each one reaching\n * into the other's state to clamp itself.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DateRangePicker.module.css\";\n\nexport type WeekStart = 0 | 1;\n\n/** A selected range. Either bound may be `null` while picking. */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\nexport interface DateRangePickerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled selected range. */\n value: DateRange;\n /** Called with the new range as the user picks start then end. */\n onChange: (range: DateRange) => void;\n /** How many month grids to show side by side. Default `2`. */\n numberOfMonths?: number;\n /** Initial visible month (uncontrolled). Defaults to the range start or today. */\n defaultMonth?: Date;\n minDate?: Date;\n maxDate?: Date;\n /** First column of the week — `0` Sunday (default) or `1` Monday. */\n weekStartsOn?: WeekStart;\n}\n\nconst WEEKDAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst MONTH_LABELS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\nconst DAYS_IN_GRID = 42;\n\nfunction makeDate(year: number, month: number, day: number): Date {\n return new Date(year, month, day);\n}\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction addMonths(date: Date, offset: number): Date {\n return makeDate(date.getFullYear(), date.getMonth() + offset, 1);\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nfunction isOutOfRange(date: Date, minDate?: Date, maxDate?: Date): boolean {\n const day = startOfDay(date).getTime();\n if (minDate && day < startOfDay(minDate).getTime()) return true;\n if (maxDate && day > startOfDay(maxDate).getTime()) return true;\n return false;\n}\n\n/** Inclusive-exclusive-agnostic \"strictly between a and b\" (a/b order-independent). */\nfunction isBetween(date: Date, a: Date, b: Date): boolean {\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(a).getTime(), startOfDay(b).getTime());\n const hi = Math.max(startOfDay(a).getTime(), startOfDay(b).getTime());\n return t > lo && t < hi;\n}\n\nfunction buildGrid(month: Date, weekStartsOn: WeekStart): Date[] {\n const year = month.getFullYear();\n const monthIndex = month.getMonth();\n const firstWeekday = makeDate(year, monthIndex, 1).getDay();\n const leading = (firstWeekday - weekStartsOn + 7) % 7;\n const start = makeDate(year, monthIndex, 1 - leading);\n return Array.from({ length: DAYS_IN_GRID }, (_, i) =>\n makeDate(start.getFullYear(), start.getMonth(), start.getDate() + i),\n );\n}\n\n/**\n * Date-range picker: pick a start day, then an end day, across one or more\n * month grids. The hovered day previews the range before the end is committed.\n * Pure `Date` math — no date libraries. For a single date, use `Calendar`.\n *\n * Selection: first click sets `start` (clears `end`); the next click sets `end`\n * (auto-ordered if you click earlier than the start); a third click starts over.\n */\nexport function DateRangePicker({\n value,\n onChange,\n numberOfMonths = 2,\n defaultMonth,\n minDate,\n maxDate,\n weekStartsOn = 0,\n className,\n ...props\n}: DateRangePickerProps) {\n const [visibleMonth, setVisibleMonth] = useState<Date>(() => {\n const base = defaultMonth ?? value.start ?? new Date();\n return makeDate(base.getFullYear(), base.getMonth(), 1);\n });\n const [hovered, setHovered] = useState<Date | null>(null);\n\n const today = useMemo(() => startOfDay(new Date()), []);\n const weekdayLabels = useMemo(\n () => Array.from({ length: 7 }, (_, i) => WEEKDAY_LABELS[(i + weekStartsOn) % 7]),\n [weekStartsOn],\n );\n\n const selectDate = useCallback(\n (date: Date): void => {\n if (isOutOfRange(date, minDate, maxDate)) return;\n const { start, end } = value;\n if (!start || end) {\n onChange({ start: date, end: null });\n } else if (startOfDay(date).getTime() < startOfDay(start).getTime()) {\n onChange({ start: date, end: start });\n } else {\n onChange({ start, end: date });\n }\n },\n [value, onChange, minDate, maxDate],\n );\n\n const changeMonth = useCallback((offset: number): void => {\n setVisibleMonth((current) => addMonths(current, offset));\n }, []);\n\n // Effective end used for preview highlighting (hovered day while picking).\n const previewEnd = value.start && !value.end ? hovered : value.end;\n\n function dayState(date: Date): {\n selected: boolean;\n inRange: boolean;\n isStart: boolean;\n isEnd: boolean;\n } {\n const { start } = value;\n const end = previewEnd;\n const isStart = start ? isSameDay(date, start) : false;\n const isEnd = end ? isSameDay(date, end) : false;\n const inRange = start && end ? isBetween(date, start, end) : false;\n return { selected: isStart || isEnd, inRange, isStart, isEnd };\n }\n\n return (\n <div className={cn(styles.root, className)} {...props}>\n <div className={styles.header}>\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Previous month\"\n onClick={() => changeMonth(-1)}\n >\n {\"‹\"}\n </button>\n <span className={styles.headerSpacer} aria-hidden />\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Next month\"\n onClick={() => changeMonth(1)}\n >\n {\"›\"}\n </button>\n </div>\n\n <div className={styles.months} onMouseLeave={() => setHovered(null)}>\n {Array.from({ length: Math.max(1, numberOfMonths) }, (_, monthOffset) => {\n const month = addMonths(visibleMonth, monthOffset);\n const cells = buildGrid(month, weekStartsOn);\n return (\n <div key={monthOffset} className={styles.month}>\n <div className={styles.title} aria-live=\"polite\">\n {MONTH_LABELS[month.getMonth()]} {month.getFullYear()}\n </div>\n <div className={styles.weekdays} role=\"row\">\n {weekdayLabels.map((label) => (\n <span\n key={label}\n className={styles.weekday}\n role=\"columnheader\"\n >\n {label}\n </span>\n ))}\n </div>\n <div className={styles.grid} role=\"grid\">\n {cells.map((date, index) => {\n const inMonth = date.getMonth() === month.getMonth();\n const disabled = isOutOfRange(date, minDate, maxDate);\n const { selected, inRange, isStart, isEnd } = dayState(date);\n const isToday = isSameDay(date, today);\n return (\n <button\n key={index}\n type=\"button\"\n role=\"gridcell\"\n className={cn(\n styles.day,\n !inMonth && styles.outside,\n inRange && styles.inRange,\n selected && styles.selected,\n isStart && styles.rangeStart,\n isEnd && styles.rangeEnd,\n isToday && styles.today,\n )}\n aria-label={`${MONTH_LABELS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`}\n aria-selected={selected}\n aria-current={isToday ? \"date\" : undefined}\n disabled={disabled}\n onClick={() => selectDate(date)}\n onMouseEnter={() => setHovered(date)}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DateRangePicker.js","names":[],"sources":["../../../src/components/DateRangePicker/DateRangePicker.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a range is two dates whose validity\n * depends on each other, so the body keeps both ends, the hovered preview end and\n * the open panel in one place; splitting the halves would mean each one reaching\n * into the other's state to clamp itself.\n */\nimport { useCallback, useMemo, useState } from \"react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DateRangePicker.module.css\";\n\nexport type WeekStart = 0 | 1;\n\n/** A selected range. Either bound may be `null` while picking. */\nexport interface DateRange {\n start: Date | null;\n end: Date | null;\n}\n\nexport interface DateRangePickerProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onChange\" | \"defaultValue\"\n> {\n /** Controlled selected range. */\n value: DateRange;\n /** Called with the new range as the user picks start then end. */\n onChange: (range: DateRange) => void;\n /** How many month grids to show side by side. Default `2`. */\n numberOfMonths?: number;\n /** Initial visible month (uncontrolled). Defaults to the range start or today. */\n defaultMonth?: Date;\n minDate?: Date;\n maxDate?: Date;\n /** First column of the week — `0` Sunday (default) or `1` Monday. */\n weekStartsOn?: WeekStart;\n}\n\nconst WEEKDAY_LABELS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nconst MONTH_LABELS = [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n];\nconst DAYS_IN_GRID = 42;\n\nfunction makeDate(year: number, month: number, day: number): Date {\n return new Date(year, month, day);\n}\n\nfunction startOfDay(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction addMonths(date: Date, offset: number): Date {\n return makeDate(date.getFullYear(), date.getMonth() + offset, 1);\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\nfunction isOutOfRange(date: Date, minDate?: Date, maxDate?: Date): boolean {\n const day = startOfDay(date).getTime();\n if (minDate && day < startOfDay(minDate).getTime()) return true;\n if (maxDate && day > startOfDay(maxDate).getTime()) return true;\n return false;\n}\n\n/** Inclusive-exclusive-agnostic \"strictly between a and b\" (a/b order-independent). */\nfunction isBetween(date: Date, a: Date, b: Date): boolean {\n const t = startOfDay(date).getTime();\n const lo = Math.min(startOfDay(a).getTime(), startOfDay(b).getTime());\n const hi = Math.max(startOfDay(a).getTime(), startOfDay(b).getTime());\n return t > lo && t < hi;\n}\n\nfunction buildGrid(month: Date, weekStartsOn: WeekStart): Date[] {\n const year = month.getFullYear();\n const monthIndex = month.getMonth();\n const firstWeekday = makeDate(year, monthIndex, 1).getDay();\n const leading = (firstWeekday - weekStartsOn + 7) % 7;\n const start = makeDate(year, monthIndex, 1 - leading);\n return Array.from({ length: DAYS_IN_GRID }, (_, i) =>\n makeDate(start.getFullYear(), start.getMonth(), start.getDate() + i),\n );\n}\n\n/**\n * Date-range picker: pick a start day, then an end day, across one or more\n * month grids. The hovered day previews the range before the end is committed.\n * Pure `Date` math — no date libraries. For a single date, use `Calendar`.\n *\n * Selection: first click sets `start` (clears `end`); the next click sets `end`\n * (auto-ordered if you click earlier than the start); a third click starts over.\n */\nexport function DateRangePicker({\n value,\n onChange,\n numberOfMonths = 2,\n defaultMonth,\n minDate,\n maxDate,\n weekStartsOn = 0,\n className,\n ...props\n}: DateRangePickerProps) {\n const [visibleMonth, setVisibleMonth] = useState<Date>(() => {\n const base = defaultMonth ?? value.start ?? new Date();\n return makeDate(base.getFullYear(), base.getMonth(), 1);\n });\n const [hovered, setHovered] = useState<Date | null>(null);\n\n const today = useMemo(() => startOfDay(new Date()), []);\n const weekdayLabels = useMemo(\n () => Array.from({ length: 7 }, (_, i) => WEEKDAY_LABELS[(i + weekStartsOn) % 7]),\n [weekStartsOn],\n );\n\n const selectDate = useCallback(\n (date: Date): void => {\n if (isOutOfRange(date, minDate, maxDate)) return;\n const { start, end } = value;\n if (!start || end) {\n onChange({ start: date, end: null });\n } else if (startOfDay(date).getTime() < startOfDay(start).getTime()) {\n onChange({ start: date, end: start });\n } else {\n onChange({ start, end: date });\n }\n },\n [value, onChange, minDate, maxDate],\n );\n\n const changeMonth = useCallback((offset: number): void => {\n setVisibleMonth((current) => addMonths(current, offset));\n }, []);\n\n // Effective end used for preview highlighting (hovered day while picking).\n const previewEnd = value.start && !value.end ? hovered : value.end;\n\n function dayState(date: Date): {\n selected: boolean;\n inRange: boolean;\n isStart: boolean;\n isEnd: boolean;\n } {\n const { start } = value;\n const end = previewEnd;\n const isStart = start ? isSameDay(date, start) : false;\n const isEnd = end ? isSameDay(date, end) : false;\n const inRange = start && end ? isBetween(date, start, end) : false;\n return { selected: isStart || isEnd, inRange, isStart, isEnd };\n }\n\n return (\n <div className={cn(styles.root, className)} {...props}>\n <div className={styles.header}>\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Previous month\"\n onClick={() => changeMonth(-1)}\n >\n {\"‹\"}\n </button>\n <span className={styles.headerSpacer} aria-hidden />\n <button\n type=\"button\"\n className={styles.nav}\n aria-label=\"Next month\"\n onClick={() => changeMonth(1)}\n >\n {\"›\"}\n </button>\n </div>\n\n <div className={styles.months} onMouseLeave={() => setHovered(null)}>\n {Array.from({ length: Math.max(1, numberOfMonths) }, (_, monthOffset) => {\n const month = addMonths(visibleMonth, monthOffset);\n const cells = buildGrid(month, weekStartsOn);\n return (\n <div key={monthOffset} className={styles.month}>\n <div className={styles.title} aria-live=\"polite\">\n {MONTH_LABELS[month.getMonth()]} {month.getFullYear()}\n </div>\n <div className={styles.weekdays} role=\"row\">\n {weekdayLabels.map((label) => (\n <span\n key={label}\n className={styles.weekday}\n role=\"columnheader\"\n >\n {label}\n </span>\n ))}\n </div>\n <div className={styles.grid} role=\"grid\">\n {cells.map((date, index) => {\n const inMonth = date.getMonth() === month.getMonth();\n const disabled = isOutOfRange(date, minDate, maxDate);\n const { selected, inRange, isStart, isEnd } = dayState(date);\n const isToday = isSameDay(date, today);\n return (\n <button\n key={index}\n type=\"button\"\n role=\"gridcell\"\n className={cn(\n styles.day,\n !inMonth && styles.outside,\n inRange && styles.inRange,\n selected && styles.selected,\n isStart && styles.rangeStart,\n isEnd && styles.rangeEnd,\n isToday && styles.today,\n )}\n aria-label={`${MONTH_LABELS[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`}\n aria-selected={selected}\n aria-current={isToday ? \"date\" : undefined}\n disabled={disabled}\n onClick={() => selectDate(date)}\n onMouseEnter={() => setHovered(date)}\n >\n {date.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DescriptionList.cjs","names":[],"sources":["../../../src/components/DescriptionList/DescriptionList.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { Fragment } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DescriptionList.module.css\";\n\nexport interface DescriptionListItem {\n /** The key/label rendered inside a `<dt>`. */\n term: ReactNode;\n /** The value rendered inside a `<dd>`. */\n description: ReactNode;\n}\n\nexport interface DescriptionListProps extends HTMLAttributes<HTMLDListElement> {\n /** Term/description pairs rendered as `<dt>`/`<dd>` rows. */\n items: DescriptionListItem[];\n}\n\n/**\n * Renders a semantic `<dl>` of term/description pairs with minimal\n * token-based key/value styling.\n *\n * @param items - The term/description pairs to render.\n * @returns The rendered description list.\n */\nexport function DescriptionList({ items, className, ...props }: DescriptionListProps) {\n return (\n <dl className={cn(styles.list, className)} {...props}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <dt className={styles.term}>{item.term}</dt>\n <dd className={styles.description}>{item.description}</dd>\n </Fragment>\n ))}\n </dl>\n );\n}\n"],"mappings":"sIAwBA,SAAgB,EAAgB,CAAE,QAAO,YAAW,GAAG,GAA+B,CAClF,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,
|
|
1
|
+
{"version":3,"file":"DescriptionList.cjs","names":[],"sources":["../../../src/components/DescriptionList/DescriptionList.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { Fragment } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DescriptionList.module.css\";\n\nexport interface DescriptionListItem {\n /** The key/label rendered inside a `<dt>`. */\n term: ReactNode;\n /** The value rendered inside a `<dd>`. */\n description: ReactNode;\n}\n\nexport interface DescriptionListProps extends HTMLAttributes<HTMLDListElement> {\n /** Term/description pairs rendered as `<dt>`/`<dd>` rows. */\n items: DescriptionListItem[];\n}\n\n/**\n * Renders a semantic `<dl>` of term/description pairs with minimal\n * token-based key/value styling.\n *\n * @param items - The term/description pairs to render.\n * @returns The rendered description list.\n */\nexport function DescriptionList({ items, className, ...props }: DescriptionListProps) {\n return (\n <dl className={cn(styles.list, className)} {...props}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <dt className={styles.term}>{item.term}</dt>\n <dd className={styles.description}>{item.description}</dd>\n </Fragment>\n ))}\n </dl>\n );\n}\n"],"mappings":"sIAwBA,SAAgB,EAAgB,CAAE,QAAO,YAAW,GAAG,GAA+B,CAClF,OACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,CAAS,EAAG,GAAI,EAC1C,SAAA,EAAM,KAAK,EAAM,KACd,EAAA,EAAA,KAAA,CAAC,EAAA,SAAD,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,KAAO,SAAA,EAAK,IAAS,CAAA,GAC3C,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,YAAc,SAAA,EAAK,WAAgB,CAAA,CACnD,CAAA,EAHK,CAGL,CACb,CACD,CAAA,CAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DescriptionList.js","names":[],"sources":["../../../src/components/DescriptionList/DescriptionList.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { Fragment } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DescriptionList.module.css\";\n\nexport interface DescriptionListItem {\n /** The key/label rendered inside a `<dt>`. */\n term: ReactNode;\n /** The value rendered inside a `<dd>`. */\n description: ReactNode;\n}\n\nexport interface DescriptionListProps extends HTMLAttributes<HTMLDListElement> {\n /** Term/description pairs rendered as `<dt>`/`<dd>` rows. */\n items: DescriptionListItem[];\n}\n\n/**\n * Renders a semantic `<dl>` of term/description pairs with minimal\n * token-based key/value styling.\n *\n * @param items - The term/description pairs to render.\n * @returns The rendered description list.\n */\nexport function DescriptionList({ items, className, ...props }: DescriptionListProps) {\n return (\n <dl className={cn(styles.list, className)} {...props}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <dt className={styles.term}>{item.term}</dt>\n <dd className={styles.description}>{item.description}</dd>\n </Fragment>\n ))}\n </dl>\n );\n}\n"],"mappings":";;;;;AAwBA,SAAgB,EAAgB,EAAE,UAAO,cAAW,GAAG,KAA+B;CAClF,OACI,kBAAC,MAAD;EAAI,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;
|
|
1
|
+
{"version":3,"file":"DescriptionList.js","names":[],"sources":["../../../src/components/DescriptionList/DescriptionList.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { Fragment } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DescriptionList.module.css\";\n\nexport interface DescriptionListItem {\n /** The key/label rendered inside a `<dt>`. */\n term: ReactNode;\n /** The value rendered inside a `<dd>`. */\n description: ReactNode;\n}\n\nexport interface DescriptionListProps extends HTMLAttributes<HTMLDListElement> {\n /** Term/description pairs rendered as `<dt>`/`<dd>` rows. */\n items: DescriptionListItem[];\n}\n\n/**\n * Renders a semantic `<dl>` of term/description pairs with minimal\n * token-based key/value styling.\n *\n * @param items - The term/description pairs to render.\n * @returns The rendered description list.\n */\nexport function DescriptionList({ items, className, ...props }: DescriptionListProps) {\n return (\n <dl className={cn(styles.list, className)} {...props}>\n {items.map((item, index) => (\n <Fragment key={index}>\n <dt className={styles.term}>{item.term}</dt>\n <dd className={styles.description}>{item.description}</dd>\n </Fragment>\n ))}\n </dl>\n );\n}\n"],"mappings":";;;;;AAwBA,SAAgB,EAAgB,EAAE,UAAO,cAAW,GAAG,KAA+B;CAClF,OACI,kBAAC,MAAD;EAAI,WAAW,EAAG,EAAO,MAAM,CAAS;EAAG,GAAI;EAC1C,UAAA,EAAM,KAAK,GAAM,MACd,kBAAC,GAAD,EAAA,UAAA,CACI,kBAAC,MAAD;GAAI,WAAW,EAAO;GAAO,UAAA,EAAK;EAAS,CAAA,GAC3C,kBAAC,MAAD;GAAI,WAAW,EAAO;GAAc,UAAA,EAAK;EAAgB,CAAA,CACnD,EAAA,GAHK,CAGL,CACb;CACD,CAAA;AAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Divider.cjs","names":[],"sources":["../../../src/components/Divider/Divider.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Divider.module.css\";\n\nexport type DividerOrientation = \"horizontal\" | \"vertical\";\nexport type DividerVariant = \"solid\" | \"dashed\";\nexport type DividerAlign = \"start\" | \"center\" | \"end\";\n\nexport interface DividerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n orientation?: DividerOrientation;\n variant?: DividerVariant;\n /** Optional label rendered inside the divider (horizontal only). */\n label?: ReactNode;\n /** Label horizontal position (only when `label` provided). Defaults to `center`. */\n align?: DividerAlign;\n}\n\n/**\n * Horizontal or vertical visual separator. When `label` is provided in\n * horizontal mode the divider splits and centers the label between two lines.\n */\nexport function Divider({\n orientation = \"horizontal\",\n variant = \"solid\",\n label,\n align = \"center\",\n className,\n ...props\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n styles.divider,\n styles.vertical,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n {...props}\n />\n );\n }\n\n if (!label) {\n return (\n <hr\n className={cn(\n styles.divider,\n styles.horizontal,\n styles.bare,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n />\n );\n }\n\n return (\n <div\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn(\n styles.divider,\n styles.horizontal,\n variant === \"dashed\" && styles.dashed,\n align === \"start\" && styles.alignStart,\n align === \"end\" && styles.alignEnd,\n className,\n )}\n {...props}\n >\n {label}\n </div>\n );\n}\n"],"mappings":"2GAqBA,SAAgB,EAAQ,CACpB,cAAc,aACd,UAAU,QACV,QACA,QAAQ,SACR,YACA,GAAG,GACU,CA+Bb,OA9BI,IAAgB,YAEZ,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,KAAK,YACL,mBAAiB,WACjB,UAAW,EAAA,GACP,EAAA,QAAO,QACP,EAAA,QAAO,SACP,IAAY,UAAY,EAAA,QAAO,OAC/B,CACJ,EACA,GAAI,CACP,CAAA,EAIJ,GAeD,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,KAAK,YACL,mBAAiB,aACjB,UAAW,EAAA,GACP,EAAA,QAAO,QACP,EAAA,QAAO,WACP,IAAY,UAAY,EAAA,QAAO,OAC/B,IAAU,SAAW,EAAA,QAAO,WAC5B,IAAU,OAAS,EAAA,QAAO,SAC1B,CACJ,EACA,GAAI,
|
|
1
|
+
{"version":3,"file":"Divider.cjs","names":[],"sources":["../../../src/components/Divider/Divider.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Divider.module.css\";\n\nexport type DividerOrientation = \"horizontal\" | \"vertical\";\nexport type DividerVariant = \"solid\" | \"dashed\";\nexport type DividerAlign = \"start\" | \"center\" | \"end\";\n\nexport interface DividerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n orientation?: DividerOrientation;\n variant?: DividerVariant;\n /** Optional label rendered inside the divider (horizontal only). */\n label?: ReactNode;\n /** Label horizontal position (only when `label` provided). Defaults to `center`. */\n align?: DividerAlign;\n}\n\n/**\n * Horizontal or vertical visual separator. When `label` is provided in\n * horizontal mode the divider splits and centers the label between two lines.\n */\nexport function Divider({\n orientation = \"horizontal\",\n variant = \"solid\",\n label,\n align = \"center\",\n className,\n ...props\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n styles.divider,\n styles.vertical,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n {...props}\n />\n );\n }\n\n if (!label) {\n return (\n <hr\n className={cn(\n styles.divider,\n styles.horizontal,\n styles.bare,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n />\n );\n }\n\n return (\n <div\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn(\n styles.divider,\n styles.horizontal,\n variant === \"dashed\" && styles.dashed,\n align === \"start\" && styles.alignStart,\n align === \"end\" && styles.alignEnd,\n className,\n )}\n {...props}\n >\n {label}\n </div>\n );\n}\n"],"mappings":"2GAqBA,SAAgB,EAAQ,CACpB,cAAc,aACd,UAAU,QACV,QACA,QAAQ,SACR,YACA,GAAG,GACU,CA+Bb,OA9BI,IAAgB,YAEZ,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,KAAK,YACL,mBAAiB,WACjB,UAAW,EAAA,GACP,EAAA,QAAO,QACP,EAAA,QAAO,SACP,IAAY,UAAY,EAAA,QAAO,OAC/B,CACJ,EACA,GAAI,CACP,CAAA,EAIJ,GAeD,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,KAAK,YACL,mBAAiB,aACjB,UAAW,EAAA,GACP,EAAA,QAAO,QACP,EAAA,QAAO,WACP,IAAY,UAAY,EAAA,QAAO,OAC/B,IAAU,SAAW,EAAA,QAAO,WAC5B,IAAU,OAAS,EAAA,QAAO,SAC1B,CACJ,EACA,GAAI,EAEH,SAAA,CACA,CAAA,GA3BD,EAAA,EAAA,IAAA,CAAC,KAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,QACP,EAAA,QAAO,WACP,EAAA,QAAO,KACP,IAAY,UAAY,EAAA,QAAO,OAC/B,CACJ,CACH,CAAA,CAqBb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Divider.js","names":[],"sources":["../../../src/components/Divider/Divider.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Divider.module.css\";\n\nexport type DividerOrientation = \"horizontal\" | \"vertical\";\nexport type DividerVariant = \"solid\" | \"dashed\";\nexport type DividerAlign = \"start\" | \"center\" | \"end\";\n\nexport interface DividerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n orientation?: DividerOrientation;\n variant?: DividerVariant;\n /** Optional label rendered inside the divider (horizontal only). */\n label?: ReactNode;\n /** Label horizontal position (only when `label` provided). Defaults to `center`. */\n align?: DividerAlign;\n}\n\n/**\n * Horizontal or vertical visual separator. When `label` is provided in\n * horizontal mode the divider splits and centers the label between two lines.\n */\nexport function Divider({\n orientation = \"horizontal\",\n variant = \"solid\",\n label,\n align = \"center\",\n className,\n ...props\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n styles.divider,\n styles.vertical,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n {...props}\n />\n );\n }\n\n if (!label) {\n return (\n <hr\n className={cn(\n styles.divider,\n styles.horizontal,\n styles.bare,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n />\n );\n }\n\n return (\n <div\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn(\n styles.divider,\n styles.horizontal,\n variant === \"dashed\" && styles.dashed,\n align === \"start\" && styles.alignStart,\n align === \"end\" && styles.alignEnd,\n className,\n )}\n {...props}\n >\n {label}\n </div>\n );\n}\n"],"mappings":";;;;AAqBA,SAAgB,EAAQ,EACpB,iBAAc,cACd,aAAU,SACV,UACA,WAAQ,UACR,cACA,GAAG,KACU;CA+Bb,OA9BI,MAAgB,aAEZ,kBAAC,QAAD;EACI,MAAK;EACL,oBAAiB;EACjB,WAAW,EACP,EAAO,SACP,EAAO,UACP,MAAY,YAAY,EAAO,QAC/B,CACJ;EACA,GAAI;CACP,CAAA,IAIJ,IAeD,kBAAC,OAAD;EACI,MAAK;EACL,oBAAiB;EACjB,WAAW,EACP,EAAO,SACP,EAAO,YACP,MAAY,YAAY,EAAO,QAC/B,MAAU,WAAW,EAAO,YAC5B,MAAU,SAAS,EAAO,UAC1B,CACJ;EACA,GAAI;
|
|
1
|
+
{"version":3,"file":"Divider.js","names":[],"sources":["../../../src/components/Divider/Divider.tsx"],"sourcesContent":["import type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Divider.module.css\";\n\nexport type DividerOrientation = \"horizontal\" | \"vertical\";\nexport type DividerVariant = \"solid\" | \"dashed\";\nexport type DividerAlign = \"start\" | \"center\" | \"end\";\n\nexport interface DividerProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n orientation?: DividerOrientation;\n variant?: DividerVariant;\n /** Optional label rendered inside the divider (horizontal only). */\n label?: ReactNode;\n /** Label horizontal position (only when `label` provided). Defaults to `center`. */\n align?: DividerAlign;\n}\n\n/**\n * Horizontal or vertical visual separator. When `label` is provided in\n * horizontal mode the divider splits and centers the label between two lines.\n */\nexport function Divider({\n orientation = \"horizontal\",\n variant = \"solid\",\n label,\n align = \"center\",\n className,\n ...props\n}: DividerProps) {\n if (orientation === \"vertical\") {\n return (\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n className={cn(\n styles.divider,\n styles.vertical,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n {...props}\n />\n );\n }\n\n if (!label) {\n return (\n <hr\n className={cn(\n styles.divider,\n styles.horizontal,\n styles.bare,\n variant === \"dashed\" && styles.dashed,\n className,\n )}\n />\n );\n }\n\n return (\n <div\n role=\"separator\"\n aria-orientation=\"horizontal\"\n className={cn(\n styles.divider,\n styles.horizontal,\n variant === \"dashed\" && styles.dashed,\n align === \"start\" && styles.alignStart,\n align === \"end\" && styles.alignEnd,\n className,\n )}\n {...props}\n >\n {label}\n </div>\n );\n}\n"],"mappings":";;;;AAqBA,SAAgB,EAAQ,EACpB,iBAAc,cACd,aAAU,SACV,UACA,WAAQ,UACR,cACA,GAAG,KACU;CA+Bb,OA9BI,MAAgB,aAEZ,kBAAC,QAAD;EACI,MAAK;EACL,oBAAiB;EACjB,WAAW,EACP,EAAO,SACP,EAAO,UACP,MAAY,YAAY,EAAO,QAC/B,CACJ;EACA,GAAI;CACP,CAAA,IAIJ,IAeD,kBAAC,OAAD;EACI,MAAK;EACL,oBAAiB;EACjB,WAAW,EACP,EAAO,SACP,EAAO,YACP,MAAY,YAAY,EAAO,QAC/B,MAAU,WAAW,EAAO,YAC5B,MAAU,SAAS,EAAO,UAC1B,CACJ;EACA,GAAI;EAEH,UAAA;CACA,CAAA,IA3BD,kBAAC,MAAD,EACI,WAAW,EACP,EAAO,SACP,EAAO,YACP,EAAO,MACP,MAAY,YAAY,EAAO,QAC/B,CACJ,EACH,CAAA;AAqBb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.cjs","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"gMAyCA,SAAgB,EAAO,CACnB,OACA,UACA,YAAY,QACZ,QACA,WACA,SACA,kBAAkB,GAClB,aAAa,GACb,kBAAkB,GAClB,YACA,kBACA,aAAa,IACD,CACZ,GAAM,CAAE,YAAa,EAAA,cAAc,EAC7B,EACF,GAAY,EAAkB,EAAkB,EAkBpD,OAhBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAC/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,CACT,SAAS,KAAK,MAAM,SAAW,EAC/B,OAAO,oBAAoB,UAAW,CAAS,CACnD,CACJ,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE1B,CAAC,GAAQ,OAAO,SAAa,IAAoB,MAErD,
|
|
1
|
+
{"version":3,"file":"Drawer.cjs","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"gMAyCA,SAAgB,EAAO,CACnB,OACA,UACA,YAAY,QACZ,QACA,WACA,SACA,kBAAkB,GAClB,aAAa,GACb,kBAAkB,GAClB,YACA,kBACA,aAAa,IACD,CACZ,GAAM,CAAE,YAAa,EAAA,cAAc,EAC7B,EACF,GAAY,EAAkB,EAAkB,EAkBpD,OAhBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAC/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,CACT,SAAS,KAAK,MAAM,SAAW,EAC/B,OAAO,oBAAoB,UAAW,CAAS,CACnD,CACJ,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE1B,CAAC,GAAQ,OAAO,SAAa,IAAoB,MAErD,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,YAAe,CACP,GAAiB,EAAQ,CACjC,CACH,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,QAAD,CACI,KAAK,SACL,aAAW,OACX,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAqB,CAAS,EAHrE,SAAA,CAKK,IACI,IAAuB,UAAY,IAAuB,SACvD,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAuB,OAAS,EAAA,QAAO,SAC3C,EACA,cAAA,EACH,CAAA,GAEP,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAU,CAAA,EACvC,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,aAAW,SACX,UAAW,EAAA,QAAO,MAClB,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAY,CAAA,CACR,CAAA,CAER,KAEZ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,EAC3C,IAAU,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAAS,SAAA,CAAe,CAAA,CAC1D,CACT,CAAA,CAAA,CAAA,CAAA,EACF,SAAS,IACb,CACJ,CAEA,SAAS,GAAY,CACjB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OACjD,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,uBACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;AAyCA,SAAgB,EAAO,EACnB,SACA,YACA,eAAY,SACZ,UACA,aACA,WACA,qBAAkB,IAClB,gBAAa,IACb,qBAAkB,IAClB,cACA,oBACA,gBAAa,MACD;CACZ,IAAM,EAAE,gBAAa,EAAc,GAC7B,IACF,KAAY,IAAkB,IAAkB;CAkBpD,OAhBA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAC/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B;GAET,AADA,SAAS,KAAK,MAAM,WAAW,GAC/B,OAAO,oBAAoB,WAAW,CAAS;EACnD;CACJ,GAAG;EAAC;EAAM;EAAY;CAAO,CAAC,GAE1B,CAAC,KAAQ,OAAO,WAAa,MAAoB,OAE9C,EACH,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;CACH,CAAA,GACD,kBAAC,SAAD;EACI,MAAK;EACL,cAAW;EACX,WAAW,EAAG,EAAO,OAAO,EAAO,IAAqB,CAAS;
|
|
1
|
+
{"version":3,"file":"Drawer.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;AAyCA,SAAgB,EAAO,EACnB,SACA,YACA,eAAY,SACZ,UACA,aACA,WACA,qBAAkB,IAClB,gBAAa,IACb,qBAAkB,IAClB,cACA,oBACA,gBAAa,MACD;CACZ,IAAM,EAAE,gBAAa,EAAc,GAC7B,IACF,KAAY,IAAkB,IAAkB;CAkBpD,OAhBA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAC/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B;GAET,AADA,SAAS,KAAK,MAAM,WAAW,GAC/B,OAAO,oBAAoB,WAAW,CAAS;EACnD;CACJ,GAAG;EAAC;EAAM;EAAY;CAAO,CAAC,GAE1B,CAAC,KAAQ,OAAO,WAAa,MAAoB,OAE9C,EACH,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;CACH,CAAA,GACD,kBAAC,SAAD;EACI,MAAK;EACL,cAAW;EACX,WAAW,EAAG,EAAO,OAAO,EAAO,IAAqB,CAAS;EAHrE,UAAA;GAKK,MACI,MAAuB,YAAY,MAAuB,UACvD,kBAAC,OAAD;IACI,WAAW,EACP,EAAO,QACP,MAAuB,SAAS,EAAO,SAC3C;IACA,eAAA;GACH,CAAA;IAEP,KAAS,CAAC,MACR,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAA1B,UAAA,CACI,kBAAC,MAAD;KAAI,WAAW,EAAO;KAAQ,UAAA;IAAU,CAAA,GACvC,CAAC,KACE,kBAAC,UAAD;KACI,MAAK;KACL,cAAW;KACX,WAAW,EAAO;KAClB,SAAS;KAET,UAAA,kBAAC,GAAD,CAAY,CAAA;IACR,CAAA,CAER;;GAEZ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO;GAAc,CAAA;GAC3C,KAAU,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAAS,UAAA;GAAe,CAAA;EAC1D;CACT,CAAA,CAAA,EAAA,CAAA,GACF,SAAS,IACb;AACJ;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, Escape, and roving focus over items that can be disabled or separators —\n * one keyboard model that has to see the whole item list to know where the next stop\n * is.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/**\n * Dropdown menu — list of selectable actions anchored to a trigger.\n *\n * - Toggle on trigger click.\n * - Close on outside click / Escape / item selection.\n * - Arrow keys navigate, Enter activates focused item.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const selectableIndexes = items\n .map((entry, index) => (entry.type === \"item\" && !entry.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const close = useCallback((): void => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const next = selectableIndexes[(current + 1) % selectableIndexes.length] ?? -1;\n setActiveIndex(next);\n itemRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const prev =\n selectableIndexes[\n (current - 1 + selectableIndexes.length) % selectableIndexes.length\n ] ?? -1;\n setActiveIndex(prev);\n itemRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [open, activeIndex, selectableIndexes, close]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen((prev) => !prev);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: Extract<DropdownMenuEntry, { type: \"item\" }>): void => {\n entry.onSelect();\n close();\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n className={cn(\n styles.item,\n entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":"mIAsCA,SAAS,EAAe,EAA0C,CAC9D,OAAQ,EAAR,CACI,IAAK,aACD,OAAO,EAAA,QAAO,UAClB,IAAK,YACD,OAAO,EAAA,QAAO,SAClB,IAAK,UACD,OAAO,EAAA,QAAO,OAElB,QACI,OAAO,EAAA,QAAO,WACtB,CACJ,CASA,SAAgB,EAAa,CACzB,UACA,QACA,YAAY,eACZ,aACkB,CAClB,GAAM,CAAC,EAAM,IAAA,
|
|
1
|
+
{"version":3,"file":"DropdownMenu.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, Escape, and roving focus over items that can be disabled or separators —\n * one keyboard model that has to see the whole item list to know where the next stop\n * is.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/**\n * Dropdown menu — list of selectable actions anchored to a trigger.\n *\n * - Toggle on trigger click.\n * - Close on outside click / Escape / item selection.\n * - Arrow keys navigate, Enter activates focused item.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const selectableIndexes = items\n .map((entry, index) => (entry.type === \"item\" && !entry.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const close = useCallback((): void => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const next = selectableIndexes[(current + 1) % selectableIndexes.length] ?? -1;\n setActiveIndex(next);\n itemRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const prev =\n selectableIndexes[\n (current - 1 + selectableIndexes.length) % selectableIndexes.length\n ] ?? -1;\n setActiveIndex(prev);\n itemRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [open, activeIndex, selectableIndexes, close]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen((prev) => !prev);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: Extract<DropdownMenuEntry, { type: \"item\" }>): void => {\n entry.onSelect();\n close();\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n className={cn(\n styles.item,\n entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":"mIAsCA,SAAS,EAAe,EAA0C,CAC9D,OAAQ,EAAR,CACI,IAAK,aACD,OAAO,EAAA,QAAO,UAClB,IAAK,YACD,OAAO,EAAA,QAAO,SAClB,IAAK,UACD,OAAO,EAAA,QAAO,OAElB,QACI,OAAO,EAAA,QAAO,WACtB,CACJ,CASA,SAAgB,EAAa,CACzB,UACA,QACA,YAAY,eACZ,aACkB,CAClB,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAS,EAAK,EAChC,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAiB,EAAE,EACnD,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,GAAA,EAAU,EAAA,OAAA,CAAwB,IAAI,EACtC,GAAA,EAAW,EAAA,OAAA,CAAwC,CAAC,CAAC,EAErD,EAAoB,EACrB,KAAK,EAAO,IAAW,EAAM,OAAS,QAAU,CAAC,EAAM,SAAW,EAAQ,EAAG,CAAC,CAC9E,OAAQ,GAAM,IAAM,EAAE,EAErB,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAQ,EAAK,EACb,EAAe,EAAE,CACrB,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAS,GAA+B,CAC1C,GAAI,EAAM,MAAQ,SAAU,CACxB,EAAM,EACN,MACJ,CACA,GAAI,EAAM,MAAQ,YAAa,CAC3B,EAAM,eAAe,EACrB,IAAM,EAAU,EAAkB,QAAQ,CAAW,EAC/C,EAAO,GAAmB,EAAU,GAAK,EAAkB,SAAW,GAC5E,EAAe,CAAI,EACnB,EAAS,QAAQ,EAAK,EAAE,MAAM,CAClC,CACA,GAAI,EAAM,MAAQ,UAAW,CACzB,EAAM,eAAe,EACrB,IAAM,EAAU,EAAkB,QAAQ,CAAW,EAC/C,EACF,GACK,EAAU,EAAI,EAAkB,QAAU,EAAkB,SAC5D,GACT,EAAe,CAAI,EACnB,EAAS,QAAQ,EAAK,EAAE,MAAM,CAClC,CACJ,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAM,EAAa,EAAmB,CAAK,CAAC,EAEhD,IAAM,EAAsB,GAAkC,CAC1D,EAAQ,MAAM,UAAU,CAAK,EAC7B,EAAS,GAAS,CAAC,CAAI,CAC3B,EAEM,EAAe,CACjB,GAAG,EACH,MAAO,CACH,GAAG,EAAQ,MACX,QAAS,EACT,gBAAiB,EACjB,gBAAiB,EACjB,gBAAiB,MACrB,CACJ,EAEM,EAAgB,GAA8D,CAChF,EAAM,SAAS,EACf,EAAM,CACV,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,IAAK,EAAS,UAAW,EAAA,QAAO,KAAtC,SAAA,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CACQ,KACJ,KAAK,OACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAe,CAAS,EAAG,CAAS,EAE9D,SAAA,EAAM,KAAK,EAAO,IACX,EAAM,OAAS,aAEX,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,KAAK,YACL,UAAW,EAAA,QAAO,UAClB,cAAA,EACH,EAJQ,EAAM,EAId,EAGL,EAAM,OAAS,SAEX,EAAA,EAAA,IAAA,CAAC,KAAD,CAAmB,KAAK,eAAe,UAAW,EAAA,QAAO,MACpD,SAAA,EAAM,KACP,EAFK,EAAM,EAEX,GAIR,EAAA,EAAA,IAAA,CAAC,KAAD,CAAmB,KAAK,OACpB,UAAA,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,IAAM,GAAO,CACT,EAAS,QAAQ,GAAS,CAC9B,EACA,KAAK,SACL,KAAK,WACL,UAAW,EAAA,GACP,EAAA,QAAO,KACP,EAAM,QAAU,EAAA,QAAO,OACvB,IAAgB,GAAS,EAAA,QAAO,MACpC,EACA,SAAU,EAAM,SAChB,YAAe,EAAa,CAAK,EACjC,iBAAoB,EAAe,CAAK,EAb5C,SAAA,CAeK,EAAM,OAAQ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,cAAA,GAAa,SAAA,EAAM,IAAW,CAAA,EAClD,EAAM,KACH,GACR,EAnBK,EAAM,EAmBX,CAEX,CACD,CAAA,CAEN,GAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, Escape, and roving focus over items that can be disabled or separators —\n * one keyboard model that has to see the whole item list to know where the next stop\n * is.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/**\n * Dropdown menu — list of selectable actions anchored to a trigger.\n *\n * - Toggle on trigger click.\n * - Close on outside click / Escape / item selection.\n * - Arrow keys navigate, Enter activates focused item.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const selectableIndexes = items\n .map((entry, index) => (entry.type === \"item\" && !entry.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const close = useCallback((): void => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const next = selectableIndexes[(current + 1) % selectableIndexes.length] ?? -1;\n setActiveIndex(next);\n itemRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const prev =\n selectableIndexes[\n (current - 1 + selectableIndexes.length) % selectableIndexes.length\n ] ?? -1;\n setActiveIndex(prev);\n itemRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [open, activeIndex, selectableIndexes, close]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen((prev) => !prev);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: Extract<DropdownMenuEntry, { type: \"item\" }>): void => {\n entry.onSelect();\n close();\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n className={cn(\n styles.item,\n entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;AAsCA,SAAS,EAAe,GAA0C;CAC9D,QAAQ,GAAR;EACI,KAAK,cACD,OAAO,EAAO;EAClB,KAAK,aACD,OAAO,EAAO;EAClB,KAAK,WACD,OAAO,EAAO;EAElB,SACI,OAAO,EAAO;CACtB;AACJ;AASA,SAAgB,EAAa,EACzB,YACA,UACA,eAAY,gBACZ,gBACkB;CAClB,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAa,KAAkB,EAAiB,EAAE,GACnD,IAAK,EAAM,GACX,IAAU,EAAwB,IAAI,GACtC,IAAW,EAAwC,CAAC,CAAC,GAErD,IAAoB,EACrB,KAAK,GAAO,MAAW,EAAM,SAAS,UAAU,CAAC,EAAM,WAAW,IAAQ,EAAG,CAAC,CAC9E,QAAQ,MAAM,MAAM,EAAE,GAErB,IAAQ,QAAwB;EAElC,AADA,EAAQ,EAAK,GACb,EAAe,EAAE;CACrB,GAAG,CAAC,CAAC;CAEL,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,KAAS,MAA+B;GAC1C,IAAI,EAAM,QAAQ,UAAU;IACxB,EAAM;IACN;GACJ;GACA,IAAI,EAAM,QAAQ,aAAa;IAC3B,EAAM,eAAe;IACrB,IAAM,IAAU,EAAkB,QAAQ,CAAW,GAC/C,IAAO,GAAmB,IAAU,KAAK,EAAkB,WAAW;IAE5E,AADA,EAAe,CAAI,GACnB,EAAS,QAAQ,EAAK,EAAE,MAAM;GAClC;GACA,IAAI,EAAM,QAAQ,WAAW;IACzB,EAAM,eAAe;IACrB,IAAM,IAAU,EAAkB,QAAQ,CAAW,GAC/C,IACF,GACK,IAAU,IAAI,EAAkB,UAAU,EAAkB,WAC5D;IAET,AADA,EAAe,CAAI,GACnB,EAAS,QAAQ,EAAK,EAAE,MAAM;GAClC;EACJ,GACM,KAAU,MAA4B;GACxC,AAAI,EAAQ,WAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,KAAG,EAAM;EAClF;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAK,GACxC,OAAO,iBAAiB,aAAa,CAAM,SAC9B;GAET,AADA,OAAO,oBAAoB,WAAW,CAAK,GAC3C,OAAO,oBAAoB,aAAa,CAAM;EAClD;CACJ,GAAG;EAAC;EAAM;EAAa;EAAmB;CAAK,CAAC;CAEhD,IAAM,KAAsB,MAAkC;EAE1D,AADA,EAAQ,MAAM,UAAU,CAAK,GAC7B,GAAS,MAAS,CAAC,CAAI;CAC3B,GAEM,IAAe;EACjB,GAAG;EACH,OAAO;GACH,GAAG,EAAQ;GACX,SAAS;GACT,iBAAiB;GACjB,iBAAiB;GACjB,iBAAiB;EACrB;CACJ,GAEM,KAAgB,MAA8D;EAEhF,AADA,EAAM,SAAS,GACf,EAAM;CACV;CAEA,OACI,kBAAC,QAAD;EAAM,KAAK;EAAS,WAAW,EAAO;
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, Escape, and roving focus over items that can be disabled or separators —\n * one keyboard model that has to see the whole item list to know where the next stop\n * is.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/**\n * Dropdown menu — list of selectable actions anchored to a trigger.\n *\n * - Toggle on trigger click.\n * - Close on outside click / Escape / item selection.\n * - Arrow keys navigate, Enter activates focused item.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const selectableIndexes = items\n .map((entry, index) => (entry.type === \"item\" && !entry.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const close = useCallback((): void => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const next = selectableIndexes[(current + 1) % selectableIndexes.length] ?? -1;\n setActiveIndex(next);\n itemRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const prev =\n selectableIndexes[\n (current - 1 + selectableIndexes.length) % selectableIndexes.length\n ] ?? -1;\n setActiveIndex(prev);\n itemRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [open, activeIndex, selectableIndexes, close]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen((prev) => !prev);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: Extract<DropdownMenuEntry, { type: \"item\" }>): void => {\n entry.onSelect();\n close();\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n className={cn(\n styles.item,\n entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropzone.cjs","names":[],"sources":["../../../src/components/Dropzone/Dropzone.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — accept, multiple and maxSize are\n * validation the component must apply before it can call onDrop or onReject, and\n * drag state, keyboard activation and the hidden input all live in the body because\n * they resolve to the same file list.\n */\nimport { useRef, useState } from \"react\";\nimport type { DragEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Dropzone.module.css\";\n\n/** Props for {@link Dropzone}. */\nexport interface DropzoneProps {\n /** Called with the accepted files after a drop or file-dialog selection. */\n onDrop: (files: File[]) => void;\n /** `accept` attribute forwarded to the hidden file input. */\n accept?: string;\n /** Allow selecting/dropping multiple files. Default `true`. */\n multiple?: boolean;\n /** Disable interaction. */\n disabled?: boolean;\n /** Maximum file size in bytes; larger files are filtered out. */\n maxSize?: number;\n /** Called with files rejected by `maxSize`. */\n onReject?: (files: File[]) => void;\n /** Custom inner content. Falls back to a default prompt. */\n children?: ReactNode;\n /** Extra class applied to the drop area. */\n className?: string;\n}\n\n/**\n * Drag-and-drop file area with a hidden file input. Clickable and keyboard\n * focusable; filters by `maxSize` before calling `onDrop`.\n *\n * @example\n * <Dropzone accept=\"image/*\" maxSize={5 * 1024 * 1024} onDrop={setFiles}>\n * Solte imagens aqui ou clique para selecionar\n * </Dropzone>\n */\nexport function Dropzone({\n onDrop,\n accept,\n multiple = true,\n disabled = false,\n maxSize,\n onReject,\n children,\n className,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n const handleFiles = (fileList: FileList | null): void => {\n if (!fileList || fileList.length === 0) return;\n const all = Array.from(fileList);\n const limited = multiple ? all : all.slice(0, 1);\n\n if (maxSize === undefined) {\n onDrop(limited);\n return;\n }\n\n const accepted: File[] = [];\n const rejected: File[] = [];\n for (const file of limited) {\n if (file.size > maxSize) rejected.push(file);\n else accepted.push(file);\n }\n if (rejected.length > 0) onReject?.(rejected);\n if (accepted.length > 0) onDrop(accepted);\n };\n\n const openDialog = (): void => {\n if (disabled) return;\n inputRef.current?.click();\n };\n\n const handleDragEnter = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n if (disabled) return;\n setIsDragging(true);\n };\n\n const handleDragOver = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n };\n\n const handleDragLeave = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n if (disabled) return;\n handleFiles(event.dataTransfer.files);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openDialog();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n className={cn(\n styles.zone,\n isDragging && styles.dragging,\n disabled && styles.disabled,\n className,\n )}\n onClick={openDialog}\n onKeyDown={handleKeyDown}\n onDragEnter={handleDragEnter}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <input\n ref={inputRef}\n type=\"file\"\n className={styles.input}\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n onChange={(event) => {\n handleFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n {children ?? (\n <span className={styles.prompt}>\n Arraste arquivos aqui ou clique para selecionar\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"+HAwCA,SAAgB,EAAS,CACrB,SACA,SACA,WAAW,GACX,WAAW,GACX,UACA,WACA,WACA,aACc,CACd,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"Dropzone.cjs","names":[],"sources":["../../../src/components/Dropzone/Dropzone.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — accept, multiple and maxSize are\n * validation the component must apply before it can call onDrop or onReject, and\n * drag state, keyboard activation and the hidden input all live in the body because\n * they resolve to the same file list.\n */\nimport { useRef, useState } from \"react\";\nimport type { DragEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Dropzone.module.css\";\n\n/** Props for {@link Dropzone}. */\nexport interface DropzoneProps {\n /** Called with the accepted files after a drop or file-dialog selection. */\n onDrop: (files: File[]) => void;\n /** `accept` attribute forwarded to the hidden file input. */\n accept?: string;\n /** Allow selecting/dropping multiple files. Default `true`. */\n multiple?: boolean;\n /** Disable interaction. */\n disabled?: boolean;\n /** Maximum file size in bytes; larger files are filtered out. */\n maxSize?: number;\n /** Called with files rejected by `maxSize`. */\n onReject?: (files: File[]) => void;\n /** Custom inner content. Falls back to a default prompt. */\n children?: ReactNode;\n /** Extra class applied to the drop area. */\n className?: string;\n}\n\n/**\n * Drag-and-drop file area with a hidden file input. Clickable and keyboard\n * focusable; filters by `maxSize` before calling `onDrop`.\n *\n * @example\n * <Dropzone accept=\"image/*\" maxSize={5 * 1024 * 1024} onDrop={setFiles}>\n * Solte imagens aqui ou clique para selecionar\n * </Dropzone>\n */\nexport function Dropzone({\n onDrop,\n accept,\n multiple = true,\n disabled = false,\n maxSize,\n onReject,\n children,\n className,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n const handleFiles = (fileList: FileList | null): void => {\n if (!fileList || fileList.length === 0) return;\n const all = Array.from(fileList);\n const limited = multiple ? all : all.slice(0, 1);\n\n if (maxSize === undefined) {\n onDrop(limited);\n return;\n }\n\n const accepted: File[] = [];\n const rejected: File[] = [];\n for (const file of limited) {\n if (file.size > maxSize) rejected.push(file);\n else accepted.push(file);\n }\n if (rejected.length > 0) onReject?.(rejected);\n if (accepted.length > 0) onDrop(accepted);\n };\n\n const openDialog = (): void => {\n if (disabled) return;\n inputRef.current?.click();\n };\n\n const handleDragEnter = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n if (disabled) return;\n setIsDragging(true);\n };\n\n const handleDragOver = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n };\n\n const handleDragLeave = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n if (disabled) return;\n handleFiles(event.dataTransfer.files);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openDialog();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n className={cn(\n styles.zone,\n isDragging && styles.dragging,\n disabled && styles.disabled,\n className,\n )}\n onClick={openDialog}\n onKeyDown={handleKeyDown}\n onDragEnter={handleDragEnter}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <input\n ref={inputRef}\n type=\"file\"\n className={styles.input}\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n onChange={(event) => {\n handleFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n {children ?? (\n <span className={styles.prompt}>\n Arraste arquivos aqui ou clique para selecionar\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"+HAwCA,SAAgB,EAAS,CACrB,SACA,SACA,WAAW,GACX,WAAW,GACX,UACA,WACA,WACA,aACc,CACd,IAAM,GAAA,EAAW,EAAA,OAAA,CAAyB,IAAI,EACxC,CAAC,EAAY,IAAA,EAAiB,EAAA,SAAA,CAAS,EAAK,EAE5C,EAAe,GAAoC,CACrD,GAAI,CAAC,GAAY,EAAS,SAAW,EAAG,OACxC,IAAM,EAAM,MAAM,KAAK,CAAQ,EACzB,EAAU,EAAW,EAAM,EAAI,MAAM,EAAG,CAAC,EAE/C,GAAI,IAAY,IAAA,GAAW,CACvB,EAAO,CAAO,EACd,MACJ,CAEA,IAAM,EAAmB,CAAC,EACpB,EAAmB,CAAC,EAC1B,IAAK,IAAM,KAAQ,EACX,EAAK,KAAO,EAAS,EAAS,KAAK,CAAI,EACtC,EAAS,KAAK,CAAI,EAEvB,EAAS,OAAS,GAAG,IAAW,CAAQ,EACxC,EAAS,OAAS,GAAG,EAAO,CAAQ,CAC5C,EAEM,MAAyB,CACvB,GACJ,EAAS,SAAS,MAAM,CAC5B,EA+BA,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,KAAK,SACL,SAAU,EAAW,GAAK,EAC1B,gBAAe,GAAY,IAAA,GAC3B,UAAW,EAAA,GACP,EAAA,QAAO,KACP,GAAc,EAAA,QAAO,SACrB,GAAY,EAAA,QAAO,SACnB,CACJ,EACA,QAAS,EACT,UAnBe,GAA+C,EAC9D,EAAM,MAAQ,SAAW,EAAM,MAAQ,OACvC,EAAM,eAAe,EACrB,EAAW,EAEnB,EAeQ,YA1CiB,GAA2C,CAChE,EAAM,eAAe,EACjB,IACJ,EAAc,EAAI,CACtB,EAuCQ,WArCgB,GAA2C,CAC/D,EAAM,eAAe,CACzB,EAoCQ,YAlCiB,GAA2C,CAChE,EAAM,eAAe,EACrB,EAAc,EAAK,CACvB,EAgCQ,OA9BY,GAA2C,CAC3D,EAAM,eAAe,EACrB,EAAc,EAAK,EACf,IACJ,EAAY,EAAM,aAAa,KAAK,CACxC,EAUI,SAAA,EAiBI,EAAA,EAAA,IAAA,CAAC,QAAD,CACI,IAAK,EACL,KAAK,OACL,UAAW,EAAA,QAAO,MACV,SACE,WACA,WACV,SAAW,GAAU,CACjB,EAAY,EAAM,OAAO,KAAK,EAC9B,EAAM,OAAO,MAAQ,EACzB,CACH,CAAA,EACA,IACG,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,OAAQ,SAAA,iDAE1B,CAAA,CAET,GAEb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropzone.js","names":[],"sources":["../../../src/components/Dropzone/Dropzone.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — accept, multiple and maxSize are\n * validation the component must apply before it can call onDrop or onReject, and\n * drag state, keyboard activation and the hidden input all live in the body because\n * they resolve to the same file list.\n */\nimport { useRef, useState } from \"react\";\nimport type { DragEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Dropzone.module.css\";\n\n/** Props for {@link Dropzone}. */\nexport interface DropzoneProps {\n /** Called with the accepted files after a drop or file-dialog selection. */\n onDrop: (files: File[]) => void;\n /** `accept` attribute forwarded to the hidden file input. */\n accept?: string;\n /** Allow selecting/dropping multiple files. Default `true`. */\n multiple?: boolean;\n /** Disable interaction. */\n disabled?: boolean;\n /** Maximum file size in bytes; larger files are filtered out. */\n maxSize?: number;\n /** Called with files rejected by `maxSize`. */\n onReject?: (files: File[]) => void;\n /** Custom inner content. Falls back to a default prompt. */\n children?: ReactNode;\n /** Extra class applied to the drop area. */\n className?: string;\n}\n\n/**\n * Drag-and-drop file area with a hidden file input. Clickable and keyboard\n * focusable; filters by `maxSize` before calling `onDrop`.\n *\n * @example\n * <Dropzone accept=\"image/*\" maxSize={5 * 1024 * 1024} onDrop={setFiles}>\n * Solte imagens aqui ou clique para selecionar\n * </Dropzone>\n */\nexport function Dropzone({\n onDrop,\n accept,\n multiple = true,\n disabled = false,\n maxSize,\n onReject,\n children,\n className,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n const handleFiles = (fileList: FileList | null): void => {\n if (!fileList || fileList.length === 0) return;\n const all = Array.from(fileList);\n const limited = multiple ? all : all.slice(0, 1);\n\n if (maxSize === undefined) {\n onDrop(limited);\n return;\n }\n\n const accepted: File[] = [];\n const rejected: File[] = [];\n for (const file of limited) {\n if (file.size > maxSize) rejected.push(file);\n else accepted.push(file);\n }\n if (rejected.length > 0) onReject?.(rejected);\n if (accepted.length > 0) onDrop(accepted);\n };\n\n const openDialog = (): void => {\n if (disabled) return;\n inputRef.current?.click();\n };\n\n const handleDragEnter = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n if (disabled) return;\n setIsDragging(true);\n };\n\n const handleDragOver = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n };\n\n const handleDragLeave = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n if (disabled) return;\n handleFiles(event.dataTransfer.files);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openDialog();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n className={cn(\n styles.zone,\n isDragging && styles.dragging,\n disabled && styles.disabled,\n className,\n )}\n onClick={openDialog}\n onKeyDown={handleKeyDown}\n onDragEnter={handleDragEnter}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <input\n ref={inputRef}\n type=\"file\"\n className={styles.input}\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n onChange={(event) => {\n handleFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n {children ?? (\n <span className={styles.prompt}>\n Arraste arquivos aqui ou clique para selecionar\n </span>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAwCA,SAAgB,EAAS,EACrB,WACA,WACA,cAAW,IACX,cAAW,IACX,YACA,aACA,aACA,gBACc;CACd,IAAM,IAAW,EAAyB,IAAI,GACxC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,KAAe,MAAoC;EACrD,IAAI,CAAC,KAAY,EAAS,WAAW,GAAG;EACxC,IAAM,IAAM,MAAM,KAAK,CAAQ,GACzB,IAAU,IAAW,IAAM,EAAI,MAAM,GAAG,CAAC;EAE/C,IAAI,MAAY,KAAA,GAAW;GACvB,EAAO,CAAO;GACd;EACJ;EAEA,IAAM,IAAmB,CAAC,GACpB,IAAmB,CAAC;EAC1B,KAAK,IAAM,KAAQ,GACf,AAAI,EAAK,OAAO,IAAS,EAAS,KAAK,CAAI,IACtC,EAAS,KAAK,CAAI;EAG3B,AADI,EAAS,SAAS,KAAG,IAAW,CAAQ,GACxC,EAAS,SAAS,KAAG,EAAO,CAAQ;CAC5C,GAEM,UAAyB;EACvB,KACJ,EAAS,SAAS,MAAM;CAC5B;CA+BA,OACI,kBAAC,OAAD;EACI,MAAK;EACL,UAAU,IAAW,KAAK;EAC1B,iBAAe,KAAY,KAAA;EAC3B,WAAW,EACP,EAAO,MACP,KAAc,EAAO,UACrB,KAAY,EAAO,UACnB,CACJ;EACA,SAAS;EACT,YAnBe,MAA+C;GAClE,CAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACvC,EAAM,eAAe,GACrB,EAAW;EAEnB;EAeQ,cA1CiB,MAA2C;GAChE,EAAM,eAAe,GACjB,MACJ,EAAc,EAAI;EACtB;EAuCQ,aArCgB,MAA2C;GAC/D,EAAM,eAAe;EACzB;EAoCQ,cAlCiB,MAA2C;GAEhE,AADA,EAAM,eAAe,GACrB,EAAc,EAAK;EACvB;EAgCQ,SA9BY,MAA2C;GAC3D,EAAM,eAAe,GACrB,EAAc,EAAK,GACf,MACJ,EAAY,EAAM,aAAa,KAAK;EACxC;
|
|
1
|
+
{"version":3,"file":"Dropzone.js","names":[],"sources":["../../../src/components/Dropzone/Dropzone.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — accept, multiple and maxSize are\n * validation the component must apply before it can call onDrop or onReject, and\n * drag state, keyboard activation and the hidden input all live in the body because\n * they resolve to the same file list.\n */\nimport { useRef, useState } from \"react\";\nimport type { DragEvent, KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Dropzone.module.css\";\n\n/** Props for {@link Dropzone}. */\nexport interface DropzoneProps {\n /** Called with the accepted files after a drop or file-dialog selection. */\n onDrop: (files: File[]) => void;\n /** `accept` attribute forwarded to the hidden file input. */\n accept?: string;\n /** Allow selecting/dropping multiple files. Default `true`. */\n multiple?: boolean;\n /** Disable interaction. */\n disabled?: boolean;\n /** Maximum file size in bytes; larger files are filtered out. */\n maxSize?: number;\n /** Called with files rejected by `maxSize`. */\n onReject?: (files: File[]) => void;\n /** Custom inner content. Falls back to a default prompt. */\n children?: ReactNode;\n /** Extra class applied to the drop area. */\n className?: string;\n}\n\n/**\n * Drag-and-drop file area with a hidden file input. Clickable and keyboard\n * focusable; filters by `maxSize` before calling `onDrop`.\n *\n * @example\n * <Dropzone accept=\"image/*\" maxSize={5 * 1024 * 1024} onDrop={setFiles}>\n * Solte imagens aqui ou clique para selecionar\n * </Dropzone>\n */\nexport function Dropzone({\n onDrop,\n accept,\n multiple = true,\n disabled = false,\n maxSize,\n onReject,\n children,\n className,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [isDragging, setIsDragging] = useState(false);\n\n const handleFiles = (fileList: FileList | null): void => {\n if (!fileList || fileList.length === 0) return;\n const all = Array.from(fileList);\n const limited = multiple ? all : all.slice(0, 1);\n\n if (maxSize === undefined) {\n onDrop(limited);\n return;\n }\n\n const accepted: File[] = [];\n const rejected: File[] = [];\n for (const file of limited) {\n if (file.size > maxSize) rejected.push(file);\n else accepted.push(file);\n }\n if (rejected.length > 0) onReject?.(rejected);\n if (accepted.length > 0) onDrop(accepted);\n };\n\n const openDialog = (): void => {\n if (disabled) return;\n inputRef.current?.click();\n };\n\n const handleDragEnter = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n if (disabled) return;\n setIsDragging(true);\n };\n\n const handleDragOver = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n };\n\n const handleDragLeave = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n };\n\n const handleDrop = (event: DragEvent<HTMLDivElement>): void => {\n event.preventDefault();\n setIsDragging(false);\n if (disabled) return;\n handleFiles(event.dataTransfer.files);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>): void => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n openDialog();\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={disabled ? -1 : 0}\n aria-disabled={disabled || undefined}\n className={cn(\n styles.zone,\n isDragging && styles.dragging,\n disabled && styles.disabled,\n className,\n )}\n onClick={openDialog}\n onKeyDown={handleKeyDown}\n onDragEnter={handleDragEnter}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onDrop={handleDrop}\n >\n <input\n ref={inputRef}\n type=\"file\"\n className={styles.input}\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n onChange={(event) => {\n handleFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n {children ?? (\n <span className={styles.prompt}>\n Arraste arquivos aqui ou clique para selecionar\n </span>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAwCA,SAAgB,EAAS,EACrB,WACA,WACA,cAAW,IACX,cAAW,IACX,YACA,aACA,aACA,gBACc;CACd,IAAM,IAAW,EAAyB,IAAI,GACxC,CAAC,GAAY,KAAiB,EAAS,EAAK,GAE5C,KAAe,MAAoC;EACrD,IAAI,CAAC,KAAY,EAAS,WAAW,GAAG;EACxC,IAAM,IAAM,MAAM,KAAK,CAAQ,GACzB,IAAU,IAAW,IAAM,EAAI,MAAM,GAAG,CAAC;EAE/C,IAAI,MAAY,KAAA,GAAW;GACvB,EAAO,CAAO;GACd;EACJ;EAEA,IAAM,IAAmB,CAAC,GACpB,IAAmB,CAAC;EAC1B,KAAK,IAAM,KAAQ,GACf,AAAI,EAAK,OAAO,IAAS,EAAS,KAAK,CAAI,IACtC,EAAS,KAAK,CAAI;EAG3B,AADI,EAAS,SAAS,KAAG,IAAW,CAAQ,GACxC,EAAS,SAAS,KAAG,EAAO,CAAQ;CAC5C,GAEM,UAAyB;EACvB,KACJ,EAAS,SAAS,MAAM;CAC5B;CA+BA,OACI,kBAAC,OAAD;EACI,MAAK;EACL,UAAU,IAAW,KAAK;EAC1B,iBAAe,KAAY,KAAA;EAC3B,WAAW,EACP,EAAO,MACP,KAAc,EAAO,UACrB,KAAY,EAAO,UACnB,CACJ;EACA,SAAS;EACT,YAnBe,MAA+C;GAClE,CAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACvC,EAAM,eAAe,GACrB,EAAW;EAEnB;EAeQ,cA1CiB,MAA2C;GAChE,EAAM,eAAe,GACjB,MACJ,EAAc,EAAI;EACtB;EAuCQ,aArCgB,MAA2C;GAC/D,EAAM,eAAe;EACzB;EAoCQ,cAlCiB,MAA2C;GAEhE,AADA,EAAM,eAAe,GACrB,EAAc,EAAK;EACvB;EAgCQ,SA9BY,MAA2C;GAC3D,EAAM,eAAe,GACrB,EAAc,EAAK,GACf,MACJ,EAAY,EAAM,aAAa,KAAK;EACxC;EAUI,UAAA,CAiBI,kBAAC,SAAD;GACI,KAAK;GACL,MAAK;GACL,WAAW,EAAO;GACV;GACE;GACA;GACV,WAAW,MAAU;IAEjB,AADA,EAAY,EAAM,OAAO,KAAK,GAC9B,EAAM,OAAO,QAAQ;GACzB;EACH,CAAA,GACA,KACG,kBAAC,QAAD;GAAM,WAAW,EAAO;GAAQ,UAAA;EAE1B,CAAA,CAET;;AAEb"}
|