@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
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, useCallback, useLayoutEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { type LayoutChangeEvent, View, type ViewProps } from "react-native";
|
|
3
|
+
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
|
4
|
+
import Animated, {
|
|
5
|
+
Easing,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withSequence,
|
|
9
|
+
withSpring,
|
|
10
|
+
withTiming,
|
|
11
|
+
} from "react-native-reanimated";
|
|
12
|
+
import { scheduleOnRN } from "react-native-worklets";
|
|
13
|
+
import { useCalmMotion } from "../../hooks/use-calm-motion";
|
|
14
|
+
import { playHaptic } from "../pressable";
|
|
15
|
+
import { useCalendarPart } from "./calendar.context";
|
|
16
|
+
import { type CalendarCell, formatMonthYear, monthGrid, serialiseDate } from "./calendar.date";
|
|
17
|
+
import { calendarVariants, resolvePageDirection } from "./calendar.variants";
|
|
18
|
+
import { CalendarDay } from "./calendar-day";
|
|
19
|
+
import { CalendarPicker } from "./calendar-picker";
|
|
20
|
+
|
|
21
|
+
export type CalendarGridProps = Omit<ViewProps, "children"> & {
|
|
22
|
+
className?: string;
|
|
23
|
+
/** Draws one cell. Omitted, a `Calendar.Day` for its date. */
|
|
24
|
+
renderDay?: (cell: CalendarCell) => ReactNode;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** How far a sideways drag travels before the grid claims it, and a vertical one before it gives up. */
|
|
28
|
+
const PAN_ACTIVATE_X = 12;
|
|
29
|
+
const PAN_FAIL_Y = 12;
|
|
30
|
+
|
|
31
|
+
/** How far a drag past a bound still follows the finger, as a fraction — enough to feel the edge. */
|
|
32
|
+
const BOUND_RESISTANCE = 0.2;
|
|
33
|
+
|
|
34
|
+
const SLIDE = { duration: 200, easing: Easing.out(Easing.cubic) } as const;
|
|
35
|
+
const SETTLE_SPRING = { damping: 20, stiffness: 260 } as const;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The six weeks of the visible month, and the surface a swipe pages on.
|
|
39
|
+
*
|
|
40
|
+
* **Paging is a slide out and a slide in.** A released swipe slides the old month off the side it
|
|
41
|
+
* was dragged towards, the month changes, and the new one slides in from the other side — the
|
|
42
|
+
* same entrance the arrows give it. Both off-screen positions are clipped, so the frame where the
|
|
43
|
+
* month swaps is never seen. With calm motion on, both slides are skipped and the page simply
|
|
44
|
+
* swaps; the page change itself is behaviour, not decoration, and always happens.
|
|
45
|
+
*
|
|
46
|
+
* **A drag towards a bound resists** rather than moving freely, then settles back with a light
|
|
47
|
+
* impact haptic — the edge is felt instead of the month silently refusing to turn.
|
|
48
|
+
*
|
|
49
|
+
* **The grid claims a sideways drag and gives up a vertical one**, so it lives inside a scrolling
|
|
50
|
+
* screen or a sheet without stealing the scroll.
|
|
51
|
+
*
|
|
52
|
+
* It also hosts `Calendar.Picker` over itself while a jump view is open, and hides its weeks
|
|
53
|
+
* rather than unmounting them, so the calendar's height never moves.
|
|
54
|
+
*/
|
|
55
|
+
export function CalendarGrid({ className, renderDay, ...props }: CalendarGridProps): ReactElement {
|
|
56
|
+
const calendar = useCalendarPart("Calendar.Grid");
|
|
57
|
+
const {
|
|
58
|
+
canGoNext,
|
|
59
|
+
canGoPrev,
|
|
60
|
+
goNext,
|
|
61
|
+
goPrev,
|
|
62
|
+
isDisabled,
|
|
63
|
+
label,
|
|
64
|
+
locale,
|
|
65
|
+
pageDirection,
|
|
66
|
+
size,
|
|
67
|
+
swipeToPage,
|
|
68
|
+
variant,
|
|
69
|
+
view,
|
|
70
|
+
} = calendar;
|
|
71
|
+
const { visibleMonth, weekStartsOn } = calendar;
|
|
72
|
+
const slots = calendarVariants({ size, variant });
|
|
73
|
+
const isCalm = useCalmMotion();
|
|
74
|
+
|
|
75
|
+
const width = useSharedValue(0);
|
|
76
|
+
const translateX = useSharedValue(0);
|
|
77
|
+
|
|
78
|
+
const handleLayout = useCallback(
|
|
79
|
+
(event: LayoutChangeEvent) => {
|
|
80
|
+
width.value = event.nativeEvent.layout.width;
|
|
81
|
+
},
|
|
82
|
+
[width]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const page = useCallback((direction: -1 | 1) => (direction === 1 ? goNext() : goPrev()), [goNext, goPrev]);
|
|
86
|
+
|
|
87
|
+
const pan = useMemo(
|
|
88
|
+
() =>
|
|
89
|
+
Gesture.Pan()
|
|
90
|
+
.enabled(swipeToPage && view === "days" && !isDisabled)
|
|
91
|
+
.activeOffsetX([-PAN_ACTIVATE_X, PAN_ACTIVATE_X])
|
|
92
|
+
.failOffsetY([-PAN_FAIL_Y, PAN_FAIL_Y])
|
|
93
|
+
.onUpdate((event) => {
|
|
94
|
+
"worklet";
|
|
95
|
+
const blocked = (event.translationX > 0 && !canGoPrev) || (event.translationX < 0 && !canGoNext);
|
|
96
|
+
translateX.value = blocked ? event.translationX * BOUND_RESISTANCE : event.translationX;
|
|
97
|
+
})
|
|
98
|
+
.onEnd((event) => {
|
|
99
|
+
"worklet";
|
|
100
|
+
const direction = resolvePageDirection({
|
|
101
|
+
canGoNext,
|
|
102
|
+
canGoPrev,
|
|
103
|
+
translationX: event.translationX,
|
|
104
|
+
velocityX: event.velocityX,
|
|
105
|
+
width: width.value,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (direction === 0) {
|
|
109
|
+
const blocked = (event.translationX > 0 && !canGoPrev) || (event.translationX < 0 && !canGoNext);
|
|
110
|
+
if (blocked) playHaptic("light");
|
|
111
|
+
translateX.value = withSpring(0, SETTLE_SPRING);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
playHaptic("selection");
|
|
116
|
+
if (isCalm) {
|
|
117
|
+
translateX.value = 0;
|
|
118
|
+
scheduleOnRN(page, direction);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
translateX.value = withTiming(-direction * width.value, SLIDE, (finished) => {
|
|
122
|
+
"worklet";
|
|
123
|
+
if (finished) scheduleOnRN(page, direction);
|
|
124
|
+
});
|
|
125
|
+
})
|
|
126
|
+
.onFinalize((_event, success) => {
|
|
127
|
+
"worklet";
|
|
128
|
+
if (!success) translateX.value = withSpring(0, SETTLE_SPRING);
|
|
129
|
+
}),
|
|
130
|
+
[canGoNext, canGoPrev, isCalm, isDisabled, page, swipeToPage, translateX, view, width]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// The new month slides in from the side the old one left by. Skipped on the first render — a
|
|
134
|
+
// calendar does not animate onto the screen — and for a jump, which has no side.
|
|
135
|
+
const monthKey = serialiseDate(visibleMonth);
|
|
136
|
+
const previousKey = useRef(monthKey);
|
|
137
|
+
useLayoutEffect(() => {
|
|
138
|
+
if (previousKey.current === monthKey) return;
|
|
139
|
+
previousKey.current = monthKey;
|
|
140
|
+
if (isCalm || pageDirection === 0 || width.value <= 0) {
|
|
141
|
+
translateX.value = 0;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
translateX.value = withSequence(withTiming(pageDirection * width.value, { duration: 0 }), withTiming(0, SLIDE));
|
|
145
|
+
}, [isCalm, monthKey, pageDirection, translateX, width]);
|
|
146
|
+
|
|
147
|
+
const slideStyle = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }] }));
|
|
148
|
+
|
|
149
|
+
const weeks = useMemo(() => monthGrid(visibleMonth, weekStartsOn), [visibleMonth, weekStartsOn]);
|
|
150
|
+
const isPicking = view !== "days";
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<View className={slots.grid({ className })} onLayout={handleLayout} {...props}>
|
|
154
|
+
<GestureDetector gesture={pan}>
|
|
155
|
+
<Animated.View
|
|
156
|
+
accessibilityElementsHidden={isPicking}
|
|
157
|
+
accessibilityLabel={label ?? formatMonthYear(visibleMonth, locale)}
|
|
158
|
+
role="grid"
|
|
159
|
+
className={isPicking ? "opacity-0" : undefined}
|
|
160
|
+
importantForAccessibility={isPicking ? "no-hide-descendants" : "auto"}
|
|
161
|
+
pointerEvents={isPicking ? "none" : "auto"}
|
|
162
|
+
style={slideStyle}
|
|
163
|
+
>
|
|
164
|
+
{weeks.map((week) => (
|
|
165
|
+
<View className={slots.week()} key={serialiseDate(week[0]?.date ?? visibleMonth)}>
|
|
166
|
+
{week.map((cell) =>
|
|
167
|
+
renderDay ? (
|
|
168
|
+
<View className="flex-1" key={serialiseDate(cell.date)}>
|
|
169
|
+
{renderDay(cell)}
|
|
170
|
+
</View>
|
|
171
|
+
) : (
|
|
172
|
+
<CalendarDay date={cell.date} key={serialiseDate(cell.date)} />
|
|
173
|
+
)
|
|
174
|
+
)}
|
|
175
|
+
</View>
|
|
176
|
+
))}
|
|
177
|
+
</Animated.View>
|
|
178
|
+
</GestureDetector>
|
|
179
|
+
{isPicking ? <CalendarPicker /> : null}
|
|
180
|
+
</View>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
CalendarGrid.displayName = "DelacourUI.Calendar.Grid";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { useCalendarPart } from "./calendar.context";
|
|
4
|
+
import { calendarVariants } from "./calendar.variants";
|
|
5
|
+
import { CalendarCaption } from "./calendar-caption";
|
|
6
|
+
import { CalendarNav } from "./calendar-nav";
|
|
7
|
+
|
|
8
|
+
export type CalendarHeaderProps = ViewProps & {
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The row above the grid: the previous arrow, the caption and the next arrow.
|
|
14
|
+
*
|
|
15
|
+
* Omit `children` for that default; pass them to rearrange it — both arrows on one side, say, or
|
|
16
|
+
* a caption with a "Today" button beside it.
|
|
17
|
+
*/
|
|
18
|
+
export function CalendarHeader({ className, children, ...props }: CalendarHeaderProps): ReactElement {
|
|
19
|
+
const { size, variant } = useCalendarPart("Calendar.Header");
|
|
20
|
+
const slots = calendarVariants({ size, variant });
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<View className={slots.header({ className })} {...props}>
|
|
24
|
+
{children ?? (
|
|
25
|
+
<>
|
|
26
|
+
<CalendarNav direction="prev" />
|
|
27
|
+
<CalendarCaption />
|
|
28
|
+
<CalendarNav direction="next" />
|
|
29
|
+
</>
|
|
30
|
+
)}
|
|
31
|
+
</View>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
CalendarHeader.displayName = "DelacourUI.Calendar.Header";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { IconChevronLeft, IconChevronRight } from "../../icons/central";
|
|
3
|
+
import { Button, type ButtonProps } from "../button";
|
|
4
|
+
import { Icon } from "../icon";
|
|
5
|
+
import { useCalendarPart } from "./calendar.context";
|
|
6
|
+
import { resolvePartTestID } from "./calendar.variants";
|
|
7
|
+
|
|
8
|
+
export type CalendarNavProps = Omit<ButtonProps, "children" | "onPress"> & {
|
|
9
|
+
/** Which way the arrow pages. */
|
|
10
|
+
direction: "prev" | "next";
|
|
11
|
+
/** The arrow's accessible name. Defaults to "Previous month" / "Next month" — or year, in the months view. */
|
|
12
|
+
label?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One paging arrow: a ghost icon button that disables itself at the bound it points at.
|
|
17
|
+
*
|
|
18
|
+
* In the month grid it pages a month; in the months view, a year; in the years view, where the
|
|
19
|
+
* list scrolls instead, it is disabled. A disabled calendar disables it too — a read-only one
|
|
20
|
+
* does not, because looking at another month changes nothing.
|
|
21
|
+
*/
|
|
22
|
+
export function CalendarNav({ direction, label, isDisabled, ...props }: CalendarNavProps): ReactElement {
|
|
23
|
+
const {
|
|
24
|
+
canGoNext,
|
|
25
|
+
canGoPrev,
|
|
26
|
+
goNext,
|
|
27
|
+
goPrev,
|
|
28
|
+
isDisabled: calendarDisabled,
|
|
29
|
+
testID,
|
|
30
|
+
view,
|
|
31
|
+
} = useCalendarPart("Calendar.Nav");
|
|
32
|
+
const isPrev = direction === "prev";
|
|
33
|
+
const canGo = (isPrev ? canGoPrev : canGoNext) && view !== "years";
|
|
34
|
+
const unit = view === "months" ? "year" : "month";
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Button
|
|
38
|
+
accessibilityLabel={label ?? `${isPrev ? "Previous" : "Next"} ${unit}`}
|
|
39
|
+
haptic="selection"
|
|
40
|
+
isDisabled={isDisabled ?? (calendarDisabled || !canGo)}
|
|
41
|
+
onPress={isPrev ? goPrev : goNext}
|
|
42
|
+
size="icon-sm"
|
|
43
|
+
testID={resolvePartTestID(testID, direction)}
|
|
44
|
+
variant="ghost"
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<Icon icon={isPrev ? IconChevronLeft : IconChevronRight} />
|
|
48
|
+
</Button>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
CalendarNav.displayName = "DelacourUI.Calendar.Nav";
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { type ReactElement, useCallback, useMemo, useRef } from "react";
|
|
2
|
+
import { type LayoutChangeEvent, ScrollView, View, type ViewProps } from "react-native";
|
|
3
|
+
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
|
|
4
|
+
import { useCalmMotion } from "../../hooks/use-calm-motion";
|
|
5
|
+
import { cn } from "../../lib/cn";
|
|
6
|
+
import { Pressable } from "../pressable";
|
|
7
|
+
import { Text } from "../text";
|
|
8
|
+
import { useCalendarPart } from "./calendar.context";
|
|
9
|
+
import { compareMonths, formatYear, monthNames } from "./calendar.date";
|
|
10
|
+
import { calendarVariants, isMonthInBounds, resolvePartTestID } from "./calendar.variants";
|
|
11
|
+
|
|
12
|
+
export type CalendarPickerProps = Omit<ViewProps, "children"> & {
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const CROSSFADE_MS = 150;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The months and years jump views, laid over the grid while one is open.
|
|
20
|
+
*
|
|
21
|
+
* **Months** is twelve names in a three-column grid: a tap shows that month and returns to the
|
|
22
|
+
* days. **Years** is a scrolling three-column list over the calendar's year span, opened already
|
|
23
|
+
* scrolled to the visible year: a tap returns to the months view in that year, so a birth date
|
|
24
|
+
* is three taps — caption, caption, year — then a month and a day.
|
|
25
|
+
*
|
|
26
|
+
* A month outside the paging bounds is disabled. The view crossfades with the grid it covers,
|
|
27
|
+
* and with calm motion on it simply appears.
|
|
28
|
+
*
|
|
29
|
+
* Rendered by `Calendar.Grid`, which sizes it — it fills the grid's own box, so opening it never
|
|
30
|
+
* changes the calendar's height. Exported for a custom grid that wants to host it itself.
|
|
31
|
+
*/
|
|
32
|
+
export function CalendarPicker({ className, ...props }: CalendarPickerProps): ReactElement {
|
|
33
|
+
const calendar = useCalendarPart("Calendar.Picker");
|
|
34
|
+
const { goToMonth, isDisabled, locale, monthBounds, setView, size, variant, view, visibleMonth, yearSpan } = calendar;
|
|
35
|
+
const isCalm = useCalmMotion();
|
|
36
|
+
const slots = calendarVariants({ size, variant });
|
|
37
|
+
|
|
38
|
+
const names = useMemo(() => monthNames(locale, "short"), [locale]);
|
|
39
|
+
const years = useMemo(() => {
|
|
40
|
+
const list: number[] = [];
|
|
41
|
+
for (let year = yearSpan.from; year <= yearSpan.to; year += 1) list.push(year);
|
|
42
|
+
return list;
|
|
43
|
+
}, [yearSpan.from, yearSpan.to]);
|
|
44
|
+
|
|
45
|
+
const scroller = useRef<ScrollView>(null);
|
|
46
|
+
const hasScrolled = useRef(false);
|
|
47
|
+
const handleCurrentLayout = useCallback((event: LayoutChangeEvent) => {
|
|
48
|
+
if (hasScrolled.current) return;
|
|
49
|
+
hasScrolled.current = true;
|
|
50
|
+
scroller.current?.scrollTo({ animated: false, y: event.nativeEvent.layout.y });
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
const pickMonth = useCallback(
|
|
54
|
+
(month: number) => {
|
|
55
|
+
goToMonth({ day: 1, month, year: visibleMonth.year });
|
|
56
|
+
setView("days");
|
|
57
|
+
},
|
|
58
|
+
[goToMonth, setView, visibleMonth.year]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const pickYear = useCallback(
|
|
62
|
+
(year: number) => {
|
|
63
|
+
goToMonth({ day: 1, month: visibleMonth.month, year });
|
|
64
|
+
setView("months");
|
|
65
|
+
},
|
|
66
|
+
[goToMonth, setView, visibleMonth.month]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const fade = isCalm ? undefined : FadeIn.duration(CROSSFADE_MS);
|
|
70
|
+
const fadeOut = isCalm ? undefined : FadeOut.duration(CROSSFADE_MS);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Animated.View className={cn("absolute inset-0", className)} entering={fade} exiting={fadeOut} {...props}>
|
|
74
|
+
{view === "months" ? (
|
|
75
|
+
<View accessibilityRole="list" className={slots.pickerGrid()}>
|
|
76
|
+
{names.map((name, index) => {
|
|
77
|
+
const month = index + 1;
|
|
78
|
+
const isCurrent = month === visibleMonth.month;
|
|
79
|
+
const inBounds = isMonthInBounds({ day: 1, month, year: visibleMonth.year }, monthBounds);
|
|
80
|
+
const item = calendarVariants({ size, tone: isCurrent ? "selected" : "plain", variant });
|
|
81
|
+
return (
|
|
82
|
+
<Pressable
|
|
83
|
+
accessibilityState={{ selected: isCurrent }}
|
|
84
|
+
className={item.pickerItem({ className: inBounds ? undefined : "opacity-40" })}
|
|
85
|
+
disabled={isDisabled || !inBounds}
|
|
86
|
+
feedback="fade"
|
|
87
|
+
haptic="selection"
|
|
88
|
+
key={name}
|
|
89
|
+
onPress={() => pickMonth(month)}
|
|
90
|
+
testID={resolvePartTestID(calendar.testID, `month-${month}`)}
|
|
91
|
+
>
|
|
92
|
+
<Text className={item.pickerItemLabel()}>{name}</Text>
|
|
93
|
+
</Pressable>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
</View>
|
|
97
|
+
) : (
|
|
98
|
+
<ScrollView ref={scroller} showsVerticalScrollIndicator={false}>
|
|
99
|
+
<View accessibilityRole="list" className={slots.pickerGrid()}>
|
|
100
|
+
{years.map((year) => {
|
|
101
|
+
const isCurrent = year === visibleMonth.year;
|
|
102
|
+
const item = calendarVariants({ size, tone: isCurrent ? "selected" : "plain", variant });
|
|
103
|
+
const inBounds =
|
|
104
|
+
(!monthBounds.first || compareMonths({ day: 1, month: 12, year }, monthBounds.first) >= 0) &&
|
|
105
|
+
(!monthBounds.last || compareMonths({ day: 1, month: 1, year }, monthBounds.last) <= 0);
|
|
106
|
+
return (
|
|
107
|
+
<Pressable
|
|
108
|
+
accessibilityState={{ selected: isCurrent }}
|
|
109
|
+
className={item.pickerItem({ className: inBounds ? undefined : "opacity-40" })}
|
|
110
|
+
disabled={isDisabled || !inBounds}
|
|
111
|
+
feedback="fade"
|
|
112
|
+
haptic="selection"
|
|
113
|
+
key={year}
|
|
114
|
+
onLayout={isCurrent ? handleCurrentLayout : undefined}
|
|
115
|
+
onPress={() => pickYear(year)}
|
|
116
|
+
testID={resolvePartTestID(calendar.testID, `year-${year}`)}
|
|
117
|
+
>
|
|
118
|
+
<Text className={item.pickerItemLabel()}>{formatYear(year, locale)}</Text>
|
|
119
|
+
</Pressable>
|
|
120
|
+
);
|
|
121
|
+
})}
|
|
122
|
+
</View>
|
|
123
|
+
</ScrollView>
|
|
124
|
+
)}
|
|
125
|
+
</Animated.View>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
CalendarPicker.displayName = "DelacourUI.Calendar.Picker";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type ReactElement, useMemo } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { Text } from "../text";
|
|
4
|
+
import { useCalendarPart } from "./calendar.context";
|
|
5
|
+
import { weekdayLabels } from "./calendar.date";
|
|
6
|
+
import { calendarVariants } from "./calendar.variants";
|
|
7
|
+
|
|
8
|
+
export type CalendarWeekdaysProps = Omit<ViewProps, "children"> & {
|
|
9
|
+
className?: string;
|
|
10
|
+
/** How the names are drawn. The accessible name is always the full one. */
|
|
11
|
+
format?: "narrow" | "short";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The row of weekday names, in the grid's column order and the calendar's locale.
|
|
16
|
+
*
|
|
17
|
+
* Each column says its short name and is announced by its full one, so a screen reader reads
|
|
18
|
+
* "Monday" rather than "Mon" — or "M", which is two different days. Hidden, not removed, while a
|
|
19
|
+
* jump view is open, so the calendar's height holds still.
|
|
20
|
+
*/
|
|
21
|
+
export function CalendarWeekdays({ className, format = "short", ...props }: CalendarWeekdaysProps): ReactElement {
|
|
22
|
+
const { locale, size, variant, view, weekStartsOn } = useCalendarPart("Calendar.Weekdays");
|
|
23
|
+
const slots = calendarVariants({ size, variant });
|
|
24
|
+
const isHidden = view !== "days";
|
|
25
|
+
|
|
26
|
+
const names = useMemo(
|
|
27
|
+
() => ({ shown: weekdayLabels(locale, weekStartsOn, format), spoken: weekdayLabels(locale, weekStartsOn, "long") }),
|
|
28
|
+
[format, locale, weekStartsOn]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View
|
|
33
|
+
accessibilityElementsHidden={isHidden}
|
|
34
|
+
className={slots.weekdays({ className: [isHidden ? "opacity-0" : undefined, className] })}
|
|
35
|
+
importantForAccessibility={isHidden ? "no-hide-descendants" : "auto"}
|
|
36
|
+
{...props}
|
|
37
|
+
>
|
|
38
|
+
{names.shown.map((name, index) => (
|
|
39
|
+
<View className={slots.weekday()} key={names.spoken[index]}>
|
|
40
|
+
<Text accessibilityLabel={names.spoken[index]} className={slots.weekdayLabel()}>
|
|
41
|
+
{name}
|
|
42
|
+
</Text>
|
|
43
|
+
</View>
|
|
44
|
+
))}
|
|
45
|
+
</View>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
CalendarWeekdays.displayName = "DelacourUI.Calendar.Weekdays";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createContext, type ReactElement, type ReactNode, use } from "react";
|
|
2
|
+
import type { CalendarDate, CalendarSelectionState, Weekday } from "./calendar.date";
|
|
3
|
+
import type { CalendarCaptionLayout } from "./calendar.types";
|
|
4
|
+
import type { CalendarRangeRole, CalendarSize, CalendarVariant, CalendarView } from "./calendar.variants";
|
|
5
|
+
|
|
6
|
+
export type CalendarContextValue = {
|
|
7
|
+
mode: CalendarSelectionState["mode"];
|
|
8
|
+
variant: CalendarVariant;
|
|
9
|
+
size: CalendarSize;
|
|
10
|
+
/** The tag names are formatted in, or `undefined` for the device's own. */
|
|
11
|
+
locale: string | undefined;
|
|
12
|
+
/** The first column's weekday, resolved — never `auto`. */
|
|
13
|
+
weekStartsOn: Weekday;
|
|
14
|
+
/** The first of the month the grid shows. */
|
|
15
|
+
visibleMonth: CalendarDate;
|
|
16
|
+
/**
|
|
17
|
+
* Which way the last page went: `1` forward, `-1` back, `0` for a jump or the first render. The
|
|
18
|
+
* grid reads it to slide the new month in from the side it came from.
|
|
19
|
+
*/
|
|
20
|
+
pageDirection: -1 | 0 | 1;
|
|
21
|
+
/** The body's view: the month grid, or a jump view. */
|
|
22
|
+
view: CalendarView;
|
|
23
|
+
setView: (view: CalendarView) => void;
|
|
24
|
+
/** Shows the month `date` falls in, held inside the month bounds. */
|
|
25
|
+
goToMonth: (date: CalendarDate) => void;
|
|
26
|
+
/** One month back. In the months view, one year. */
|
|
27
|
+
goPrev: () => void;
|
|
28
|
+
/** One month forward. In the months view, one year. */
|
|
29
|
+
goNext: () => void;
|
|
30
|
+
canGoPrev: boolean;
|
|
31
|
+
canGoNext: boolean;
|
|
32
|
+
/** The first and last months paging reaches; `null` is unbounded. */
|
|
33
|
+
monthBounds: { first: CalendarDate | null; last: CalendarDate | null };
|
|
34
|
+
/** The years the year view lists. */
|
|
35
|
+
yearSpan: { from: number; to: number };
|
|
36
|
+
/** Whether a day cannot be picked — bounds and matchers, not the calendar's own state. */
|
|
37
|
+
isDateDisabled: (date: CalendarDate) => boolean;
|
|
38
|
+
isDateSelected: (date: CalendarDate) => boolean;
|
|
39
|
+
/** Where a day sits in the range, or `null`. Always `null` outside range mode. */
|
|
40
|
+
rangeRole: (date: CalendarDate) => CalendarRangeRole | null;
|
|
41
|
+
/** The selection, by mode. */
|
|
42
|
+
selection: CalendarSelectionState;
|
|
43
|
+
/** Applies a tap on `date` to the selection. Ignored while disabled, read-only, or on a disabled day. */
|
|
44
|
+
select: (date: CalendarDate) => void;
|
|
45
|
+
today: CalendarDate;
|
|
46
|
+
isDisabled: boolean;
|
|
47
|
+
isInvalid: boolean;
|
|
48
|
+
isReadOnly: boolean;
|
|
49
|
+
showOutsideDays: boolean;
|
|
50
|
+
selectOutsideDays: boolean;
|
|
51
|
+
swipeToPage: boolean;
|
|
52
|
+
captionLayout: CalendarCaptionLayout;
|
|
53
|
+
/** The calendar's own `testID`, which its parts derive theirs from — see `resolvePartTestID`. */
|
|
54
|
+
testID: string | undefined;
|
|
55
|
+
/** The enclosing `Field.Label`'s text, for the grid's accessible name. */
|
|
56
|
+
label: string | null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const CalendarContext = createContext<CalendarContextValue | null>(null);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Supplies one calendar's state to its own parts.
|
|
63
|
+
*
|
|
64
|
+
* Lives in its own module, importing nothing but React and types, so a part can read it without
|
|
65
|
+
* importing `./calendar` and closing a cycle (AGENTS.md rule 3).
|
|
66
|
+
*/
|
|
67
|
+
export function CalendarProvider({
|
|
68
|
+
value,
|
|
69
|
+
children,
|
|
70
|
+
}: {
|
|
71
|
+
value: CalendarContextValue;
|
|
72
|
+
children: ReactNode;
|
|
73
|
+
}): ReactElement {
|
|
74
|
+
return <CalendarContext value={value}>{children}</CalendarContext>;
|
|
75
|
+
}
|
|
76
|
+
CalendarProvider.displayName = "DelacourUI.Calendar.Provider";
|
|
77
|
+
|
|
78
|
+
/** The enclosing calendar's state, or null outside a `<Calendar>` — for another folder's control. */
|
|
79
|
+
export function useCalendarContext(): CalendarContextValue | null {
|
|
80
|
+
return use(CalendarContext);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reads the enclosing calendar's state.
|
|
85
|
+
*
|
|
86
|
+
* For a custom day or header that has to match the calendar it sits in. Throws outside one — use
|
|
87
|
+
* {@link useCalendarContext} where the calendar is optional.
|
|
88
|
+
*/
|
|
89
|
+
export function useCalendar(): CalendarContextValue {
|
|
90
|
+
const context = useCalendarContext();
|
|
91
|
+
if (!context) {
|
|
92
|
+
throw new Error("useCalendar must be called inside a <Calendar>.");
|
|
93
|
+
}
|
|
94
|
+
return context;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The enclosing calendar's state, for a compound part that cannot work without one.
|
|
99
|
+
*
|
|
100
|
+
* Internal: deliberately not re-exported from `index.ts`.
|
|
101
|
+
*/
|
|
102
|
+
export function useCalendarPart(component: string): CalendarContextValue {
|
|
103
|
+
const context = useCalendarContext();
|
|
104
|
+
if (!context) {
|
|
105
|
+
throw new Error(`${component} must be rendered inside a <Calendar>.`);
|
|
106
|
+
}
|
|
107
|
+
return context;
|
|
108
|
+
}
|