@delacour/react-native-ui 0.1.0-alpha.4 → 0.2.0-alpha.20261010061731
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 +10 -6
- package/package.json +49 -5
- package/src/components/accordion/AGENTS.md +21 -9
- package/src/components/accordion/accordion-content.tsx +7 -4
- package/src/components/accordion/accordion-item.tsx +15 -13
- package/src/components/accordion/accordion.context.tsx +11 -5
- package/src/components/accordion/accordion.variants.test.ts +64 -3
- package/src/components/accordion/accordion.variants.ts +33 -9
- package/src/components/alert/AGENTS.md +102 -0
- package/src/components/alert/alert-action.tsx +17 -0
- package/src/components/alert/alert-close-button.tsx +50 -0
- package/src/components/alert/alert-content.tsx +15 -0
- package/src/components/alert/alert-description.tsx +16 -0
- package/src/components/alert/alert-glyphs.ts +25 -0
- package/src/components/alert/alert-indicator.tsx +40 -0
- package/src/components/alert/alert-title.tsx +18 -0
- package/src/components/alert/alert.context.tsx +69 -0
- package/src/components/alert/alert.tsx +160 -0
- package/src/components/alert/alert.types.ts +10 -0
- package/src/components/alert/alert.variants.test.ts +218 -0
- package/src/components/alert/alert.variants.ts +180 -0
- package/src/components/alert/index.ts +23 -0
- package/src/components/avatar/AGENTS.md +106 -0
- package/src/components/avatar/avatar-badge.tsx +51 -0
- package/src/components/avatar/avatar-group.tsx +139 -0
- package/src/components/avatar/avatar.context.tsx +93 -0
- package/src/components/avatar/avatar.tsx +201 -0
- package/src/components/avatar/avatar.utils.test.ts +174 -0
- package/src/components/avatar/avatar.utils.ts +141 -0
- package/src/components/avatar/avatar.variants.test.ts +251 -0
- package/src/components/avatar/avatar.variants.ts +270 -0
- package/src/components/avatar/index.ts +39 -0
- package/src/components/badge/AGENTS.md +10 -0
- package/src/components/badge/badge-close-button.tsx +2 -2
- package/src/components/badge/badge.context.tsx +2 -1
- package/src/components/badge/badge.tsx +13 -4
- package/src/components/badge/badge.variants.test.ts +26 -0
- package/src/components/badge/badge.variants.ts +21 -0
- package/src/components/bottom-sheet/AGENTS.md +186 -231
- package/src/components/bottom-sheet/bottom-sheet-background.tsx +31 -22
- package/src/components/bottom-sheet/bottom-sheet-close.tsx +23 -30
- package/src/components/bottom-sheet/bottom-sheet-container.tsx +54 -208
- package/src/components/bottom-sheet/bottom-sheet-content.tsx +25 -48
- package/src/components/bottom-sheet/bottom-sheet-description.tsx +8 -2
- package/src/components/bottom-sheet/bottom-sheet-flat-list.tsx +65 -0
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +53 -122
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +31 -26
- package/src/components/bottom-sheet/bottom-sheet-legend-list.tsx +69 -0
- package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +25 -147
- package/src/components/bottom-sheet/bottom-sheet-provider.tsx +42 -0
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +38 -62
- package/src/components/bottom-sheet/bottom-sheet-section-list.tsx +58 -0
- package/src/components/bottom-sheet/bottom-sheet-steps.tsx +88 -0
- package/src/components/bottom-sheet/bottom-sheet-text-input.tsx +44 -0
- package/src/components/bottom-sheet/bottom-sheet-title.tsx +12 -8
- package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +19 -37
- package/src/components/bottom-sheet/bottom-sheet.tsx +113 -60
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +47 -87
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +50 -126
- package/src/components/bottom-sheet/index.ts +39 -17
- package/src/components/bottom-sheet/use-bottom-sheet-input.ts +16 -115
- package/src/components/button/AGENTS.md +51 -11
- package/src/components/button/button-group.tsx +44 -8
- package/src/components/button/button.context.tsx +10 -0
- package/src/components/button/button.tsx +7 -1
- package/src/components/button/button.variants.test.ts +190 -0
- package/src/components/button/button.variants.ts +87 -6
- package/src/components/button/index.ts +2 -0
- package/src/components/calendar/AGENTS.md +142 -0
- package/src/components/calendar/calendar-caption.tsx +116 -0
- package/src/components/calendar/calendar-day.tsx +103 -0
- package/src/components/calendar/calendar-grid.tsx +183 -0
- package/src/components/calendar/calendar-header.tsx +34 -0
- package/src/components/calendar/calendar-nav.tsx +51 -0
- package/src/components/calendar/calendar-picker.tsx +128 -0
- package/src/components/calendar/calendar-weekdays.tsx +48 -0
- package/src/components/calendar/calendar.context.tsx +108 -0
- package/src/components/calendar/calendar.date.test.ts +456 -0
- package/src/components/calendar/calendar.date.ts +584 -0
- package/src/components/calendar/calendar.tsx +389 -0
- package/src/components/calendar/calendar.types.ts +87 -0
- package/src/components/calendar/calendar.variants.test.ts +363 -0
- package/src/components/calendar/calendar.variants.ts +426 -0
- package/src/components/calendar/index.ts +96 -0
- package/src/components/card/AGENTS.md +92 -0
- package/src/components/card/card-action.tsx +16 -0
- package/src/components/card/card-content.tsx +12 -0
- package/src/components/card/card-description.tsx +12 -0
- package/src/components/card/card-footer.tsx +30 -0
- package/src/components/card/card-header.tsx +45 -0
- package/src/components/card/card-title.tsx +25 -0
- package/src/components/card/card.context.tsx +64 -0
- package/src/components/card/card.tsx +100 -0
- package/src/components/card/card.types.ts +13 -0
- package/src/components/card/card.variants.test.ts +240 -0
- package/src/components/card/card.variants.ts +177 -0
- package/src/components/card/index.ts +15 -0
- package/src/components/chart/AGENTS.md +8 -0
- package/src/components/chart/chart.tsx +8 -1
- package/src/components/chip/AGENTS.md +113 -0
- package/src/components/chip/chip-close-button.tsx +48 -0
- package/src/components/chip/chip-end-content.tsx +15 -0
- package/src/components/chip/chip-label.tsx +21 -0
- package/src/components/chip/chip-start-content.tsx +16 -0
- package/src/components/chip/chip.context.tsx +66 -0
- package/src/components/chip/chip.tsx +268 -0
- package/src/components/chip/chip.types.ts +10 -0
- package/src/components/chip/chip.variants.test.ts +335 -0
- package/src/components/chip/chip.variants.ts +321 -0
- package/src/components/chip/index.ts +24 -0
- package/src/components/collapsible/AGENTS.md +97 -0
- package/src/components/collapsible/collapsible-content.tsx +97 -0
- package/src/components/collapsible/collapsible-description.tsx +17 -0
- package/src/components/collapsible/collapsible-indicator.tsx +92 -0
- package/src/components/collapsible/collapsible-title.tsx +17 -0
- package/src/components/collapsible/collapsible-trigger.tsx +133 -0
- package/src/components/collapsible/collapsible.context.tsx +91 -0
- package/src/components/collapsible/collapsible.tsx +167 -0
- package/src/components/collapsible/collapsible.types.ts +9 -0
- package/src/components/collapsible/collapsible.variants.test.ts +338 -0
- package/src/components/collapsible/collapsible.variants.ts +228 -0
- package/src/components/collapsible/index.ts +32 -0
- package/src/components/context-menu/AGENTS.md +103 -0
- package/src/components/context-menu/context-menu-content.tsx +64 -0
- package/src/components/context-menu/context-menu-item.tsx +22 -0
- package/src/components/context-menu/context-menu-preview.tsx +60 -0
- package/src/components/context-menu/context-menu-trigger.tsx +160 -0
- package/src/components/context-menu/context-menu.context.tsx +91 -0
- package/src/components/context-menu/context-menu.tsx +90 -0
- package/src/components/context-menu/context-menu.variants.test.ts +266 -0
- package/src/components/context-menu/context-menu.variants.ts +147 -0
- package/src/components/context-menu/index.ts +18 -0
- package/src/components/dialog/AGENTS.md +97 -0
- package/src/components/dialog/dialog-body.tsx +21 -0
- package/src/components/dialog/dialog-close.tsx +82 -0
- package/src/components/dialog/dialog-content.tsx +153 -0
- package/src/components/dialog/dialog-description.tsx +23 -0
- package/src/components/dialog/dialog-footer.tsx +32 -0
- package/src/components/dialog/dialog-header.tsx +20 -0
- package/src/components/dialog/dialog-title.tsx +42 -0
- package/src/components/dialog/dialog-trigger.tsx +54 -0
- package/src/components/dialog/dialog.context.tsx +90 -0
- package/src/components/dialog/dialog.tsx +119 -0
- package/src/components/dialog/dialog.variants.test.ts +224 -0
- package/src/components/dialog/dialog.variants.ts +173 -0
- package/src/components/dialog/index.ts +30 -0
- package/src/components/drawer/AGENTS.md +123 -0
- package/src/components/drawer/drawer-body.tsx +46 -0
- package/src/components/drawer/drawer-close.tsx +76 -0
- package/src/components/drawer/drawer-content.tsx +186 -0
- package/src/components/drawer/drawer-description.tsx +23 -0
- package/src/components/drawer/drawer-footer.tsx +21 -0
- package/src/components/drawer/drawer-header.tsx +42 -0
- package/src/components/drawer/drawer-title.tsx +35 -0
- package/src/components/drawer/drawer-trigger.tsx +54 -0
- package/src/components/drawer/drawer.context.tsx +114 -0
- package/src/components/drawer/drawer.tsx +119 -0
- package/src/components/drawer/drawer.variants.test.ts +392 -0
- package/src/components/drawer/drawer.variants.ts +244 -0
- package/src/components/drawer/index.ts +41 -0
- package/src/components/drawer/use-drawer-pan.ts +108 -0
- package/src/components/empty-state/AGENTS.md +90 -0
- package/src/components/empty-state/empty-state-content.tsx +17 -0
- package/src/components/empty-state/empty-state-description.tsx +12 -0
- package/src/components/empty-state/empty-state-header.tsx +17 -0
- package/src/components/empty-state/empty-state-media.tsx +56 -0
- package/src/components/empty-state/empty-state-title.tsx +18 -0
- package/src/components/empty-state/empty-state.context.tsx +65 -0
- package/src/components/empty-state/empty-state.tsx +84 -0
- package/src/components/empty-state/empty-state.types.ts +14 -0
- package/src/components/empty-state/empty-state.variants.test.ts +245 -0
- package/src/components/empty-state/empty-state.variants.ts +132 -0
- package/src/components/empty-state/index.ts +20 -0
- package/src/components/fab/AGENTS.md +119 -0
- package/src/components/fab/fab-action.tsx +118 -0
- package/src/components/fab/fab-group.tsx +214 -0
- package/src/components/fab/fab-label.tsx +18 -0
- package/src/components/fab/fab.context.tsx +125 -0
- package/src/components/fab/fab.tsx +156 -0
- package/src/components/fab/fab.types.ts +17 -0
- package/src/components/fab/fab.variants.test.ts +274 -0
- package/src/components/fab/fab.variants.ts +234 -0
- package/src/components/fab/index.ts +37 -0
- package/src/components/feedback/AGENTS.md +95 -0
- package/src/components/feedback/feedback-action.tsx +16 -0
- package/src/components/feedback/feedback-cancel.tsx +34 -0
- package/src/components/feedback/feedback-close.tsx +21 -0
- package/src/components/feedback/feedback-content.tsx +24 -0
- package/src/components/feedback/feedback-field.tsx +70 -0
- package/src/components/feedback/feedback-footer.tsx +21 -0
- package/src/components/feedback/feedback-panel.tsx +54 -0
- package/src/components/feedback/feedback-submit.tsx +74 -0
- package/src/components/feedback/feedback-title.tsx +34 -0
- package/src/components/feedback/feedback-trigger.tsx +20 -0
- package/src/components/feedback/feedback.context.tsx +73 -0
- package/src/components/feedback/feedback.tsx +135 -0
- package/src/components/feedback/feedback.variants.test.ts +185 -0
- package/src/components/feedback/feedback.variants.ts +128 -0
- package/src/components/feedback/index.ts +24 -0
- package/src/components/field/AGENTS.md +19 -0
- package/src/components/field/field-label.tsx +19 -5
- package/src/components/field/field.context.tsx +17 -0
- package/src/components/field/field.tsx +15 -2
- package/src/components/field/field.variants.test.ts +24 -0
- package/src/components/field/field.variants.ts +20 -0
- package/src/components/field/index.ts +1 -0
- package/src/components/input/AGENTS.md +5 -0
- package/src/components/input/input.variants.test.ts +32 -0
- package/src/components/input/input.variants.ts +13 -1
- package/src/components/item/AGENTS.md +92 -0
- package/src/components/item/index.ts +25 -0
- package/src/components/item/item-actions.tsx +28 -0
- package/src/components/item/item-content.tsx +15 -0
- package/src/components/item/item-description.tsx +12 -0
- package/src/components/item/item-footer.tsx +13 -0
- package/src/components/item/item-group.tsx +32 -0
- package/src/components/item/item-header.tsx +13 -0
- package/src/components/item/item-media.tsx +37 -0
- package/src/components/item/item-separator.tsx +13 -0
- package/src/components/item/item-title.tsx +17 -0
- package/src/components/item/item.context.tsx +66 -0
- package/src/components/item/item.tsx +218 -0
- package/src/components/item/item.types.ts +12 -0
- package/src/components/item/item.variants.test.ts +263 -0
- package/src/components/item/item.variants.ts +200 -0
- package/src/components/kpi/AGENTS.md +125 -0
- package/src/components/kpi/index.ts +45 -0
- package/src/components/kpi/kpi-action.tsx +12 -0
- package/src/components/kpi/kpi-content.tsx +35 -0
- package/src/components/kpi/kpi-footer.tsx +52 -0
- package/src/components/kpi/kpi-group.tsx +70 -0
- package/src/components/kpi/kpi-header.tsx +25 -0
- package/src/components/kpi/kpi-icon.tsx +28 -0
- package/src/components/kpi/kpi-sparkline.tsx +145 -0
- package/src/components/kpi/kpi-stat.tsx +20 -0
- package/src/components/kpi/kpi-title.tsx +19 -0
- package/src/components/kpi/kpi-trend.tsx +105 -0
- package/src/components/kpi/kpi-value.tsx +32 -0
- package/src/components/kpi/kpi.context.tsx +111 -0
- package/src/components/kpi/kpi.tsx +154 -0
- package/src/components/kpi/kpi.types.ts +9 -0
- package/src/components/kpi/kpi.variants.test.ts +428 -0
- package/src/components/kpi/kpi.variants.ts +347 -0
- package/src/components/label/AGENTS.md +89 -0
- package/src/components/label/index.ts +10 -0
- package/src/components/label/label.tsx +97 -0
- package/src/components/label/label.variants.test.ts +176 -0
- package/src/components/label/label.variants.ts +123 -0
- package/src/components/menu/AGENTS.md +176 -0
- package/src/components/menu/index.ts +44 -0
- package/src/components/menu/menu-background.tsx +17 -0
- package/src/components/menu/menu-checkbox-item.tsx +57 -0
- package/src/components/menu/menu-content.tsx +303 -0
- package/src/components/menu/menu-item.tsx +162 -0
- package/src/components/menu/menu-label.tsx +14 -0
- package/src/components/menu/menu-radio-group.tsx +50 -0
- package/src/components/menu/menu-radio-item.tsx +57 -0
- package/src/components/menu/menu-separator.tsx +11 -0
- package/src/components/menu/menu-sub-content.tsx +72 -0
- package/src/components/menu/menu-sub-trigger.tsx +50 -0
- package/src/components/menu/menu-sub.tsx +75 -0
- package/src/components/menu/menu-trigger.tsx +57 -0
- package/src/components/menu/menu.context.tsx +214 -0
- package/src/components/menu/menu.tsx +88 -0
- package/src/components/menu/menu.types.ts +33 -0
- package/src/components/menu/menu.variants.test.ts +400 -0
- package/src/components/menu/menu.variants.ts +353 -0
- package/src/components/meter/AGENTS.md +135 -0
- package/src/components/meter/index.ts +43 -0
- package/src/components/meter/meter-fill.tsx +19 -0
- package/src/components/meter/meter-header.tsx +17 -0
- package/src/components/meter/meter-label.tsx +18 -0
- package/src/components/meter/meter-output.tsx +42 -0
- package/src/components/meter/meter-segments.tsx +83 -0
- package/src/components/meter/meter-track.tsx +16 -0
- package/src/components/meter/meter.context.tsx +62 -0
- package/src/components/meter/meter.tsx +286 -0
- package/src/components/meter/meter.types.ts +30 -0
- package/src/components/meter/meter.variants.test.ts +430 -0
- package/src/components/meter/meter.variants.ts +347 -0
- package/src/components/overlay/AGENTS.md +89 -0
- package/src/components/overlay/index.ts +27 -0
- package/src/components/overlay/overlay-portal.tsx +84 -0
- package/src/components/overlay/overlay-presence.test.ts +46 -0
- package/src/components/overlay/overlay-presence.ts +49 -0
- package/src/components/overlay/overlay-registry.test.ts +96 -0
- package/src/components/overlay/overlay-registry.ts +99 -0
- package/src/components/overlay/overlay-scrim.tsx +49 -0
- package/src/components/overlay/overlay.context.tsx +48 -0
- package/src/components/overlay/overlay.tsx +118 -0
- package/src/components/overlay/overlay.variants.test.ts +37 -0
- package/src/components/overlay/overlay.variants.ts +33 -0
- package/src/components/overlay/use-overlay-back-handler.ts +34 -0
- package/src/components/overlay/use-overlay-presence.ts +87 -0
- package/src/components/popover/AGENTS.md +138 -0
- package/src/components/popover/index.ts +48 -0
- package/src/components/popover/popover-anchor.tsx +49 -0
- package/src/components/popover/popover-arrow.tsx +89 -0
- package/src/components/popover/popover-close.tsx +73 -0
- package/src/components/popover/popover-content.tsx +235 -0
- package/src/components/popover/popover-description.tsx +18 -0
- package/src/components/popover/popover-title.tsx +33 -0
- package/src/components/popover/popover-trigger.tsx +56 -0
- package/src/components/popover/popover.context.tsx +71 -0
- package/src/components/popover/popover.position.test.ts +354 -0
- package/src/components/popover/popover.position.ts +286 -0
- package/src/components/popover/popover.tsx +123 -0
- package/src/components/popover/popover.variants.test.ts +135 -0
- package/src/components/popover/popover.variants.ts +85 -0
- package/src/components/popover/use-anchor-measure.ts +76 -0
- package/src/components/popover/use-anchored-content.ts +219 -0
- package/src/components/pressable/AGENTS.md +11 -1
- package/src/components/pressable/pressable.tsx +7 -2
- package/src/components/pressable/pressable.variants.test.ts +29 -0
- package/src/components/pressable/pressable.variants.ts +17 -0
- package/src/components/progress/AGENTS.md +120 -0
- package/src/components/progress/index.ts +40 -0
- package/src/components/progress/progress-fill.tsx +152 -0
- package/src/components/progress/progress-header.tsx +19 -0
- package/src/components/progress/progress-label.tsx +27 -0
- package/src/components/progress/progress-output.tsx +47 -0
- package/src/components/progress/progress-track.tsx +38 -0
- package/src/components/progress/progress.context.tsx +79 -0
- package/src/components/progress/progress.tsx +171 -0
- package/src/components/progress/progress.types.ts +23 -0
- package/src/components/progress/progress.variants.test.ts +335 -0
- package/src/components/progress/progress.variants.ts +284 -0
- package/src/components/progress-button/AGENTS.md +116 -0
- package/src/components/progress-button/index.ts +36 -0
- package/src/components/progress-button/progress-button-done.tsx +33 -0
- package/src/components/progress-button/progress-button-label.tsx +27 -0
- package/src/components/progress-button/progress-button.context.tsx +93 -0
- package/src/components/progress-button/progress-button.tsx +414 -0
- package/src/components/progress-button/progress-button.variants.test.ts +314 -0
- package/src/components/progress-button/progress-button.variants.ts +236 -0
- package/src/components/provider/AGENTS.md +38 -25
- package/src/components/provider/provider.tsx +38 -11
- package/src/components/radio/AGENTS.md +8 -10
- package/src/components/radio/radio.variants.test.ts +5 -6
- package/src/components/radio/radio.variants.ts +6 -7
- package/src/components/rating/AGENTS.md +143 -0
- package/src/components/rating/index.ts +43 -0
- package/src/components/rating/rating-output.tsx +39 -0
- package/src/components/rating/rating-star.tsx +70 -0
- package/src/components/rating/rating-stars.tsx +219 -0
- package/src/components/rating/rating.context.tsx +79 -0
- package/src/components/rating/rating.tsx +163 -0
- package/src/components/rating/rating.variants.test.ts +347 -0
- package/src/components/rating/rating.variants.ts +354 -0
- package/src/components/selection-mode/AGENTS.md +155 -0
- package/src/components/selection-mode/index.ts +43 -0
- package/src/components/selection-mode/selection-mode-action.tsx +65 -0
- package/src/components/selection-mode/selection-mode-bar.tsx +79 -0
- package/src/components/selection-mode/selection-mode-group.tsx +123 -0
- package/src/components/selection-mode/selection-mode-header.tsx +85 -0
- package/src/components/selection-mode/selection-mode-indicator.tsx +118 -0
- package/src/components/selection-mode/selection-mode-item.tsx +182 -0
- package/src/components/selection-mode/selection-mode.context.tsx +89 -0
- package/src/components/selection-mode/selection-mode.tsx +207 -0
- package/src/components/selection-mode/selection-mode.types.ts +36 -0
- package/src/components/selection-mode/selection-mode.variants.test.ts +292 -0
- package/src/components/selection-mode/selection-mode.variants.ts +329 -0
- package/src/components/skeleton/AGENTS.md +120 -0
- package/src/components/skeleton/index.ts +15 -0
- package/src/components/skeleton/skeleton-clock.ts +48 -0
- package/src/components/skeleton/skeleton-group.tsx +64 -0
- package/src/components/skeleton/skeleton-shimmer.tsx +80 -0
- package/src/components/skeleton/skeleton.context.tsx +45 -0
- package/src/components/skeleton/skeleton.tsx +227 -0
- package/src/components/skeleton/skeleton.variants.test.ts +245 -0
- package/src/components/skeleton/skeleton.variants.ts +222 -0
- package/src/components/slide-button/AGENTS.md +122 -0
- package/src/components/slide-button/index.ts +43 -0
- package/src/components/slide-button/slide-button-label.tsx +70 -0
- package/src/components/slide-button/slide-button-thumb.tsx +93 -0
- package/src/components/slide-button/slide-button.context.tsx +76 -0
- package/src/components/slide-button/slide-button.tsx +457 -0
- package/src/components/slide-button/slide-button.variants.test.ts +288 -0
- package/src/components/slide-button/slide-button.variants.ts +324 -0
- package/src/components/slider/index.ts +2 -0
- package/src/components/slider/slider-thumb.tsx +33 -1
- package/src/components/slider/slider.variants.test.ts +52 -0
- package/src/components/slider/slider.variants.ts +56 -2
- package/src/components/stack-card/AGENTS.md +169 -0
- package/src/components/stack-card/index.ts +37 -0
- package/src/components/stack-card/stack-card-action.tsx +66 -0
- package/src/components/stack-card/stack-card-actions.tsx +18 -0
- package/src/components/stack-card/stack-card-card.tsx +71 -0
- package/src/components/stack-card/stack-card-empty.tsx +30 -0
- package/src/components/stack-card/stack-card-slot.tsx +90 -0
- package/src/components/stack-card/stack-card-stamp.tsx +76 -0
- package/src/components/stack-card/stack-card.context.tsx +117 -0
- package/src/components/stack-card/stack-card.tsx +557 -0
- package/src/components/stack-card/stack-card.types.ts +26 -0
- package/src/components/stack-card/stack-card.variants.test.ts +355 -0
- package/src/components/stack-card/stack-card.variants.ts +360 -0
- package/src/components/steps/AGENTS.md +153 -0
- package/src/components/steps/index.ts +45 -0
- package/src/components/steps/steps-connector.tsx +31 -0
- package/src/components/steps/steps-description.tsx +26 -0
- package/src/components/steps/steps-indicator.tsx +106 -0
- package/src/components/steps/steps-item.tsx +225 -0
- package/src/components/steps/steps-panel.tsx +41 -0
- package/src/components/steps/steps-title.tsx +32 -0
- package/src/components/steps/steps-track.tsx +59 -0
- package/src/components/steps/steps.context.tsx +123 -0
- package/src/components/steps/steps.tsx +166 -0
- package/src/components/steps/steps.variants.test.ts +388 -0
- package/src/components/steps/steps.variants.ts +468 -0
- package/src/components/surface/AGENTS.md +82 -0
- package/src/components/surface/index.ts +15 -0
- package/src/components/surface/surface.context.tsx +56 -0
- package/src/components/surface/surface.tsx +87 -0
- package/src/components/surface/surface.variants.test.ts +227 -0
- package/src/components/surface/surface.variants.ts +165 -0
- package/src/components/swipe/AGENTS.md +140 -0
- package/src/components/swipe/index.ts +31 -0
- package/src/components/swipe/swipe-action.tsx +99 -0
- package/src/components/swipe/swipe-end.tsx +14 -0
- package/src/components/swipe/swipe-group.tsx +55 -0
- package/src/components/swipe/swipe-panel.tsx +55 -0
- package/src/components/swipe/swipe-start.tsx +15 -0
- package/src/components/swipe/swipe.context.tsx +135 -0
- package/src/components/swipe/swipe.tsx +393 -0
- package/src/components/swipe/swipe.types.ts +7 -0
- package/src/components/swipe/swipe.variants.test.ts +337 -0
- package/src/components/swipe/swipe.variants.ts +290 -0
- package/src/components/switch/switch.variants.ts +2 -2
- package/src/components/tabs/AGENTS.md +4 -3
- package/src/components/tabs/tabs.variants.test.ts +1 -1
- package/src/components/tabs/tabs.variants.ts +4 -6
- package/src/components/text/AGENTS.md +8 -2
- package/src/components/text/text.tsx +7 -0
- package/src/components/text/text.variants.test.ts +24 -1
- package/src/components/text/text.variants.ts +5 -0
- package/src/components/textarea/AGENTS.md +89 -0
- package/src/components/textarea/index.ts +16 -0
- package/src/components/textarea/textarea.tsx +157 -0
- package/src/components/textarea/textarea.variants.test.ts +219 -0
- package/src/components/textarea/textarea.variants.ts +171 -0
- package/src/components/toast/AGENTS.md +140 -0
- package/src/components/toast/index.ts +74 -0
- package/src/components/toast/toast-action.tsx +59 -0
- package/src/components/toast/toast-api.ts +32 -0
- package/src/components/toast/toast-close.tsx +44 -0
- package/src/components/toast/toast-content.tsx +11 -0
- package/src/components/toast/toast-description.tsx +11 -0
- package/src/components/toast/toast-indicator.tsx +34 -0
- package/src/components/toast/toast-item.tsx +308 -0
- package/src/components/toast/toast-title.tsx +13 -0
- package/src/components/toast/toast-viewport.tsx +135 -0
- package/src/components/toast/toast.context.tsx +69 -0
- package/src/components/toast/toast.store.test.ts +259 -0
- package/src/components/toast/toast.store.ts +338 -0
- package/src/components/toast/toast.timer.test.ts +64 -0
- package/src/components/toast/toast.timer.ts +51 -0
- package/src/components/toast/toast.tsx +132 -0
- package/src/components/toast/toast.variants.test.ts +393 -0
- package/src/components/toast/toast.variants.ts +304 -0
- package/src/components/toggle-button/AGENTS.md +106 -0
- package/src/components/toggle-button/index.ts +30 -0
- package/src/components/toggle-button/toggle-button-group.tsx +156 -0
- package/src/components/toggle-button/toggle-button-label.tsx +19 -0
- package/src/components/toggle-button/toggle-button.context.tsx +130 -0
- package/src/components/toggle-button/toggle-button.tsx +172 -0
- package/src/components/toggle-button/toggle-button.variants.test.ts +189 -0
- package/src/components/toggle-button/toggle-button.variants.ts +160 -0
- package/src/components/tooltip/AGENTS.md +133 -0
- package/src/components/tooltip/index.ts +28 -0
- package/src/components/tooltip/tooltip-arrow.tsx +40 -0
- package/src/components/tooltip/tooltip-content.tsx +179 -0
- package/src/components/tooltip/tooltip-description.tsx +19 -0
- package/src/components/tooltip/tooltip-text.tsx +21 -0
- package/src/components/tooltip/tooltip-title.tsx +20 -0
- package/src/components/tooltip/tooltip-trigger.tsx +103 -0
- package/src/components/tooltip/tooltip.context.tsx +68 -0
- package/src/components/tooltip/tooltip.tsx +213 -0
- package/src/components/tooltip/tooltip.variants.test.ts +231 -0
- package/src/components/tooltip/tooltip.variants.ts +177 -0
- package/src/hooks/use-calm-motion.tsx +31 -0
- package/src/lib/calm-motion.test.ts +17 -0
- package/src/lib/calm-motion.ts +23 -0
- package/src/lib/cn.parity.test.ts +62 -0
- package/src/lib/cn.test.ts +14 -0
- package/src/lib/cn.ts +11 -6
- package/src/no-gorhom.test.ts +54 -0
- package/src/styles/geometry.test.ts +1 -0
- package/src/styles/theme.css +4 -0
- package/src/styles/tokens.css +44 -0
- package/src/styles/tokens.test.ts +56 -0
- package/src/styles/tokens.ts +29 -1
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +0 -72
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +0 -135
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { type ReactElement, useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { type AccessibilityActionEvent, type LayoutChangeEvent, View, type ViewProps } from "react-native";
|
|
3
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
4
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
5
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
6
|
+
import { useThemeColor } from "../../hooks/use-theme-color";
|
|
7
|
+
import { playHaptic } from "../pressable";
|
|
8
|
+
import { useRatingPart } from "./rating.context";
|
|
9
|
+
import {
|
|
10
|
+
describeRating,
|
|
11
|
+
ratingFromOffset,
|
|
12
|
+
ratingVariants,
|
|
13
|
+
resolveRatingPaint,
|
|
14
|
+
shouldClearRating,
|
|
15
|
+
starFillOf,
|
|
16
|
+
stepRating,
|
|
17
|
+
} from "./rating.variants";
|
|
18
|
+
import { RatingStar } from "./rating-star";
|
|
19
|
+
|
|
20
|
+
export type RatingStarsProps = Omit<ViewProps, "children" | "style"> & {
|
|
21
|
+
className?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The row of stars, the surface the gesture is claimed on, and the whole
|
|
26
|
+
* accessibility surface.
|
|
27
|
+
*
|
|
28
|
+
* **It is not a `Pressable`, and neither is a star.** The reasons are
|
|
29
|
+
* `Slider.Track`'s: `Pressable`'s tap fires `onPress` on every touch-to-set, and a
|
|
30
|
+
* `Pressable` per star would nest five taps inside the row's pan and leave them
|
|
31
|
+
* to negotiate for one drag. One `Gesture.Pan()` on the row reads the star under
|
|
32
|
+
* the finger, on touch-down and throughout the drag. What is inherited is the
|
|
33
|
+
* vocabulary — `playHaptic` from `pressable.tsx`, the one haptic switch.
|
|
34
|
+
*
|
|
35
|
+
* **The value is written in `onBegin`**, because a pan activates on the first
|
|
36
|
+
* movement and a stationary tap would otherwise never set anything.
|
|
37
|
+
* `minDistance(0)` wins the touch from an enclosing scroll view, and
|
|
38
|
+
* `shouldCancelWhenOutside(false)` keeps a drag past the last star tracking it.
|
|
39
|
+
* `onFinalize` is where a clear is decided and the gesture reported finished,
|
|
40
|
+
* because it is the one callback that fires on every path.
|
|
41
|
+
*
|
|
42
|
+
* **The stars render from React state; the gesture reads a shared value.** A
|
|
43
|
+
* rating crosses at most a handful of stops in a drag, so a commit per crossing
|
|
44
|
+
* is a handful of commits and no mirror on the UI thread is worth its code. The
|
|
45
|
+
* shared value exists so the worklet can tell a crossing from a repeat without
|
|
46
|
+
* waiting on a render, and it is re-synced from React whenever the gesture ends —
|
|
47
|
+
* which is what snaps a controlled rating back when its parent rejects a value.
|
|
48
|
+
*
|
|
49
|
+
* **To assistive technology the row is one `adjustable` control.** Its value is
|
|
50
|
+
* spoken as "3.5 out of 5" and a swipe up or down steps it. A read-only rating
|
|
51
|
+
* is an `image` with the same value: something to read, not something to set.
|
|
52
|
+
*/
|
|
53
|
+
export function RatingStars({ className, onLayout, ...props }: RatingStarsProps): ReactElement {
|
|
54
|
+
const {
|
|
55
|
+
value,
|
|
56
|
+
count,
|
|
57
|
+
step,
|
|
58
|
+
color,
|
|
59
|
+
size,
|
|
60
|
+
isDisabled,
|
|
61
|
+
isInvalid,
|
|
62
|
+
isReadOnly,
|
|
63
|
+
allowClear,
|
|
64
|
+
haptic,
|
|
65
|
+
setValue,
|
|
66
|
+
commitEnd,
|
|
67
|
+
} = useRatingPart("Rating.Stars");
|
|
68
|
+
const slots = ratingVariants({ isDisabled, size });
|
|
69
|
+
const paint = resolveRatingPaint({ color, isInvalid });
|
|
70
|
+
const fillColor = useThemeColor(paint.fill);
|
|
71
|
+
const emptyColor = useThemeColor(paint.empty);
|
|
72
|
+
const isInteractive = !isDisabled && !isReadOnly;
|
|
73
|
+
|
|
74
|
+
const width = useSharedValue(0);
|
|
75
|
+
const current = useSharedValue(value);
|
|
76
|
+
const startValue = useSharedValue(value);
|
|
77
|
+
const startX = useSharedValue(0);
|
|
78
|
+
const travel = useSharedValue(0);
|
|
79
|
+
|
|
80
|
+
// Bumped when a gesture ends, purely to re-run the sync below. A controlled
|
|
81
|
+
// parent that rejects a value leaves `value` unchanged, so without the token
|
|
82
|
+
// the shared value would keep the rejected one and the next tap would compare
|
|
83
|
+
// against a number nobody holds.
|
|
84
|
+
const [settledGestures, setSettledGestures] = useState(0);
|
|
85
|
+
const settle = useCallback(() => setSettledGestures((settledCount) => settledCount + 1), []);
|
|
86
|
+
|
|
87
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: the token is the re-run trigger, see above
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
current.value = value;
|
|
90
|
+
}, [current, value, settledGestures]);
|
|
91
|
+
|
|
92
|
+
const handleLayout = useCallback(
|
|
93
|
+
(event: LayoutChangeEvent) => {
|
|
94
|
+
width.value = event.nativeEvent.layout.width;
|
|
95
|
+
onLayout?.(event);
|
|
96
|
+
},
|
|
97
|
+
[onLayout, width]
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const gesture = useMemo(() => {
|
|
101
|
+
// Beside its callers rather than at module scope, so it is captured by
|
|
102
|
+
// ordinary closure — see Slider's AGENTS.md on module-scope worklets.
|
|
103
|
+
const applyTouch = (x: number, isGrab: boolean) => {
|
|
104
|
+
"worklet";
|
|
105
|
+
const next = ratingFromOffset({ count, position: x, step, width: width.value });
|
|
106
|
+
if (next <= 0) return;
|
|
107
|
+
|
|
108
|
+
const changed = next !== current.value;
|
|
109
|
+
if (changed) {
|
|
110
|
+
current.value = next;
|
|
111
|
+
scheduleOnRN(setValue, next);
|
|
112
|
+
}
|
|
113
|
+
if (haptic !== false && (isGrab || changed)) playHaptic(haptic);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return Gesture.Pan()
|
|
117
|
+
.enabled(isInteractive)
|
|
118
|
+
.minDistance(0)
|
|
119
|
+
.shouldCancelWhenOutside(false)
|
|
120
|
+
.onBegin((event) => {
|
|
121
|
+
"worklet";
|
|
122
|
+
startValue.value = current.value;
|
|
123
|
+
startX.value = event.x;
|
|
124
|
+
travel.value = 0;
|
|
125
|
+
applyTouch(event.x, true);
|
|
126
|
+
})
|
|
127
|
+
.onUpdate((event) => {
|
|
128
|
+
"worklet";
|
|
129
|
+
const moved = Math.abs(event.x - startX.value);
|
|
130
|
+
if (moved > travel.value) travel.value = moved;
|
|
131
|
+
applyTouch(event.x, false);
|
|
132
|
+
})
|
|
133
|
+
.onFinalize(() => {
|
|
134
|
+
"worklet";
|
|
135
|
+
if (
|
|
136
|
+
shouldClearRating({
|
|
137
|
+
allowClear,
|
|
138
|
+
startValue: startValue.value,
|
|
139
|
+
touchedValue: current.value,
|
|
140
|
+
travel: travel.value,
|
|
141
|
+
})
|
|
142
|
+
) {
|
|
143
|
+
current.value = 0;
|
|
144
|
+
scheduleOnRN(setValue, 0);
|
|
145
|
+
if (haptic !== false) playHaptic(haptic);
|
|
146
|
+
}
|
|
147
|
+
scheduleOnRN(commitEnd, current.value);
|
|
148
|
+
scheduleOnRN(settle);
|
|
149
|
+
});
|
|
150
|
+
}, [
|
|
151
|
+
allowClear,
|
|
152
|
+
commitEnd,
|
|
153
|
+
count,
|
|
154
|
+
current,
|
|
155
|
+
haptic,
|
|
156
|
+
isInteractive,
|
|
157
|
+
setValue,
|
|
158
|
+
settle,
|
|
159
|
+
startValue,
|
|
160
|
+
startX,
|
|
161
|
+
step,
|
|
162
|
+
travel,
|
|
163
|
+
width,
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
const handleAccessibilityAction = useCallback(
|
|
167
|
+
(event: AccessibilityActionEvent) => {
|
|
168
|
+
if (!isInteractive) return;
|
|
169
|
+
const direction = event.nativeEvent.actionName === "increment" ? 1 : -1;
|
|
170
|
+
const next = stepRating(value, direction, step, count);
|
|
171
|
+
if (next === value) return;
|
|
172
|
+
setValue(next);
|
|
173
|
+
commitEnd(next);
|
|
174
|
+
},
|
|
175
|
+
[commitEnd, count, isInteractive, setValue, step, value]
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const glyphClassName = slots.glyph();
|
|
179
|
+
const clipClassName = slots.clip();
|
|
180
|
+
const cellClassName = slots.cell();
|
|
181
|
+
const stars = Array.from({ length: count }, (_, index) => index);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<GestureDetector gesture={gesture}>
|
|
185
|
+
<View
|
|
186
|
+
accessibilityActions={isReadOnly ? undefined : ACCESSIBILITY_ACTIONS}
|
|
187
|
+
accessibilityLabel="Rating"
|
|
188
|
+
accessibilityRole={isReadOnly ? "image" : "adjustable"}
|
|
189
|
+
accessibilityState={{ disabled: isDisabled }}
|
|
190
|
+
accessibilityValue={{ max: count, min: 0, now: value, text: describeRating(value, count) }}
|
|
191
|
+
accessible
|
|
192
|
+
className={slots.stars({ className })}
|
|
193
|
+
onAccessibilityAction={handleAccessibilityAction}
|
|
194
|
+
onLayout={handleLayout}
|
|
195
|
+
{...props}
|
|
196
|
+
>
|
|
197
|
+
{stars.map((index) => (
|
|
198
|
+
<View className={cellClassName} key={index}>
|
|
199
|
+
<RatingStar
|
|
200
|
+
clipClassName={clipClassName}
|
|
201
|
+
emptyColor={emptyColor}
|
|
202
|
+
fill={starFillOf(index, value)}
|
|
203
|
+
fillColor={fillColor}
|
|
204
|
+
glyphClassName={glyphClassName}
|
|
205
|
+
/>
|
|
206
|
+
</View>
|
|
207
|
+
))}
|
|
208
|
+
</View>
|
|
209
|
+
</GestureDetector>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
RatingStars.displayName = "DelacourUI.Rating.Stars";
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The two actions an assistive swipe on an `adjustable` maps to.
|
|
216
|
+
*
|
|
217
|
+
* Module scope on purpose: a fresh array each render is a new prop every commit.
|
|
218
|
+
*/
|
|
219
|
+
const ACCESSIBILITY_ACTIONS = [{ name: "increment" }, { name: "decrement" }] as const;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createContext, type ReactElement, type ReactNode, use } from "react";
|
|
2
|
+
import type { HapticFeedback } from "../pressable";
|
|
3
|
+
import type { RatingColor, RatingSize } from "./rating.variants";
|
|
4
|
+
|
|
5
|
+
/** What a render function inside a rating is handed. */
|
|
6
|
+
export type RatingRenderProps = {
|
|
7
|
+
/** The value, clamped to the count but not snapped — a read-only 3.7 stays 3.7. */
|
|
8
|
+
value: number;
|
|
9
|
+
/** How many stars the row draws. */
|
|
10
|
+
count: number;
|
|
11
|
+
/** The increment a touch snaps to. */
|
|
12
|
+
step: number;
|
|
13
|
+
isDisabled: boolean;
|
|
14
|
+
isInvalid: boolean;
|
|
15
|
+
isReadOnly: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type RatingContextValue = RatingRenderProps & {
|
|
19
|
+
color: RatingColor;
|
|
20
|
+
size: RatingSize;
|
|
21
|
+
/** A tap on the value already held clears it back to zero. */
|
|
22
|
+
allowClear: boolean;
|
|
23
|
+
/** Played on grab, on every star crossed and on a clear. `false` silences it. */
|
|
24
|
+
haptic: false | HapticFeedback;
|
|
25
|
+
/**
|
|
26
|
+
* Writes a new value from the **JS thread**. Called from the gesture's mirror,
|
|
27
|
+
* from an assistive action and from a clear — never in render.
|
|
28
|
+
*/
|
|
29
|
+
setValue: (next: number) => void;
|
|
30
|
+
/** Reports the value a gesture or an assistive action settled on, once. */
|
|
31
|
+
commitEnd: (next: number) => void;
|
|
32
|
+
/** The settled state a render function is handed. */
|
|
33
|
+
renderProps: RatingRenderProps;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const RatingContext = createContext<RatingContextValue | null>(null);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Supplies one rating's settled state to its own parts.
|
|
40
|
+
*
|
|
41
|
+
* Lives in its own module, importing nothing but React and types, so a part can
|
|
42
|
+
* read it without importing `./rating` and closing a cycle (AGENTS.md rule 3).
|
|
43
|
+
*/
|
|
44
|
+
export function RatingProvider({ value, children }: { value: RatingContextValue; children: ReactNode }): ReactElement {
|
|
45
|
+
return <RatingContext value={value}>{children}</RatingContext>;
|
|
46
|
+
}
|
|
47
|
+
RatingProvider.displayName = "DelacourUI.Rating.Provider";
|
|
48
|
+
|
|
49
|
+
/** The enclosing rating's state, or null outside a `<Rating>`. */
|
|
50
|
+
export function useRatingContext(): RatingContextValue | null {
|
|
51
|
+
return use(RatingContext);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Reads the enclosing rating's settled state.
|
|
56
|
+
*
|
|
57
|
+
* For a custom readout that has to match the rating it sits beside. Throws
|
|
58
|
+
* outside one — use {@link useRatingContext} where the rating is optional.
|
|
59
|
+
*/
|
|
60
|
+
export function useRating(): RatingContextValue {
|
|
61
|
+
const context = useRatingContext();
|
|
62
|
+
if (!context) {
|
|
63
|
+
throw new Error("useRating must be called inside a <Rating>.");
|
|
64
|
+
}
|
|
65
|
+
return context;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The enclosing rating's state, for a compound part that cannot work without one.
|
|
70
|
+
*
|
|
71
|
+
* Internal: deliberately not re-exported from `index.ts`.
|
|
72
|
+
*/
|
|
73
|
+
export function useRatingPart(component: string): RatingContextValue {
|
|
74
|
+
const context = useRatingContext();
|
|
75
|
+
if (!context) {
|
|
76
|
+
throw new Error(`${component} must be rendered inside a <Rating>.`);
|
|
77
|
+
}
|
|
78
|
+
return context;
|
|
79
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, useCallback, useMemo } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { useControllableState } from "../../hooks/use-controllable-state";
|
|
4
|
+
import { useFieldContext } from "../field/field.context";
|
|
5
|
+
import type { HapticFeedback } from "../pressable";
|
|
6
|
+
import { type RatingContextValue, RatingProvider, type RatingRenderProps } from "./rating.context";
|
|
7
|
+
import {
|
|
8
|
+
clampRating,
|
|
9
|
+
normalizeRatingCount,
|
|
10
|
+
normalizeRatingStep,
|
|
11
|
+
type RatingColor,
|
|
12
|
+
type RatingSize,
|
|
13
|
+
ratingVariants,
|
|
14
|
+
resolveRatingAxes,
|
|
15
|
+
} from "./rating.variants";
|
|
16
|
+
import { RatingOutput } from "./rating-output";
|
|
17
|
+
import { RatingStars } from "./rating-stars";
|
|
18
|
+
|
|
19
|
+
export type RatingProps = Omit<ViewProps, "children"> & {
|
|
20
|
+
/** Controlled value. Any number is drawn; only `step` limits what a touch can set. */
|
|
21
|
+
value?: number;
|
|
22
|
+
/** Starting value while uncontrolled. */
|
|
23
|
+
defaultValue?: number;
|
|
24
|
+
/** How many stars. A fraction rounds down; anything that is not a count falls back to 5. */
|
|
25
|
+
maxValue?: number;
|
|
26
|
+
/** The increment a touch snaps to — `1` for whole stars, `0.5` for halves. Must divide one star. */
|
|
27
|
+
step?: number;
|
|
28
|
+
/** What a filled star means. */
|
|
29
|
+
color?: RatingColor;
|
|
30
|
+
size?: RatingSize;
|
|
31
|
+
/** Blocks the gesture and fades the control. Inherited from an enclosing `Field`. */
|
|
32
|
+
isDisabled?: boolean;
|
|
33
|
+
/** Paints the stars destructive. Inherited from an enclosing `Field`. */
|
|
34
|
+
isInvalid?: boolean;
|
|
35
|
+
/** Shows the score without taking a touch, and without fading. */
|
|
36
|
+
isReadOnly?: boolean;
|
|
37
|
+
/** A tap on the value already held clears it back to zero. */
|
|
38
|
+
allowClear?: boolean;
|
|
39
|
+
/** Played on grab, on every star crossed and on a clear. `false` silences it. */
|
|
40
|
+
haptic?: false | HapticFeedback;
|
|
41
|
+
/** Fires on every change during a drag. */
|
|
42
|
+
onChange?: (value: number) => void;
|
|
43
|
+
/** Fires once, when the gesture ends. Where a network write belongs. */
|
|
44
|
+
onChangeEnd?: (value: number) => void;
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function RatingRoot({
|
|
49
|
+
value,
|
|
50
|
+
defaultValue = 0,
|
|
51
|
+
maxValue,
|
|
52
|
+
step,
|
|
53
|
+
color,
|
|
54
|
+
size,
|
|
55
|
+
isDisabled,
|
|
56
|
+
isInvalid,
|
|
57
|
+
isReadOnly,
|
|
58
|
+
allowClear = false,
|
|
59
|
+
haptic = "selection",
|
|
60
|
+
onChange,
|
|
61
|
+
onChangeEnd,
|
|
62
|
+
className,
|
|
63
|
+
children,
|
|
64
|
+
...props
|
|
65
|
+
}: RatingProps): ReactElement {
|
|
66
|
+
const field = useFieldContext();
|
|
67
|
+
const axes = resolveRatingAxes({ field, own: { color, isDisabled, isInvalid, isReadOnly, size } });
|
|
68
|
+
const count = normalizeRatingCount(maxValue);
|
|
69
|
+
const settledStep = normalizeRatingStep(step);
|
|
70
|
+
|
|
71
|
+
const [current, setCurrent] = useControllableState<number>({ defaultValue, onChange, value });
|
|
72
|
+
const settled = clampRating(current, count);
|
|
73
|
+
|
|
74
|
+
const commitEnd = useCallback((next: number) => onChangeEnd?.(next), [onChangeEnd]);
|
|
75
|
+
|
|
76
|
+
const renderProps = useMemo<RatingRenderProps>(
|
|
77
|
+
() => ({
|
|
78
|
+
count,
|
|
79
|
+
isDisabled: axes.isDisabled,
|
|
80
|
+
isInvalid: axes.isInvalid,
|
|
81
|
+
isReadOnly: axes.isReadOnly,
|
|
82
|
+
step: settledStep,
|
|
83
|
+
value: settled,
|
|
84
|
+
}),
|
|
85
|
+
[axes.isDisabled, axes.isInvalid, axes.isReadOnly, count, settled, settledStep]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const context = useMemo<RatingContextValue>(
|
|
89
|
+
() => ({
|
|
90
|
+
...renderProps,
|
|
91
|
+
allowClear,
|
|
92
|
+
color: axes.color,
|
|
93
|
+
commitEnd,
|
|
94
|
+
haptic,
|
|
95
|
+
renderProps,
|
|
96
|
+
setValue: setCurrent,
|
|
97
|
+
size: axes.size,
|
|
98
|
+
}),
|
|
99
|
+
[allowClear, axes.color, axes.size, commitEnd, haptic, renderProps, setCurrent]
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<RatingProvider value={context}>
|
|
104
|
+
<View className={ratingVariants({ isDisabled: axes.isDisabled, size: axes.size }).root({ className })} {...props}>
|
|
105
|
+
{children}
|
|
106
|
+
</View>
|
|
107
|
+
</RatingProvider>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A row of stars that reads or sets a score.
|
|
113
|
+
*
|
|
114
|
+
* The anatomy is written out: a `Rating.Stars` row, with an optional
|
|
115
|
+
* `Rating.Output` beside or below it. The root is a plain column, so a readout
|
|
116
|
+
* on the same line is a `className="flex-row items-center"` away.
|
|
117
|
+
*
|
|
118
|
+
* **The whole row is the target.** Touching down takes the star under the
|
|
119
|
+
* finger, and dragging along the row follows it — one rule, not a tap mode and a
|
|
120
|
+
* drag mode. `step={0.5}` splits every star at its centre. `allowClear` lets a
|
|
121
|
+
* tap on the value already held withdraw the rating.
|
|
122
|
+
*
|
|
123
|
+
* **Any value is drawn; only a touch is snapped.** A read-only average of 3.7
|
|
124
|
+
* fills three stars and seven tenths of the fourth, while a finger on the same
|
|
125
|
+
* control can land only on the stops `step` allows. A non-finite value draws as
|
|
126
|
+
* zero rather than breaking the row.
|
|
127
|
+
*
|
|
128
|
+
* **State works either way from one hook**: pass `value` to control it, or
|
|
129
|
+
* `defaultValue` and let it hold its own. `onChange` fires as a drag crosses
|
|
130
|
+
* each star; `onChangeEnd` fires once when the gesture ends.
|
|
131
|
+
*
|
|
132
|
+
* `isInvalid` and `isDisabled` cascade in from an enclosing `Field`, and an
|
|
133
|
+
* explicit prop still wins. `isReadOnly` is the rating's own: it takes no touch
|
|
134
|
+
* and does not fade, because a score on a review card is content, not a control
|
|
135
|
+
* that happens to be off.
|
|
136
|
+
*
|
|
137
|
+
* To assistive technology the row is one `adjustable` control whose swipe up and
|
|
138
|
+
* down step the value — see `Rating.Stars`.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* <Rating defaultValue={3}>
|
|
142
|
+
* <Rating.Stars accessibilityLabel="Your rating" />
|
|
143
|
+
* </Rating>
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* <Rating allowClear onChange={setScore} step={0.5} value={score}>
|
|
147
|
+
* <Rating.Stars accessibilityLabel="Rate this recipe" />
|
|
148
|
+
* <Rating.Output />
|
|
149
|
+
* </Rating>
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* <Rating className="flex-row items-center gap-2" isReadOnly size="sm" value={4.3}>
|
|
153
|
+
* <Rating.Stars accessibilityLabel="Average rating" />
|
|
154
|
+
* <Rating.Output>{({ value }) => `${value.toFixed(1)} · 128 reviews`}</Rating.Output>
|
|
155
|
+
* </Rating>
|
|
156
|
+
*/
|
|
157
|
+
export const Rating = Object.assign(RatingRoot, {
|
|
158
|
+
/** The row of stars — the drag surface, and the whole accessibility surface. */
|
|
159
|
+
Stars: RatingStars,
|
|
160
|
+
/** The value, formatted. Give it a function to label it yourself. */
|
|
161
|
+
Output: RatingOutput,
|
|
162
|
+
displayName: "DelacourUI.Rating",
|
|
163
|
+
});
|