@delacour/react-native-ui 0.1.0-alpha.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- 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 +5 -0
- package/src/components/badge/badge.tsx +6 -1
- package/src/components/bottom-sheet/AGENTS.md +186 -221
- 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 -207
- 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 -120
- 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 -52
- 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 -70
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +50 -105
- 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 +45 -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 +2 -1
- package/src/components/button/button.variants.test.ts +154 -0
- package/src/components/button/button.variants.ts +71 -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 +88 -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 +94 -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 +9 -1
- 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/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/screen/screen-navbar-back-button.tsx +1 -1
- 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 +72 -0
- package/src/components/surface/index.ts +15 -0
- package/src/components/surface/surface.context.tsx +54 -0
- package/src/components/surface/surface.tsx +70 -0
- package/src/components/surface/surface.variants.test.ts +180 -0
- package/src/components/surface/surface.variants.ts +133 -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/textarea/AGENTS.md +86 -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.test.ts +14 -0
- package/src/lib/cn.ts +2 -2
- 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 +11 -0
- package/src/styles/tokens.test.ts +22 -0
- package/src/styles/tokens.ts +20 -0
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +0 -72
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +0 -135
|
@@ -1,73 +1,106 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
BottomSheetHost,
|
|
3
|
+
BottomSheet as Headless,
|
|
4
|
+
type BottomSheetProps as HeadlessBottomSheetProps,
|
|
5
|
+
} from "@delacour/react-native-bottom-sheet";
|
|
6
|
+
import type { ReactElement } from "react";
|
|
7
|
+
import { Presets } from "react-native-pulsar";
|
|
8
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
9
|
+
import { useKeyboardAnimationGuard } from "../../hooks/use-keyboard-state-sync";
|
|
10
|
+
import { BottomSheetBackground } from "./bottom-sheet-background";
|
|
4
11
|
import { BottomSheetClose } from "./bottom-sheet-close";
|
|
5
12
|
import { BottomSheetContainer } from "./bottom-sheet-container";
|
|
6
13
|
import { BottomSheetContent } from "./bottom-sheet-content";
|
|
7
14
|
import { BottomSheetDescription } from "./bottom-sheet-description";
|
|
15
|
+
import { BottomSheetFlatList } from "./bottom-sheet-flat-list";
|
|
8
16
|
import { BottomSheetFooter } from "./bottom-sheet-footer";
|
|
17
|
+
import { BottomSheetHandle } from "./bottom-sheet-handle";
|
|
18
|
+
import { BottomSheetLegendList } from "./bottom-sheet-legend-list";
|
|
9
19
|
import { BottomSheetOverlay } from "./bottom-sheet-overlay";
|
|
10
|
-
import {
|
|
20
|
+
import { BottomSheetProvider } from "./bottom-sheet-provider";
|
|
11
21
|
import { BottomSheetScrollView } from "./bottom-sheet-scroll-view";
|
|
22
|
+
import { BottomSheetSectionList } from "./bottom-sheet-section-list";
|
|
23
|
+
import { BottomSheetStep, BottomSheetSteps } from "./bottom-sheet-steps";
|
|
24
|
+
import { BottomSheetTextInput } from "./bottom-sheet-text-input";
|
|
12
25
|
import { BottomSheetTitle } from "./bottom-sheet-title";
|
|
13
26
|
import { BottomSheetTrigger } from "./bottom-sheet-trigger";
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Called whenever the sheet opens or closes — by a trigger, a swipe down, a
|
|
23
|
-
* press on the backdrop, `BottomSheet.Close`, or a controlled `isOpen`.
|
|
24
|
-
*
|
|
25
|
-
* One callback for every path, deliberately. gorhom's own `onClose` fires for
|
|
26
|
-
* the gesture alone, which is the shape that makes a caller wire three
|
|
27
|
-
* handlers and still miss one.
|
|
28
|
-
*/
|
|
29
|
-
onOpenChange?: (isOpen: boolean) => void;
|
|
30
|
-
};
|
|
28
|
+
/**
|
|
29
|
+
* Every prop the engine's root takes, with two defaults this skin fills in:
|
|
30
|
+
* `bottomInset` is the device's safe-area bottom unless a caller says
|
|
31
|
+
* otherwise, and the two haptics are the library's own.
|
|
32
|
+
*/
|
|
33
|
+
export type BottomSheetProps = HeadlessBottomSheetProps;
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
/**
|
|
36
|
+
* The haptics the sheet plays, as module-scope worklets.
|
|
37
|
+
*
|
|
38
|
+
* Both run on the UI thread from inside the engine's pan, so they are
|
|
39
|
+
* `Presets.System.*` — themselves worklets — and nothing else: a JS function
|
|
40
|
+
* here would be `undefined is not a function` at the moment a finger crosses a
|
|
41
|
+
* snap point. `selection` on a snap point because that is what a picker's tick feels
|
|
42
|
+
* like, and `impactLight` on a close because letting go of a sheet is a
|
|
43
|
+
* heavier event than passing a stop, but not by much.
|
|
44
|
+
*/
|
|
45
|
+
const snapPointHaptic = Presets.System.selection;
|
|
46
|
+
const closeHaptic = Presets.System.impactLight;
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
/**
|
|
49
|
+
* The engine's root with this library's defaults on it. It renders no view of
|
|
50
|
+
* its own.
|
|
51
|
+
*
|
|
52
|
+
* Three things happen here and nowhere else in the skin:
|
|
53
|
+
*
|
|
54
|
+
* - **`bottomInset` defaults to the safe-area bottom**, read from
|
|
55
|
+
* `useSafeAreaInsets()`. The engine reserves that band under the body or the
|
|
56
|
+
* sticky footer and gives it back to the keyboard as it arrives; a caller
|
|
57
|
+
* never pads for the home indicator by hand.
|
|
58
|
+
* - **The haptics are on by default.** Pass `onSnapPointHaptic={undefined}` to
|
|
59
|
+
* turn one off, or a worklet of your own to change it.
|
|
60
|
+
* - **The stale-keyboard guard runs on mount.** A sheet mounted while
|
|
61
|
+
* `KeyboardProvider`'s shared values are pinned open by a keyboard that
|
|
62
|
+
* vanished without a `will` event would lift for a keyboard that is not
|
|
63
|
+
* there; `Screen.Footer` runs the same repair for the same reason.
|
|
64
|
+
*
|
|
65
|
+
* `topInset` is left at the engine's zero, so a `%` snap point is a fraction of the
|
|
66
|
+
* whole window — the same fraction it was before this rewrite.
|
|
67
|
+
*/
|
|
68
|
+
function BottomSheetRoot({
|
|
69
|
+
bottomInset,
|
|
70
|
+
onSnapPointHaptic = snapPointHaptic,
|
|
71
|
+
onCloseHaptic = closeHaptic,
|
|
72
|
+
...props
|
|
73
|
+
}: BottomSheetProps): ReactElement {
|
|
74
|
+
const insets = useSafeAreaInsets();
|
|
75
|
+
useKeyboardAnimationGuard();
|
|
42
76
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
77
|
+
return (
|
|
78
|
+
<Headless
|
|
79
|
+
bottomInset={bottomInset ?? insets.bottom}
|
|
80
|
+
onCloseHaptic={onCloseHaptic}
|
|
81
|
+
onSnapPointHaptic={onSnapPointHaptic}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
46
84
|
);
|
|
47
|
-
|
|
48
|
-
return <BottomSheetProvider value={context}>{children}</BottomSheetProvider>;
|
|
49
85
|
}
|
|
50
86
|
|
|
51
87
|
/**
|
|
52
88
|
* A panel that slides up from the bottom of the screen, over everything.
|
|
53
89
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
90
|
+
* A thin Uniwind skin over `@delacour/react-native-bottom-sheet`: every part
|
|
91
|
+
* here is the engine's part with this library's classes, `Text` presets and
|
|
92
|
+
* `Pressable` on it. The engine owns the state, the geometry, the gestures,
|
|
93
|
+
* the keyboard, the portal and the accessibility; this file owns what it looks
|
|
94
|
+
* like.
|
|
58
95
|
*
|
|
59
|
-
* **The app
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `BottomSheet.Footer sticky` are lifted out of the tree and handed to gorhom as
|
|
65
|
-
* render props — see `BottomSheet.Portal` and `BottomSheet.Container` for why the
|
|
66
|
-
* anatomy is worth that. Everything else renders in place.
|
|
96
|
+
* **The app mounts `BottomSheetProvider` once**, inside `DelacourProvider` and
|
|
97
|
+
* around its navigator. `DelacourProvider` cannot do it — the engine is an
|
|
98
|
+
* optional peer of this library — so the provider is exported from this
|
|
99
|
+
* subpath for the app to place. Without it a `Portal` renders where it is
|
|
100
|
+
* written, which is the inline sheet.
|
|
67
101
|
*
|
|
68
102
|
* Controlled or not, from one hook: pass `isOpen` and `onOpenChange` to own the
|
|
69
|
-
* state, or nothing at all and let the sheet hold it.
|
|
70
|
-
* locked in on first render.
|
|
103
|
+
* state, or nothing at all and let the sheet hold it.
|
|
71
104
|
*
|
|
72
105
|
* @example
|
|
73
106
|
* <BottomSheet>
|
|
@@ -90,12 +123,12 @@ function BottomSheetRoot({ children, defaultOpen = false, isOpen, onOpenChange }
|
|
|
90
123
|
* </BottomSheet>
|
|
91
124
|
*
|
|
92
125
|
* @example
|
|
93
|
-
* // Controlled, and
|
|
94
|
-
* <BottomSheet isOpen={isOpen} onOpenChange={setOpen}>
|
|
126
|
+
* // Controlled, snapped, and scrolling.
|
|
127
|
+
* <BottomSheet dynamicSizing={false} isOpen={isOpen} onOpenChange={setOpen} snapPoints={["50%", "90%"]}>
|
|
95
128
|
* <BottomSheet.Portal>
|
|
96
129
|
* <BottomSheet.Overlay />
|
|
97
130
|
* <BottomSheet.Container>
|
|
98
|
-
* <BottomSheet.
|
|
131
|
+
* <BottomSheet.ScrollView>{rows}</BottomSheet.ScrollView>
|
|
99
132
|
* </BottomSheet.Container>
|
|
100
133
|
* </BottomSheet.Portal>
|
|
101
134
|
* </BottomSheet>
|
|
@@ -103,23 +136,43 @@ function BottomSheetRoot({ children, defaultOpen = false, isOpen, onOpenChange }
|
|
|
103
136
|
export const BottomSheet = Object.assign(BottomSheetRoot, {
|
|
104
137
|
/** The control that opens the sheet. `asChild` to make a `Button` the trigger. */
|
|
105
138
|
Trigger: BottomSheetTrigger,
|
|
106
|
-
/** Everything drawn above the app
|
|
107
|
-
Portal:
|
|
108
|
-
/** The scrim.
|
|
139
|
+
/** Everything drawn above the app, teleported to the nearest host. `inline` to render in place. */
|
|
140
|
+
Portal: Headless.Portal,
|
|
141
|
+
/** The scrim. Omit it and the sheet has none. */
|
|
109
142
|
Overlay: BottomSheetOverlay,
|
|
110
|
-
/** The
|
|
143
|
+
/** The panel that moves. Brings its own `Background` and `Handle` unless you write them. */
|
|
111
144
|
Container: BottomSheetContainer,
|
|
112
|
-
/** The
|
|
145
|
+
/** The panel's surface — `bg-popover`, top corners rounded, every corner when `detached`. */
|
|
146
|
+
Background: BottomSheetBackground,
|
|
147
|
+
/** The grabber's row, and the sheet's one adjustable accessibility element. */
|
|
148
|
+
Handle: BottomSheetHandle,
|
|
149
|
+
/** The static body. A sheet sized to its content is sized to this. */
|
|
113
150
|
Content: BottomSheetContent,
|
|
114
|
-
/** A scrolling body.
|
|
151
|
+
/** A scrolling body. Sizes to its rows, capped by `maxDynamicContentSize`, or fills explicit `snapPoints`. */
|
|
115
152
|
ScrollView: BottomSheetScrollView,
|
|
153
|
+
/** A virtualised body. */
|
|
154
|
+
FlatList: BottomSheetFlatList,
|
|
155
|
+
/** A virtualised body with sticky section headers. */
|
|
156
|
+
SectionList: BottomSheetSectionList,
|
|
157
|
+
/** A `LegendList` body. Needs the optional `@legendapp/list` peer. */
|
|
158
|
+
LegendList: BottomSheetLegendList,
|
|
159
|
+
/** Controls at the bottom. `sticky` pins them over the body and rides the keyboard. */
|
|
160
|
+
Footer: BottomSheetFooter,
|
|
116
161
|
/** The dismiss control, positioned out of the content's flow. */
|
|
117
162
|
Close: BottomSheetClose,
|
|
118
|
-
/** The sheet's heading — a `Text.Header`
|
|
163
|
+
/** The sheet's heading — a `Text.Header` that labels the panel for a screen reader. */
|
|
119
164
|
Title: BottomSheetTitle,
|
|
120
165
|
/** Supporting copy under the title — a muted `Text.Paragraph`. */
|
|
121
166
|
Description: BottomSheetDescription,
|
|
122
|
-
/**
|
|
123
|
-
|
|
167
|
+
/** An `Input` registered with the sheet, so the keyboard it raises is the sheet's. */
|
|
168
|
+
TextInput: BottomSheetTextInput,
|
|
169
|
+
/** A body whose contents follow a step machine and whose height glides between them. */
|
|
170
|
+
Steps: BottomSheetSteps,
|
|
171
|
+
/** One step of a `Steps` body. */
|
|
172
|
+
Step: BottomSheetStep,
|
|
173
|
+
/** A place for sheets to teleport to other than the root — the recipe for a native modal. */
|
|
174
|
+
Host: BottomSheetHost,
|
|
175
|
+
/** The engine's provider, sharing `OverlayProvider`'s teleport host. Mount it once, inside `DelacourProvider`. */
|
|
176
|
+
Provider: BottomSheetProvider,
|
|
124
177
|
displayName: "DelacourUI.BottomSheet",
|
|
125
178
|
});
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { resolveSheetBottomInset, resolveSheetScrollEndPadding } from "@delacour/react-native-bottom-sheet/core";
|
|
2
3
|
import { declaredTokens, tokenValue } from "../../styles/theme-tokens.test";
|
|
3
4
|
import {
|
|
4
5
|
BOTTOM_SHEET_BACKDROP_INDICES,
|
|
5
6
|
BOTTOM_SHEET_CLOSE_HIT_SLOP,
|
|
6
7
|
BOTTOM_SHEET_FOOTER_GAP,
|
|
7
8
|
BOTTOM_SHEET_FOOTER_PADDING,
|
|
8
|
-
BOTTOM_SHEET_KEYBOARD_DEFAULTS,
|
|
9
9
|
BOTTOM_SHEET_OVERLAY_OPACITY,
|
|
10
10
|
BOTTOM_SHEET_OVERLAY_TOKEN,
|
|
11
11
|
bottomSheetVariants,
|
|
12
|
-
resolveFooterPlacement,
|
|
13
|
-
resolveSheetBottomInset,
|
|
14
12
|
} from "./bottom-sheet.variants";
|
|
15
13
|
|
|
16
14
|
const LIGHT = declaredTokens("light");
|
|
@@ -32,6 +30,7 @@ function colorTokens(cls: string): string[] {
|
|
|
32
30
|
}
|
|
33
31
|
|
|
34
32
|
const SLOTS = bottomSheetVariants();
|
|
33
|
+
const DETACHED = bottomSheetVariants({ detached: true });
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
36
|
* The slots this component declares, pinned rather than derived.
|
|
@@ -47,6 +46,8 @@ const SLOT_NAMES = [
|
|
|
47
46
|
"handleIndicator",
|
|
48
47
|
"content",
|
|
49
48
|
"scrollContent",
|
|
49
|
+
"steps",
|
|
50
|
+
"step",
|
|
50
51
|
"footer",
|
|
51
52
|
"stickyFooter",
|
|
52
53
|
"close",
|
|
@@ -81,8 +82,8 @@ describe("the overlay token", () => {
|
|
|
81
82
|
expect(alpha).toBeLessThan(1);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
//
|
|
85
|
-
// the
|
|
85
|
+
// Any other opacity would multiply against that alpha and land the scrim
|
|
86
|
+
// somewhere the theme did not ask for.
|
|
86
87
|
expect(BOTTOM_SHEET_OVERLAY_OPACITY).toBe(1);
|
|
87
88
|
});
|
|
88
89
|
|
|
@@ -112,74 +113,28 @@ describe("every token the slots name", () => {
|
|
|
112
113
|
|
|
113
114
|
describe("the backdrop indices", () => {
|
|
114
115
|
test("show the scrim from the first snap point and hide it only when closed", () => {
|
|
115
|
-
//
|
|
116
|
-
// on screen. A modal sheet has no resting state.
|
|
116
|
+
// A modal sheet has no resting state: presented or gone.
|
|
117
117
|
expect(BOTTOM_SHEET_BACKDROP_INDICES.appearsOnIndex).toBe(0);
|
|
118
118
|
expect(BOTTOM_SHEET_BACKDROP_INDICES.disappearsOnIndex).toBe(-1);
|
|
119
119
|
expect(BOTTOM_SHEET_BACKDROP_INDICES.disappearsOnIndex).toBeLessThan(BOTTOM_SHEET_BACKDROP_INDICES.appearsOnIndex);
|
|
120
120
|
});
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
describe("the
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
android_keyboardInputMode: "adjustResize",
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("does not leave Android on gorhom's adjustPan", () => {
|
|
133
|
-
// `adjustPan` slides the whole window up instead of resizing it, which puts
|
|
134
|
-
// a sheet's footer off-screen. `resize` is what Expo configures and what
|
|
135
|
-
// `KeyboardProvider` requires, so this is the value that matches reality.
|
|
136
|
-
expect(BOTTOM_SHEET_KEYBOARD_DEFAULTS.android_keyboardInputMode).not.toBe("adjustPan");
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("resolveFooterPlacement", () => {
|
|
141
|
-
const footer = (isSticky: boolean) => ({ isFooter: true, isSticky });
|
|
142
|
-
const other = { isFooter: false, isSticky: false };
|
|
143
|
-
|
|
144
|
-
test("reports none for children holding no footer", () => {
|
|
145
|
-
expect(resolveFooterPlacement([])).toBe("none");
|
|
146
|
-
expect(resolveFooterPlacement([other, other])).toBe("none");
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test("reports inline for a footer that did not ask to be pinned", () => {
|
|
150
|
-
expect(resolveFooterPlacement([other, footer(false)])).toBe("inline");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test("reports sticky for one that did, wherever it sits", () => {
|
|
154
|
-
expect(resolveFooterPlacement([footer(true), other])).toBe("sticky");
|
|
155
|
-
expect(resolveFooterPlacement([other, footer(true)])).toBe("sticky");
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test("lets a single sticky footer win over an inline one", () => {
|
|
159
|
-
// Two footers is not a state worth expressing, and the pinned one is the
|
|
160
|
-
// one a caller meant.
|
|
161
|
-
expect(resolveFooterPlacement([footer(false), footer(true)])).toBe("sticky");
|
|
162
|
-
expect(resolveFooterPlacement([footer(true), footer(false)])).toBe("sticky");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("never reads a sticky flag off a child that is not a footer", () => {
|
|
166
|
-
expect(resolveFooterPlacement([{ isFooter: false, isSticky: true }])).toBe("none");
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
describe("resolveSheetBottomInset", () => {
|
|
171
|
-
test("gives the safe-area band to the content when nothing is pinned below it", () => {
|
|
123
|
+
describe("the inset helpers the engine's core exports", () => {
|
|
124
|
+
// They used to live here. They moved to the engine so the engine's own footer
|
|
125
|
+
// and body could share them; the skin re-exports them for the callers that
|
|
126
|
+
// imported them from `@delacour/react-native-ui/bottom-sheet`.
|
|
127
|
+
test("give the safe-area band to static content when nothing is pinned below it", () => {
|
|
172
128
|
expect(resolveSheetBottomInset({ bottom: 34, hasStickyFooter: false })).toBe(34);
|
|
173
129
|
});
|
|
174
130
|
|
|
175
|
-
test("
|
|
176
|
-
//
|
|
177
|
-
//
|
|
131
|
+
test("hold content off a pinned footer by the gap alone, never the band", () => {
|
|
132
|
+
// The footer's own box carries the band; the content asking for it too
|
|
133
|
+
// would count it twice.
|
|
178
134
|
expect(resolveSheetBottomInset({ bottom: 34, hasStickyFooter: true })).toBe(0);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
expect(resolveSheetBottomInset({ bottom: 0, hasStickyFooter: false })).toBe(0);
|
|
135
|
+
expect(
|
|
136
|
+
resolveSheetScrollEndPadding({ bottom: 34, footerGap: BOTTOM_SHEET_FOOTER_GAP, hasStickyFooter: true })
|
|
137
|
+
).toBe(BOTTOM_SHEET_FOOTER_GAP);
|
|
183
138
|
});
|
|
184
139
|
});
|
|
185
140
|
|
|
@@ -211,7 +166,7 @@ describe("bottomSheetVariants slots", () => {
|
|
|
211
166
|
expect(declared.sort()).toEqual([...SLOT_NAMES].sort());
|
|
212
167
|
});
|
|
213
168
|
|
|
214
|
-
test("
|
|
169
|
+
test("an attached sheet's surface rounds its top corners only", () => {
|
|
215
170
|
const background = SLOTS.background();
|
|
216
171
|
expect(background).toMatch(/\brounded-t-/);
|
|
217
172
|
expect(background).not.toMatch(/\brounded-b-/);
|
|
@@ -220,6 +175,17 @@ describe("bottomSheetVariants slots", () => {
|
|
|
220
175
|
expect(background).not.toMatch(/\brounded-(?:none|xs|sm|md|lg|xl|2xl|3xl|full)\b/);
|
|
221
176
|
});
|
|
222
177
|
|
|
178
|
+
test("a detached card's surface rounds every corner, and only once", () => {
|
|
179
|
+
const background = DETACHED.background();
|
|
180
|
+
// The card floats, so its bottom edge is on screen and needs the radius.
|
|
181
|
+
expect(background).toMatch(/\brounded-2xl\b/);
|
|
182
|
+
// tailwind-merge has to have replaced the top-only radius, not stacked
|
|
183
|
+
// beside it — two radius utilities on one view is order-dependent.
|
|
184
|
+
expect(background).not.toMatch(/\brounded-t-/);
|
|
185
|
+
// The surface itself is unchanged.
|
|
186
|
+
expect(colorTokens(background)).toEqual(colorTokens(SLOTS.background()));
|
|
187
|
+
});
|
|
188
|
+
|
|
223
189
|
test("a pinned footer brings a surface and a line where an inline one does not", () => {
|
|
224
190
|
// It draws OVER the content, so without them the content scrolls straight
|
|
225
191
|
// through it. An inline footer is in the flow and needs neither.
|
|
@@ -229,10 +195,10 @@ describe("bottomSheetVariants slots", () => {
|
|
|
229
195
|
expect(SLOTS.footer()).not.toMatch(/\bborder-t\b/);
|
|
230
196
|
});
|
|
231
197
|
|
|
232
|
-
test("a pinned footer writes its vertical padding as
|
|
233
|
-
// The
|
|
234
|
-
//
|
|
235
|
-
//
|
|
198
|
+
test("a pinned footer writes its vertical padding as the engine's prop, never a class", () => {
|
|
199
|
+
// The engine measures the footer's inner box into the sheet's height, and
|
|
200
|
+
// padding handed to its `padding` prop lands on that box. A class on the
|
|
201
|
+
// outer view would be height the snap point never counts.
|
|
236
202
|
expect(SLOTS.stickyFooter()).not.toMatch(/\bp[tby]?-[\d.]+\b/);
|
|
237
203
|
expect(BOTTOM_SHEET_FOOTER_PADDING).toBeGreaterThan(0);
|
|
238
204
|
});
|
|
@@ -256,9 +222,20 @@ describe("bottomSheetVariants slots", () => {
|
|
|
256
222
|
expect(SLOTS.scrollContent()).toMatch(/\bpx-screen-gutter\b/);
|
|
257
223
|
});
|
|
258
224
|
|
|
225
|
+
test("a multi-step body keeps the gutter on the stack and the vertical padding on each step", () => {
|
|
226
|
+
// The engine measures each `Step`, not the box around the stack, so
|
|
227
|
+
// vertical padding on `steps` would be height the sheet never counts.
|
|
228
|
+
expect(SLOTS.steps()).toMatch(/\bpx-screen-gutter\b/);
|
|
229
|
+
expect(SLOTS.steps()).not.toMatch(/\bp[tby]-[\d.]+\b/);
|
|
230
|
+
expect(SLOTS.step()).not.toMatch(/\bpx-/);
|
|
231
|
+
// A step reads like static content: same gap, same top padding.
|
|
232
|
+
expect(SLOTS.step().match(/\bgap-[\d.]+\b/)?.[0]).toBe(SLOTS.content().match(/\bgap-[\d.]+\b/)?.[0]);
|
|
233
|
+
expect(SLOTS.step().match(/\bpt-[\d.]+\b/)?.[0]).toBe(SLOTS.content().match(/\bpt-[\d.]+\b/)?.[0]);
|
|
234
|
+
});
|
|
235
|
+
|
|
259
236
|
test("the overlay sets no opacity of its own", () => {
|
|
260
|
-
//
|
|
261
|
-
// would be a second writer of the same style property.
|
|
237
|
+
// The engine animates the scrim's opacity off the sheet's index; a class
|
|
238
|
+
// here would be a second writer of the same style property.
|
|
262
239
|
expect(SLOTS.overlay()).not.toMatch(/\bopacity-\d/);
|
|
263
240
|
});
|
|
264
241
|
|
|
@@ -13,45 +13,23 @@ export const BOTTOM_SHEET_OVERLAY_TOKEN = "overlay";
|
|
|
13
13
|
/**
|
|
14
14
|
* The backdrop's opacity at full appearance.
|
|
15
15
|
*
|
|
16
|
-
* **1
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* **1.** The alpha lives in `--overlay`, so any other number here would multiply
|
|
17
|
+
* against it and land the scrim somewhere the theme did not ask for. One source
|
|
18
|
+
* for the alpha, and it is the token — which is also what lets the two theme
|
|
19
|
+
* variants differ.
|
|
20
20
|
*/
|
|
21
21
|
export const BOTTOM_SHEET_OVERLAY_OPACITY = 1;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* The snap indices the scrim appears and disappears on.
|
|
24
|
+
* The snap point indices the scrim appears and disappears on.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* is only absent when the sheet is closed (`-1`).
|
|
26
|
+
* A modal sheet has no resting state: it is either presented or gone, so the
|
|
27
|
+
* scrim belongs from the FIRST snap point (`0`) and is only absent when the sheet
|
|
28
|
+
* is closed (`-1`). These are the engine's own defaults too; they are named
|
|
29
|
+
* here so the test that pins them has something to read.
|
|
31
30
|
*/
|
|
32
31
|
export const BOTTOM_SHEET_BACKDROP_INDICES = { appearsOnIndex: 0, disappearsOnIndex: -1 } as const;
|
|
33
32
|
|
|
34
|
-
/**
|
|
35
|
-
* Keyboard behaviour every `BottomSheet.Container` starts from.
|
|
36
|
-
*
|
|
37
|
-
* A const rather than three defaults spelled inline, so `bun test` pins them and
|
|
38
|
-
* a retune is one edit rather than three.
|
|
39
|
-
*
|
|
40
|
-
* - `interactive` — the sheet grows to keep its content above the keyboard,
|
|
41
|
-
* following it frame by frame rather than snapping once it has settled.
|
|
42
|
-
* - `restore` — blurring returns the sheet to the snap point it was on. Without
|
|
43
|
-
* it a sheet that grew for the keyboard stays grown over empty screen.
|
|
44
|
-
* - `adjustResize` — gorhom's own default is `adjustPan`, which leaves the
|
|
45
|
-
* Android window height alone and slides the whole view up, so a sheet's
|
|
46
|
-
* footer lands off-screen. `resize` is already what Expo configures and what
|
|
47
|
-
* `KeyboardProvider` requires, so this is the value that matches the window.
|
|
48
|
-
*/
|
|
49
|
-
export const BOTTOM_SHEET_KEYBOARD_DEFAULTS = {
|
|
50
|
-
keyboardBehavior: "interactive",
|
|
51
|
-
keyboardBlurBehavior: "restore",
|
|
52
|
-
android_keyboardInputMode: "adjustResize",
|
|
53
|
-
} as const;
|
|
54
|
-
|
|
55
33
|
/**
|
|
56
34
|
* Slop around `BottomSheet.Close`.
|
|
57
35
|
*
|
|
@@ -62,22 +40,23 @@ export const BOTTOM_SHEET_KEYBOARD_DEFAULTS = {
|
|
|
62
40
|
export const BOTTOM_SHEET_CLOSE_HIT_SLOP = 8;
|
|
63
41
|
|
|
64
42
|
/**
|
|
65
|
-
* The padding a pinned footer
|
|
43
|
+
* The padding a pinned footer's measured box carries.
|
|
66
44
|
*
|
|
67
|
-
* A number rather than a `p-4`,
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
45
|
+
* A number rather than a `p-4`, because it is handed to the engine's `padding`
|
|
46
|
+
* prop: the engine measures the footer's inner box into the dynamic snap point,
|
|
47
|
+
* and padding written there is counted where a class on the outer view would
|
|
48
|
+
* not be. The horizontal gutter still comes from the `stickyFooter` slot, which
|
|
49
|
+
* is merged after it.
|
|
71
50
|
*/
|
|
72
51
|
export const BOTTOM_SHEET_FOOTER_PADDING = 16;
|
|
73
52
|
|
|
74
53
|
/**
|
|
75
54
|
* The gap between the last of the content and a pinned footer.
|
|
76
55
|
*
|
|
77
|
-
* Also a number, because the
|
|
78
|
-
* the footer's measured height. Without it the last
|
|
79
|
-
* against the footer's hairline, which reads as content
|
|
80
|
-
* content ended.
|
|
56
|
+
* Also a number, because it is the engine's `footerGap` — added to the spacer it
|
|
57
|
+
* reserves under the body from the footer's measured height. Without it the last
|
|
58
|
+
* row of a list sits flush against the footer's hairline, which reads as content
|
|
59
|
+
* clipped rather than content ended.
|
|
81
60
|
*/
|
|
82
61
|
export const BOTTOM_SHEET_FOOTER_GAP = 16;
|
|
83
62
|
|
|
@@ -85,7 +64,7 @@ export const bottomSheetVariants = tv({
|
|
|
85
64
|
slots: {
|
|
86
65
|
/**
|
|
87
66
|
* The scrim. Carries the colour and nothing else — the fade is an animated
|
|
88
|
-
* opacity
|
|
67
|
+
* opacity the engine drives off the sheet's own index.
|
|
89
68
|
*/
|
|
90
69
|
overlay: "bg-overlay",
|
|
91
70
|
/**
|
|
@@ -94,7 +73,8 @@ export const bottomSheetVariants = tv({
|
|
|
94
73
|
* surface token nothing else in the package had claimed.
|
|
95
74
|
*
|
|
96
75
|
* Only the top corners round. The bottom edge runs off the screen, and a
|
|
97
|
-
* radius there shows as two notches of the app behind it.
|
|
76
|
+
* radius there shows as two notches of the app behind it. The `detached`
|
|
77
|
+
* variant is the exception — a floating card has a visible bottom edge.
|
|
98
78
|
*/
|
|
99
79
|
background: "rounded-t-2xl bg-popover",
|
|
100
80
|
handle: "items-center justify-center pt-3 pb-1",
|
|
@@ -102,6 +82,14 @@ export const bottomSheetVariants = tv({
|
|
|
102
82
|
content: "gap-4 px-screen-gutter pt-2",
|
|
103
83
|
/** A scrollable body's content container. Same treatment as `content`. */
|
|
104
84
|
scrollContent: "gap-4 px-screen-gutter pt-2",
|
|
85
|
+
/**
|
|
86
|
+
* A multi-step body. The gutter only: the engine measures each `Step`, not
|
|
87
|
+
* the box around the stack, so vertical padding here would be height the
|
|
88
|
+
* sheet never counts.
|
|
89
|
+
*/
|
|
90
|
+
steps: "px-screen-gutter",
|
|
91
|
+
/** One step of a multi-step body — `content` less the gutter `steps` already carries. */
|
|
92
|
+
step: "gap-4 pt-2",
|
|
105
93
|
footer: "gap-3 px-screen-gutter pt-4",
|
|
106
94
|
/**
|
|
107
95
|
* A pinned footer draws OVER the content, so unlike the inline one it has
|
|
@@ -110,9 +98,8 @@ export const bottomSheetVariants = tv({
|
|
|
110
98
|
* to overlap blank space. The same reason `Screen.Footer`'s backing lives
|
|
111
99
|
* inside its sticky view.
|
|
112
100
|
*
|
|
113
|
-
* It carries no vertical padding: that is
|
|
114
|
-
*
|
|
115
|
-
* {@link BOTTOM_SHEET_FOOTER_PADDING}.
|
|
101
|
+
* It carries no vertical padding: that is the engine's `padding` prop, so
|
|
102
|
+
* the measured box counts it. See {@link BOTTOM_SHEET_FOOTER_PADDING}.
|
|
116
103
|
*/
|
|
117
104
|
stickyFooter: "gap-3 border-border border-t bg-popover px-screen-gutter",
|
|
118
105
|
close: "absolute top-4 right-4 z-10",
|
|
@@ -131,69 +118,27 @@ export const bottomSheetVariants = tv({
|
|
|
131
118
|
*/
|
|
132
119
|
title: "pr-8",
|
|
133
120
|
},
|
|
121
|
+
variants: {
|
|
122
|
+
/**
|
|
123
|
+
* A floating card. Every corner is on screen, so every corner rounds;
|
|
124
|
+
* `rounded-2xl` replaces `rounded-t-2xl` through tailwind-merge rather
|
|
125
|
+
* than stacking beside it.
|
|
126
|
+
*/
|
|
127
|
+
detached: {
|
|
128
|
+
true: {
|
|
129
|
+
background: "rounded-2xl",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
134
133
|
});
|
|
135
134
|
|
|
136
135
|
/**
|
|
137
|
-
* There is no `description` or `scrollView` slot.
|
|
136
|
+
* There is no `description`, `panel`, `portal` or `scrollView` slot.
|
|
138
137
|
*
|
|
139
|
-
*
|
|
140
|
-
* gap column
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* caller's `className` with `cn()` instead.
|
|
138
|
+
* None has any layout of its own — a description is a `Text.Paragraph` in a
|
|
139
|
+
* gap column, the panel is a transparent frame the engine positions, a scroll
|
|
140
|
+
* view fills whatever it is given — and `tv` emits `undefined` for a slot whose
|
|
141
|
+
* class string is empty, so a slot that says nothing is a slot no test can
|
|
142
|
+
* assert against. Those parts merge the caller's `className` with `cn()` instead.
|
|
144
143
|
*/
|
|
145
144
|
export type BottomSheetVariantProps = VariantProps<typeof bottomSheetVariants>;
|
|
146
|
-
|
|
147
|
-
/** Where a `BottomSheet.Footer` among a container's children has to be rendered. */
|
|
148
|
-
export type BottomSheetFooterPlacement = "sticky" | "inline" | "none";
|
|
149
|
-
|
|
150
|
-
/** One child of a `BottomSheet.Container`, reduced to what the placement turns on. */
|
|
151
|
-
export type BottomSheetFooterFlag = {
|
|
152
|
-
isFooter: boolean;
|
|
153
|
-
isSticky: boolean;
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Whether a container's children hold a footer, and where it has to go.
|
|
158
|
-
*
|
|
159
|
-
* gorhom takes a footer as a `footerComponent` render prop rather than as a
|
|
160
|
-
* child, so a sticky one has to be lifted out of the tree it was written in and
|
|
161
|
-
* handed over; an inline one simply stays put. This is the decision, kept pure
|
|
162
|
-
* so `bun test` reaches the whole matrix — the walk over `Children.toArray` that
|
|
163
|
-
* produces the flags lives with its caller, the way `withIndicator` does in
|
|
164
|
-
* `radio.tsx`.
|
|
165
|
-
*
|
|
166
|
-
* A single sticky footer anywhere in the children wins, because two footers is
|
|
167
|
-
* not a state worth expressing and the pinned one is the one a caller meant.
|
|
168
|
-
*/
|
|
169
|
-
export function resolveFooterPlacement(flags: readonly BottomSheetFooterFlag[]): BottomSheetFooterPlacement {
|
|
170
|
-
let hasFooter = false;
|
|
171
|
-
|
|
172
|
-
for (const flag of flags) {
|
|
173
|
-
if (!flag.isFooter) continue;
|
|
174
|
-
if (flag.isSticky) return "sticky";
|
|
175
|
-
hasFooter = true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return hasFooter ? "inline" : "none";
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* The safe-area band `BottomSheet.Content` and `BottomSheet.ScrollView` reserve.
|
|
183
|
-
*
|
|
184
|
-
* Exactly one thing in the sheet pays for the home indicator. A pinned footer is
|
|
185
|
-
* the bottom-most thing there is, and its own box carries the band so its surface
|
|
186
|
-
* reaches the bottom of the screen — gorhom then adds that whole measured height
|
|
187
|
-
* to the content's reserve, so the content asking for the band as well would
|
|
188
|
-
* count it twice and leave a gap the height of the indicator above the footer.
|
|
189
|
-
* With no pinned footer the content IS the bottom-most thing and takes it.
|
|
190
|
-
*/
|
|
191
|
-
export function resolveSheetBottomInset({
|
|
192
|
-
hasStickyFooter,
|
|
193
|
-
bottom,
|
|
194
|
-
}: {
|
|
195
|
-
hasStickyFooter: boolean;
|
|
196
|
-
bottom: number;
|
|
197
|
-
}): number {
|
|
198
|
-
return hasStickyFooter ? 0 : bottom;
|
|
199
|
-
}
|