@delacour/react-native-ui 0.1.0-alpha.4 → 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 +10 -6
- package/package.json +48 -4
- 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 -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 +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 +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/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 +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
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { ScrollView, type ScrollViewProps, View, type ViewProps } from "react-native";
|
|
3
|
+
import { cn } from "../../lib/cn";
|
|
4
|
+
import { drawerVariants } from "./drawer.variants";
|
|
5
|
+
|
|
6
|
+
export type DrawerBodyProps =
|
|
7
|
+
/** Scrolls when its content is taller than the panel. The default. */
|
|
8
|
+
| ({ isScrollable?: true; className?: string; contentContainerClassName?: string } & ScrollViewProps)
|
|
9
|
+
/** A plain column, for content that fits — a few chips and a slider. */
|
|
10
|
+
| ({ isScrollable: false; className?: string } & ViewProps);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The panel's content, between the header and the footer, taking the height
|
|
14
|
+
* left over.
|
|
15
|
+
*
|
|
16
|
+
* Scrolls by default. The panel's pan waits for travel along its own axis and
|
|
17
|
+
* fails on travel across it, so a vertical scroll in a start or end drawer
|
|
18
|
+
* never moves the drawer. A top or bottom drawer's pan runs along the same axis
|
|
19
|
+
* as a vertical scroll — keep their content short, or reach for a `BottomSheet`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <Drawer.Body>
|
|
23
|
+
* <ListGroup>…</ListGroup>
|
|
24
|
+
* </Drawer.Body>
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* <Drawer.Body isScrollable={false}>…</Drawer.Body>
|
|
28
|
+
*/
|
|
29
|
+
export function DrawerBody(props: DrawerBodyProps): ReactElement {
|
|
30
|
+
const slots = drawerVariants();
|
|
31
|
+
|
|
32
|
+
if (props.isScrollable === false) {
|
|
33
|
+
const { isScrollable: _isScrollable, className, ...rest } = props;
|
|
34
|
+
return <View className={cn(slots.body(), slots.bodyContent(), className)} {...rest} />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { isScrollable: _isScrollable, className, contentContainerClassName, ...rest } = props;
|
|
38
|
+
return (
|
|
39
|
+
<ScrollView
|
|
40
|
+
className={slots.body({ className })}
|
|
41
|
+
contentContainerClassName={slots.bodyContent({ className: contentContainerClassName })}
|
|
42
|
+
{...rest}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
DrawerBody.displayName = "DelacourUI.Drawer.Body";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type ReactElement, useCallback } from "react";
|
|
2
|
+
import { IconCrossSmall } from "../../icons/central";
|
|
3
|
+
import { Slot } from "../../lib/slot";
|
|
4
|
+
import { Icon } from "../icon";
|
|
5
|
+
import { Pressable, type PressableProps } from "../pressable";
|
|
6
|
+
import { useDrawerPart } from "./drawer.context";
|
|
7
|
+
import { DRAWER_CLOSE_HIT_SLOP, drawerVariants } from "./drawer.variants";
|
|
8
|
+
|
|
9
|
+
export type DrawerCloseProps =
|
|
10
|
+
/** Donates the close to the single child — a `Button` in the footer, a navigation row. */
|
|
11
|
+
| ({ asChild: true; children: ReactElement } & Omit<PressableProps, "asChild" | "children">)
|
|
12
|
+
/** The ✕ glyph. */
|
|
13
|
+
| ({ asChild?: false; accessibilityLabel?: string } & Omit<PressableProps, "asChild" | "children">);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Closes the drawer.
|
|
17
|
+
*
|
|
18
|
+
* Without children it is a ✕: this library's `Pressable` with `fade` feedback
|
|
19
|
+
* — a scale on a glyph this small reads as a jitter — 8pt of slop because a
|
|
20
|
+
* bare glyph has no capsule to bring it toward 44pt, and `"Close"` as its
|
|
21
|
+
* label. `Drawer.Header` writes one at its trailing edge unless told not to.
|
|
22
|
+
*
|
|
23
|
+
* With `asChild` it donates the close to its child, chained ahead of the
|
|
24
|
+
* child's own `onPress` — a navigation row that should also shut the menu.
|
|
25
|
+
* Either way it is the same `setOpen(false)` the scrim, the swipe and the back
|
|
26
|
+
* button take, and it closes even a non-dismissible drawer: it is one of the
|
|
27
|
+
* drawer's own actions.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* <Drawer.Close asChild>
|
|
31
|
+
* <Button variant="secondary">Done</Button>
|
|
32
|
+
* </Drawer.Close>
|
|
33
|
+
*/
|
|
34
|
+
export function DrawerClose(props: DrawerCloseProps): ReactElement {
|
|
35
|
+
const { close } = useDrawerPart("Drawer.Close");
|
|
36
|
+
const { onPress } = props;
|
|
37
|
+
|
|
38
|
+
const handlePress = useCallback(() => {
|
|
39
|
+
close();
|
|
40
|
+
onPress?.();
|
|
41
|
+
}, [close, onPress]);
|
|
42
|
+
|
|
43
|
+
if (props.asChild) {
|
|
44
|
+
const { asChild: _asChild, children, onPress: _onPress, ...rest } = props;
|
|
45
|
+
return (
|
|
46
|
+
<Slot onPress={handlePress} {...rest}>
|
|
47
|
+
{children}
|
|
48
|
+
</Slot>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
asChild: _asChild,
|
|
54
|
+
accessibilityLabel = "Close",
|
|
55
|
+
className,
|
|
56
|
+
feedback = "fade",
|
|
57
|
+
hitSlop = DRAWER_CLOSE_HIT_SLOP,
|
|
58
|
+
onPress: _onPress,
|
|
59
|
+
...rest
|
|
60
|
+
} = props;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Pressable
|
|
64
|
+
accessibilityLabel={accessibilityLabel}
|
|
65
|
+
accessibilityRole="button"
|
|
66
|
+
className={drawerVariants().close({ className })}
|
|
67
|
+
feedback={feedback}
|
|
68
|
+
hitSlop={hitSlop}
|
|
69
|
+
onPress={handlePress}
|
|
70
|
+
{...rest}
|
|
71
|
+
>
|
|
72
|
+
<Icon color="muted-foreground" icon={IconCrossSmall} />
|
|
73
|
+
</Pressable>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
DrawerClose.displayName = "DelacourUI.Drawer.Close";
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { type ReactElement, useCallback, useEffect, useId, useMemo } from "react";
|
|
2
|
+
import {
|
|
3
|
+
AccessibilityInfo,
|
|
4
|
+
findNodeHandle,
|
|
5
|
+
I18nManager,
|
|
6
|
+
useWindowDimensions,
|
|
7
|
+
View,
|
|
8
|
+
type ViewProps,
|
|
9
|
+
} from "react-native";
|
|
10
|
+
import { GestureDetector } from "react-native-gesture-handler";
|
|
11
|
+
import Animated, { useAnimatedReaction, useAnimatedStyle, useSharedValue } from "react-native-reanimated";
|
|
12
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
13
|
+
import { Overlay, useOverlayBackHandler, useOverlayPresence } from "../overlay";
|
|
14
|
+
import { type DrawerFocusTarget, DrawerPanelProvider, type DrawerPanelValue, useDrawerPart } from "./drawer.context";
|
|
15
|
+
import {
|
|
16
|
+
type DrawerSide,
|
|
17
|
+
type DrawerSize,
|
|
18
|
+
drawerVariants,
|
|
19
|
+
resolveDrawerDragFraction,
|
|
20
|
+
resolveDrawerEdge,
|
|
21
|
+
resolveDrawerExtent,
|
|
22
|
+
resolveDrawerFrameExtent,
|
|
23
|
+
resolveDrawerInsets,
|
|
24
|
+
resolveDrawerOffset,
|
|
25
|
+
} from "./drawer.variants";
|
|
26
|
+
import { useDrawerPan } from "./use-drawer-pan";
|
|
27
|
+
|
|
28
|
+
export type DrawerContentProps = ViewProps & {
|
|
29
|
+
className?: string;
|
|
30
|
+
/** Classes for the scrim behind the panel. */
|
|
31
|
+
scrimClassName?: string;
|
|
32
|
+
/** The edge it opens from. `start` and `end` follow the layout direction. Default `"start"`. */
|
|
33
|
+
side?: DrawerSide;
|
|
34
|
+
/** The panel's width (start, end) or height (top, bottom): `sm` 62% to 280, `md` 78% to 320, `lg` 88% to 400, `full` 94%. Default `"md"`. */
|
|
35
|
+
size?: DrawerSize;
|
|
36
|
+
/** Whether a swipe toward the edge dismisses it. Default true. */
|
|
37
|
+
isSwipeDismissible?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** Moves VoiceOver / TalkBack focus to a mounted element. A no-op when it has gone. */
|
|
41
|
+
function focus(target: DrawerFocusTarget | null): void {
|
|
42
|
+
if (target === null) return;
|
|
43
|
+
const handle = findNodeHandle(target as Parameters<typeof findNodeHandle>[0]);
|
|
44
|
+
if (handle !== null) AccessibilityInfo.setAccessibilityFocus(handle);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The panel, its scrim and the portal they draw through.
|
|
49
|
+
*
|
|
50
|
+
* Renders nothing until the drawer opens, and stays mounted through the exit
|
|
51
|
+
* (`useOverlayPresence`), so the portal, the z-order and the back button all
|
|
52
|
+
* follow presence. It draws in the `modal` band — over the navigator's header
|
|
53
|
+
* and over an open bottom sheet.
|
|
54
|
+
*
|
|
55
|
+
* - **Geometry.** `side` resolves to a physical edge under `I18nManager.isRTL`;
|
|
56
|
+
* `size` to an extent along that edge's axis. The panel pads the safe-area
|
|
57
|
+
* insets on every side that meets a screen edge.
|
|
58
|
+
* - **Motion.** Opening and closing slide the panel along `progress`; under
|
|
59
|
+
* reduce motion it fades in place instead. A swipe toward the edge drags it
|
|
60
|
+
* and thins the scrim with it; see `useDrawerPan` for the release.
|
|
61
|
+
* - **Dismissal.** A scrim tap, Android back, the iOS escape gesture and the
|
|
62
|
+
* swipe close a dismissible drawer. With `isDismissible={false}` the scrim
|
|
63
|
+
* still takes the touch and only the drawer's own actions close it.
|
|
64
|
+
* - **Accessibility.** The panel is modal to assistive technology, a `dialog`
|
|
65
|
+
* labelled by the title; focus moves to the title once it has entered and
|
|
66
|
+
* back to the trigger, if still mounted, once it has gone.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* <Drawer.Content side="end" size="lg">
|
|
70
|
+
* <Drawer.Header>…</Drawer.Header>
|
|
71
|
+
* <Drawer.Body>…</Drawer.Body>
|
|
72
|
+
* </Drawer.Content>
|
|
73
|
+
*/
|
|
74
|
+
export function DrawerContent({
|
|
75
|
+
className,
|
|
76
|
+
scrimClassName,
|
|
77
|
+
side = "start",
|
|
78
|
+
size = "md",
|
|
79
|
+
isSwipeDismissible = true,
|
|
80
|
+
children,
|
|
81
|
+
style,
|
|
82
|
+
...props
|
|
83
|
+
}: DrawerContentProps): ReactElement | null {
|
|
84
|
+
const { isOpen, close, isDismissible, titleId, titleRef, triggerRef } = useDrawerPart("Drawer.Content");
|
|
85
|
+
const id = useId();
|
|
86
|
+
|
|
87
|
+
const isRTL = I18nManager.isRTL;
|
|
88
|
+
const edge = resolveDrawerEdge(side, isRTL);
|
|
89
|
+
const isHorizontal = edge === "left" || edge === "right";
|
|
90
|
+
const window = useWindowDimensions();
|
|
91
|
+
const safeArea = useSafeAreaInsets();
|
|
92
|
+
const insets = resolveDrawerInsets(edge, safeArea);
|
|
93
|
+
const extent = resolveDrawerFrameExtent(
|
|
94
|
+
edge,
|
|
95
|
+
resolveDrawerExtent(size, isHorizontal ? window.width : window.height),
|
|
96
|
+
insets
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const handleEntered = useCallback(() => focus(titleRef.current), [titleRef]);
|
|
100
|
+
const handleExited = useCallback(() => focus(triggerRef.current), [triggerRef]);
|
|
101
|
+
const { isPresent, isReduced, progress } = useOverlayPresence({
|
|
102
|
+
isOpen,
|
|
103
|
+
onEntered: handleEntered,
|
|
104
|
+
onExited: handleExited,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
useOverlayBackHandler({ id, isEnabled: isPresent && isDismissible, onBack: close });
|
|
108
|
+
|
|
109
|
+
const drag = useSharedValue(0);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (isOpen) drag.value = 0;
|
|
112
|
+
}, [isOpen, drag]);
|
|
113
|
+
|
|
114
|
+
const pan = useDrawerPan({
|
|
115
|
+
drag,
|
|
116
|
+
edge,
|
|
117
|
+
extent,
|
|
118
|
+
isEnabled: isSwipeDismissible && isDismissible,
|
|
119
|
+
onDismiss: close,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// The scrim fades with presence and thins as a drag carries the panel out.
|
|
123
|
+
// A shared value of its own, because `Overlay.Scrim` takes a writable one.
|
|
124
|
+
const scrimProgress = useSharedValue(0);
|
|
125
|
+
useAnimatedReaction(
|
|
126
|
+
() => progress.value * (1 - resolveDrawerDragFraction(edge, drag.value, extent)),
|
|
127
|
+
(visibility) => {
|
|
128
|
+
scrimProgress.value = visibility;
|
|
129
|
+
},
|
|
130
|
+
[edge, extent]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const panelStyle = useAnimatedStyle(() => {
|
|
134
|
+
const slide = isReduced ? { translateX: 0, translateY: 0 } : resolveDrawerOffset(edge, extent, progress.value);
|
|
135
|
+
return {
|
|
136
|
+
opacity: isReduced ? progress.value : 1,
|
|
137
|
+
transform: [
|
|
138
|
+
{ translateX: slide.translateX + (isHorizontal ? drag.value : 0) },
|
|
139
|
+
{ translateY: slide.translateY + (isHorizontal ? 0 : drag.value) },
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
}, [edge, extent, isHorizontal, isReduced]);
|
|
143
|
+
|
|
144
|
+
const panel = useMemo<DrawerPanelValue>(() => ({ edge, side }), [edge, side]);
|
|
145
|
+
|
|
146
|
+
if (!isPresent) return null;
|
|
147
|
+
|
|
148
|
+
const slots = drawerVariants({ edge });
|
|
149
|
+
const onEscape = isDismissible ? close : undefined;
|
|
150
|
+
const frame = {
|
|
151
|
+
height: isHorizontal ? undefined : extent,
|
|
152
|
+
paddingBottom: insets.bottom,
|
|
153
|
+
paddingLeft: insets.left,
|
|
154
|
+
paddingRight: insets.right,
|
|
155
|
+
paddingTop: insets.top,
|
|
156
|
+
width: isHorizontal ? extent : undefined,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<Overlay.Portal id={id} layer="modal">
|
|
161
|
+
<Overlay.Scrim
|
|
162
|
+
className={slots.scrim({ className: scrimClassName })}
|
|
163
|
+
onDismiss={onEscape}
|
|
164
|
+
progress={scrimProgress}
|
|
165
|
+
/>
|
|
166
|
+
<View className={slots.positioner()} pointerEvents="box-none" style={{ direction: "ltr" }}>
|
|
167
|
+
<GestureDetector gesture={pan}>
|
|
168
|
+
<Animated.View
|
|
169
|
+
accessibilityLabelledBy={titleId}
|
|
170
|
+
accessibilityViewIsModal
|
|
171
|
+
className={slots.content({ className })}
|
|
172
|
+
onAccessibilityEscape={onEscape}
|
|
173
|
+
role="dialog"
|
|
174
|
+
style={[frame, panelStyle, style]}
|
|
175
|
+
{...props}
|
|
176
|
+
>
|
|
177
|
+
<View className={slots.inner()} style={{ direction: isRTL ? "rtl" : "ltr" }}>
|
|
178
|
+
<DrawerPanelProvider value={panel}>{children}</DrawerPanelProvider>
|
|
179
|
+
</View>
|
|
180
|
+
</Animated.View>
|
|
181
|
+
</GestureDetector>
|
|
182
|
+
</View>
|
|
183
|
+
</Overlay.Portal>
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
DrawerContent.displayName = "DelacourUI.Drawer.Content";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Text, type TextPresetProps } from "../text";
|
|
4
|
+
import { useDrawerPart } from "./drawer.context";
|
|
5
|
+
|
|
6
|
+
export type DrawerDescriptionProps = TextPresetProps;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Supporting copy under the title.
|
|
10
|
+
*
|
|
11
|
+
* *Is* a `Text.Paragraph`, muted by default so the title and this read as a
|
|
12
|
+
* hierarchy. `color` is an ordinary prop. It has no slot in `drawerVariants` —
|
|
13
|
+
* see the note there.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* <Drawer.Description>Signed in as aria@harbour.studio</Drawer.Description>
|
|
17
|
+
*/
|
|
18
|
+
export function DrawerDescription({ className, color = "muted", ...props }: DrawerDescriptionProps): ReactElement {
|
|
19
|
+
const { descriptionId } = useDrawerPart("Drawer.Description");
|
|
20
|
+
|
|
21
|
+
return <Text.Paragraph className={cn(className)} color={color} nativeID={descriptionId} {...props} />;
|
|
22
|
+
}
|
|
23
|
+
DrawerDescription.displayName = "DelacourUI.Drawer.Description";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { drawerVariants } from "./drawer.variants";
|
|
4
|
+
|
|
5
|
+
export type DrawerFooterProps = ViewProps & { className?: string };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Actions at the panel's far end, under a hairline: a row, right-aligned in a
|
|
9
|
+
* left-to-right layout. It pushes itself to the panel's end with `mt-auto`, so
|
|
10
|
+
* it sits at the bottom however short the content is — with or without a body.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <Drawer.Footer>
|
|
14
|
+
* <Button variant="secondary" onPress={reset}>Reset</Button>
|
|
15
|
+
* <Drawer.Close asChild><Button>Show results</Button></Drawer.Close>
|
|
16
|
+
* </Drawer.Footer>
|
|
17
|
+
*/
|
|
18
|
+
export function DrawerFooter({ className, ...props }: DrawerFooterProps): ReactElement {
|
|
19
|
+
return <View className={drawerVariants().footer({ className })} {...props} />;
|
|
20
|
+
}
|
|
21
|
+
DrawerFooter.displayName = "DelacourUI.Drawer.Footer";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { drawerVariants } from "./drawer.variants";
|
|
4
|
+
import { DrawerClose } from "./drawer-close";
|
|
5
|
+
|
|
6
|
+
export type DrawerHeaderProps = ViewProps & {
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Hide the ✕ the header writes at its trailing edge. Default false. */
|
|
9
|
+
isCloseHidden?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The top of the panel: `Drawer.Title` and `Drawer.Description` in a column
|
|
14
|
+
* that takes the row's slack, and a ✕ at the trailing edge.
|
|
15
|
+
*
|
|
16
|
+
* The ✕ is in the flow, not pinned to a corner, so a long title wraps before
|
|
17
|
+
* it rather than under it, and it lands on the correct side under RTL with no
|
|
18
|
+
* rule of its own. `isCloseHidden` drops it — a navigation drawer whose rows
|
|
19
|
+
* all close it, or a drawer with a `Close` in its footer.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <Drawer.Header>
|
|
23
|
+
* <Drawer.Title>Filters</Drawer.Title>
|
|
24
|
+
* <Drawer.Description>12 results</Drawer.Description>
|
|
25
|
+
* </Drawer.Header>
|
|
26
|
+
*/
|
|
27
|
+
export function DrawerHeader({
|
|
28
|
+
className,
|
|
29
|
+
isCloseHidden = false,
|
|
30
|
+
children,
|
|
31
|
+
...props
|
|
32
|
+
}: DrawerHeaderProps): ReactElement {
|
|
33
|
+
const slots = drawerVariants();
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<View className={slots.header({ className })} {...props}>
|
|
37
|
+
<View className={slots.heading()}>{children}</View>
|
|
38
|
+
{isCloseHidden ? null : <DrawerClose />}
|
|
39
|
+
</View>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
DrawerHeader.displayName = "DelacourUI.Drawer.Header";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ReactElement, useCallback } from "react";
|
|
2
|
+
import { cn } from "../../lib/cn";
|
|
3
|
+
import { Text, type TextPresetProps } from "../text";
|
|
4
|
+
import { type DrawerFocusTarget, useDrawerPart } from "./drawer.context";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A `Text.Header`'s props less `ref`: the drawer holds the title's ref itself,
|
|
8
|
+
* to move accessibility focus onto it.
|
|
9
|
+
*/
|
|
10
|
+
export type DrawerTitleProps = Omit<TextPresetProps, "ref">;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The drawer's heading.
|
|
14
|
+
*
|
|
15
|
+
* *Is* a `Text.Header`. It carries the `nativeID` the panel is labelled by on
|
|
16
|
+
* Android, and it is where accessibility focus lands once the panel has
|
|
17
|
+
* finished entering, so a screen reader announces what the drawer is first.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* <Drawer.Title>Menu</Drawer.Title>
|
|
21
|
+
*/
|
|
22
|
+
export function DrawerTitle({ className, ...props }: DrawerTitleProps): ReactElement {
|
|
23
|
+
const { titleId, titleRef } = useDrawerPart("Drawer.Title");
|
|
24
|
+
const setTitleRef = useCallback(
|
|
25
|
+
(node: DrawerFocusTarget | null) => {
|
|
26
|
+
titleRef.current = node;
|
|
27
|
+
},
|
|
28
|
+
[titleRef]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Text.Header accessibilityRole="header" className={cn(className)} nativeID={titleId} ref={setTitleRef} {...props} />
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
DrawerTitle.displayName = "DelacourUI.Drawer.Title";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ReactElement, useCallback } from "react";
|
|
2
|
+
import { composeRefs } from "../../lib/compose-refs";
|
|
3
|
+
import { Slot } from "../../lib/slot";
|
|
4
|
+
import { Pressable, type PressableProps } from "../pressable";
|
|
5
|
+
import { useDrawerPart } from "./drawer.context";
|
|
6
|
+
|
|
7
|
+
export type DrawerTriggerProps = PressableProps;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The control that opens the drawer.
|
|
11
|
+
*
|
|
12
|
+
* On its own it is this library's `Pressable`. **`asChild` donates the press
|
|
13
|
+
* rather than wrapping the child**, the way `BottomSheet.Trigger` does: two tap
|
|
14
|
+
* gestures in an ancestor/descendant pair are not simultaneous, so a `Button`
|
|
15
|
+
* wrapped in a pressable trigger would win the touch and the drawer would never
|
|
16
|
+
* open. With `asChild` the open is chained ahead of the child's own `onPress`.
|
|
17
|
+
*
|
|
18
|
+
* The trigger's ref is kept so accessibility focus can return to it when the
|
|
19
|
+
* drawer closes.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <Drawer.Trigger asChild>
|
|
23
|
+
* <Button accessibilityLabel="Menu" size="icon-md" variant="ghost">
|
|
24
|
+
* <Icon icon={IconMenu} />
|
|
25
|
+
* </Button>
|
|
26
|
+
* </Drawer.Trigger>
|
|
27
|
+
*/
|
|
28
|
+
export function DrawerTrigger({ asChild = false, children, onPress, ref, ...props }: DrawerTriggerProps): ReactElement {
|
|
29
|
+
const { setOpen, triggerRef } = useDrawerPart("Drawer.Trigger");
|
|
30
|
+
|
|
31
|
+
const handlePress = useCallback(() => {
|
|
32
|
+
setOpen(true);
|
|
33
|
+
onPress?.();
|
|
34
|
+
}, [setOpen, onPress]);
|
|
35
|
+
|
|
36
|
+
const composedRef = composeRefs(ref, (node) => {
|
|
37
|
+
triggerRef.current = node;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (asChild) {
|
|
41
|
+
return (
|
|
42
|
+
<Slot onPress={handlePress} ref={composedRef} {...props}>
|
|
43
|
+
{children}
|
|
44
|
+
</Slot>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Pressable accessibilityRole="button" onPress={handlePress} ref={composedRef} {...props}>
|
|
50
|
+
{children}
|
|
51
|
+
</Pressable>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
DrawerTrigger.displayName = "DelacourUI.Drawer.Trigger";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { createContext, type ReactElement, type ReactNode, type RefObject, use } from "react";
|
|
2
|
+
import { I18nManager } from "react-native";
|
|
3
|
+
import { type DrawerEdge, type DrawerSide, resolveDrawerEdge } from "./drawer.variants";
|
|
4
|
+
|
|
5
|
+
/** Anything `AccessibilityInfo.setAccessibilityFocus` can be pointed at through `findNodeHandle`. */
|
|
6
|
+
export type DrawerFocusTarget = object;
|
|
7
|
+
|
|
8
|
+
export type DrawerContextValue = {
|
|
9
|
+
/** Whether the drawer is open — the owner's state, not the exit animation's. */
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
/** Opens or closes it. Reports through `onOpenChange` only when the value changes. */
|
|
12
|
+
setOpen: (isOpen: boolean) => void;
|
|
13
|
+
/** `setOpen(false)`, for an action inside the panel. */
|
|
14
|
+
close: () => void;
|
|
15
|
+
/** Whether the scrim, Android back and the iOS escape gesture close it. */
|
|
16
|
+
isDismissible: boolean;
|
|
17
|
+
/** The logical side `Drawer.Content` opens from. `"start"` outside the content. */
|
|
18
|
+
side: DrawerSide;
|
|
19
|
+
/** The physical edge that side resolves to under the layout direction. */
|
|
20
|
+
edge: DrawerEdge;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** The root's state, before `Drawer.Content` adds its side. */
|
|
24
|
+
export type DrawerRootValue = Omit<DrawerContextValue, "side" | "edge"> & {
|
|
25
|
+
/** The `nativeID` `Drawer.Title` carries and the panel is labelled by. */
|
|
26
|
+
titleId: string;
|
|
27
|
+
/** The `nativeID` `Drawer.Description` carries. */
|
|
28
|
+
descriptionId: string;
|
|
29
|
+
/** The trigger, so focus can return to it on close. */
|
|
30
|
+
triggerRef: RefObject<DrawerFocusTarget | null>;
|
|
31
|
+
/** The title, so focus can land on it on open. */
|
|
32
|
+
titleRef: RefObject<DrawerFocusTarget | null>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** The side and edge `Drawer.Content` resolved, for the parts inside it. */
|
|
36
|
+
export type DrawerPanelValue = { side: DrawerSide; edge: DrawerEdge };
|
|
37
|
+
|
|
38
|
+
const DrawerContext = createContext<DrawerRootValue | null>(null);
|
|
39
|
+
|
|
40
|
+
const DrawerPanelContext = createContext<DrawerPanelValue>({
|
|
41
|
+
edge: resolveDrawerEdge("start", I18nManager.isRTL),
|
|
42
|
+
side: "start",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Supplies the drawer's state to its parts.
|
|
47
|
+
*
|
|
48
|
+
* Lives in its own module, importing nothing but `drawer.variants`, so a part
|
|
49
|
+
* can read it without importing `./drawer` — that import would close a cycle,
|
|
50
|
+
* and Metro serves a partially initialised module for one.
|
|
51
|
+
*/
|
|
52
|
+
export function DrawerProvider({ value, children }: { value: DrawerRootValue; children: ReactNode }): ReactElement {
|
|
53
|
+
return <DrawerContext value={value}>{children}</DrawerContext>;
|
|
54
|
+
}
|
|
55
|
+
DrawerProvider.displayName = "DelacourUI.Drawer.Provider";
|
|
56
|
+
|
|
57
|
+
/** Supplies the resolved side and edge to the parts inside `Drawer.Content`. */
|
|
58
|
+
export function DrawerPanelProvider({
|
|
59
|
+
value,
|
|
60
|
+
children,
|
|
61
|
+
}: {
|
|
62
|
+
value: DrawerPanelValue;
|
|
63
|
+
children: ReactNode;
|
|
64
|
+
}): ReactElement {
|
|
65
|
+
return <DrawerPanelContext value={value}>{children}</DrawerPanelContext>;
|
|
66
|
+
}
|
|
67
|
+
DrawerPanelProvider.displayName = "DelacourUI.Drawer.PanelProvider";
|
|
68
|
+
|
|
69
|
+
/** The enclosing drawer's context, or null outside a `<Drawer>`. */
|
|
70
|
+
export function useDrawerContext(): DrawerContextValue | null {
|
|
71
|
+
const root = use(DrawerContext);
|
|
72
|
+
const panel = use(DrawerPanelContext);
|
|
73
|
+
if (!root) return null;
|
|
74
|
+
return {
|
|
75
|
+
close: root.close,
|
|
76
|
+
edge: panel.edge,
|
|
77
|
+
isDismissible: root.isDismissible,
|
|
78
|
+
isOpen: root.isOpen,
|
|
79
|
+
setOpen: root.setOpen,
|
|
80
|
+
side: panel.side,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reads the enclosing drawer's open state, its setter and — inside
|
|
86
|
+
* `Drawer.Content` — the side it opened from and the edge that resolved to.
|
|
87
|
+
*
|
|
88
|
+
* Lets a custom row close the drawer after it navigates, without the caller
|
|
89
|
+
* threading a setter down. Throws outside a `<Drawer>`.
|
|
90
|
+
*/
|
|
91
|
+
export function useDrawer(): DrawerContextValue {
|
|
92
|
+
const context = useDrawerContext();
|
|
93
|
+
if (!context) {
|
|
94
|
+
throw new Error("useDrawer must be called inside a <Drawer>.");
|
|
95
|
+
}
|
|
96
|
+
return context;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The root's full context, for a part that cannot work without one. Internal:
|
|
101
|
+
* not re-exported from `index.ts`.
|
|
102
|
+
*/
|
|
103
|
+
export function useDrawerPart(component: string): DrawerRootValue {
|
|
104
|
+
const context = use(DrawerContext);
|
|
105
|
+
if (!context) {
|
|
106
|
+
throw new Error(`${component} must be rendered inside a <Drawer>.`);
|
|
107
|
+
}
|
|
108
|
+
return context;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The side and edge `Drawer.Content` resolved. */
|
|
112
|
+
export function useDrawerPanel(): DrawerPanelValue {
|
|
113
|
+
return use(DrawerPanelContext);
|
|
114
|
+
}
|