@delacour/react-native-ui 0.1.0-alpha.4 → 0.2.0-alpha.20261010061731
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/package.json +49 -5
- package/src/components/accordion/AGENTS.md +21 -9
- package/src/components/accordion/accordion-content.tsx +7 -4
- package/src/components/accordion/accordion-item.tsx +15 -13
- package/src/components/accordion/accordion.context.tsx +11 -5
- package/src/components/accordion/accordion.variants.test.ts +64 -3
- package/src/components/accordion/accordion.variants.ts +33 -9
- package/src/components/alert/AGENTS.md +102 -0
- package/src/components/alert/alert-action.tsx +17 -0
- package/src/components/alert/alert-close-button.tsx +50 -0
- package/src/components/alert/alert-content.tsx +15 -0
- package/src/components/alert/alert-description.tsx +16 -0
- package/src/components/alert/alert-glyphs.ts +25 -0
- package/src/components/alert/alert-indicator.tsx +40 -0
- package/src/components/alert/alert-title.tsx +18 -0
- package/src/components/alert/alert.context.tsx +69 -0
- package/src/components/alert/alert.tsx +160 -0
- package/src/components/alert/alert.types.ts +10 -0
- package/src/components/alert/alert.variants.test.ts +218 -0
- package/src/components/alert/alert.variants.ts +180 -0
- package/src/components/alert/index.ts +23 -0
- package/src/components/avatar/AGENTS.md +106 -0
- package/src/components/avatar/avatar-badge.tsx +51 -0
- package/src/components/avatar/avatar-group.tsx +139 -0
- package/src/components/avatar/avatar.context.tsx +93 -0
- package/src/components/avatar/avatar.tsx +201 -0
- package/src/components/avatar/avatar.utils.test.ts +174 -0
- package/src/components/avatar/avatar.utils.ts +141 -0
- package/src/components/avatar/avatar.variants.test.ts +251 -0
- package/src/components/avatar/avatar.variants.ts +270 -0
- package/src/components/avatar/index.ts +39 -0
- package/src/components/badge/AGENTS.md +10 -0
- package/src/components/badge/badge-close-button.tsx +2 -2
- package/src/components/badge/badge.context.tsx +2 -1
- package/src/components/badge/badge.tsx +13 -4
- package/src/components/badge/badge.variants.test.ts +26 -0
- package/src/components/badge/badge.variants.ts +21 -0
- package/src/components/bottom-sheet/AGENTS.md +186 -231
- package/src/components/bottom-sheet/bottom-sheet-background.tsx +31 -22
- package/src/components/bottom-sheet/bottom-sheet-close.tsx +23 -30
- package/src/components/bottom-sheet/bottom-sheet-container.tsx +54 -208
- package/src/components/bottom-sheet/bottom-sheet-content.tsx +25 -48
- package/src/components/bottom-sheet/bottom-sheet-description.tsx +8 -2
- package/src/components/bottom-sheet/bottom-sheet-flat-list.tsx +65 -0
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +53 -122
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +31 -26
- package/src/components/bottom-sheet/bottom-sheet-legend-list.tsx +69 -0
- package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +25 -147
- package/src/components/bottom-sheet/bottom-sheet-provider.tsx +42 -0
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +38 -62
- package/src/components/bottom-sheet/bottom-sheet-section-list.tsx +58 -0
- package/src/components/bottom-sheet/bottom-sheet-steps.tsx +88 -0
- package/src/components/bottom-sheet/bottom-sheet-text-input.tsx +44 -0
- package/src/components/bottom-sheet/bottom-sheet-title.tsx +12 -8
- package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +19 -37
- package/src/components/bottom-sheet/bottom-sheet.tsx +113 -60
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +47 -87
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +50 -126
- package/src/components/bottom-sheet/index.ts +39 -17
- package/src/components/bottom-sheet/use-bottom-sheet-input.ts +16 -115
- package/src/components/button/AGENTS.md +51 -11
- package/src/components/button/button-group.tsx +44 -8
- package/src/components/button/button.context.tsx +10 -0
- package/src/components/button/button.tsx +7 -1
- package/src/components/button/button.variants.test.ts +190 -0
- package/src/components/button/button.variants.ts +87 -6
- package/src/components/button/index.ts +2 -0
- package/src/components/calendar/AGENTS.md +142 -0
- package/src/components/calendar/calendar-caption.tsx +116 -0
- package/src/components/calendar/calendar-day.tsx +103 -0
- package/src/components/calendar/calendar-grid.tsx +183 -0
- package/src/components/calendar/calendar-header.tsx +34 -0
- package/src/components/calendar/calendar-nav.tsx +51 -0
- package/src/components/calendar/calendar-picker.tsx +128 -0
- package/src/components/calendar/calendar-weekdays.tsx +48 -0
- package/src/components/calendar/calendar.context.tsx +108 -0
- package/src/components/calendar/calendar.date.test.ts +456 -0
- package/src/components/calendar/calendar.date.ts +584 -0
- package/src/components/calendar/calendar.tsx +389 -0
- package/src/components/calendar/calendar.types.ts +87 -0
- package/src/components/calendar/calendar.variants.test.ts +363 -0
- package/src/components/calendar/calendar.variants.ts +426 -0
- package/src/components/calendar/index.ts +96 -0
- package/src/components/card/AGENTS.md +92 -0
- package/src/components/card/card-action.tsx +16 -0
- package/src/components/card/card-content.tsx +12 -0
- package/src/components/card/card-description.tsx +12 -0
- package/src/components/card/card-footer.tsx +30 -0
- package/src/components/card/card-header.tsx +45 -0
- package/src/components/card/card-title.tsx +25 -0
- package/src/components/card/card.context.tsx +64 -0
- package/src/components/card/card.tsx +100 -0
- package/src/components/card/card.types.ts +13 -0
- package/src/components/card/card.variants.test.ts +240 -0
- package/src/components/card/card.variants.ts +177 -0
- package/src/components/card/index.ts +15 -0
- package/src/components/chart/AGENTS.md +8 -0
- package/src/components/chart/chart.tsx +8 -1
- package/src/components/chip/AGENTS.md +113 -0
- package/src/components/chip/chip-close-button.tsx +48 -0
- package/src/components/chip/chip-end-content.tsx +15 -0
- package/src/components/chip/chip-label.tsx +21 -0
- package/src/components/chip/chip-start-content.tsx +16 -0
- package/src/components/chip/chip.context.tsx +66 -0
- package/src/components/chip/chip.tsx +268 -0
- package/src/components/chip/chip.types.ts +10 -0
- package/src/components/chip/chip.variants.test.ts +335 -0
- package/src/components/chip/chip.variants.ts +321 -0
- package/src/components/chip/index.ts +24 -0
- package/src/components/collapsible/AGENTS.md +97 -0
- package/src/components/collapsible/collapsible-content.tsx +97 -0
- package/src/components/collapsible/collapsible-description.tsx +17 -0
- package/src/components/collapsible/collapsible-indicator.tsx +92 -0
- package/src/components/collapsible/collapsible-title.tsx +17 -0
- package/src/components/collapsible/collapsible-trigger.tsx +133 -0
- package/src/components/collapsible/collapsible.context.tsx +91 -0
- package/src/components/collapsible/collapsible.tsx +167 -0
- package/src/components/collapsible/collapsible.types.ts +9 -0
- package/src/components/collapsible/collapsible.variants.test.ts +338 -0
- package/src/components/collapsible/collapsible.variants.ts +228 -0
- package/src/components/collapsible/index.ts +32 -0
- package/src/components/context-menu/AGENTS.md +103 -0
- package/src/components/context-menu/context-menu-content.tsx +64 -0
- package/src/components/context-menu/context-menu-item.tsx +22 -0
- package/src/components/context-menu/context-menu-preview.tsx +60 -0
- package/src/components/context-menu/context-menu-trigger.tsx +160 -0
- package/src/components/context-menu/context-menu.context.tsx +91 -0
- package/src/components/context-menu/context-menu.tsx +90 -0
- package/src/components/context-menu/context-menu.variants.test.ts +266 -0
- package/src/components/context-menu/context-menu.variants.ts +147 -0
- package/src/components/context-menu/index.ts +18 -0
- package/src/components/dialog/AGENTS.md +97 -0
- package/src/components/dialog/dialog-body.tsx +21 -0
- package/src/components/dialog/dialog-close.tsx +82 -0
- package/src/components/dialog/dialog-content.tsx +153 -0
- package/src/components/dialog/dialog-description.tsx +23 -0
- package/src/components/dialog/dialog-footer.tsx +32 -0
- package/src/components/dialog/dialog-header.tsx +20 -0
- package/src/components/dialog/dialog-title.tsx +42 -0
- package/src/components/dialog/dialog-trigger.tsx +54 -0
- package/src/components/dialog/dialog.context.tsx +90 -0
- package/src/components/dialog/dialog.tsx +119 -0
- package/src/components/dialog/dialog.variants.test.ts +224 -0
- package/src/components/dialog/dialog.variants.ts +173 -0
- package/src/components/dialog/index.ts +30 -0
- package/src/components/drawer/AGENTS.md +123 -0
- package/src/components/drawer/drawer-body.tsx +46 -0
- package/src/components/drawer/drawer-close.tsx +76 -0
- package/src/components/drawer/drawer-content.tsx +186 -0
- package/src/components/drawer/drawer-description.tsx +23 -0
- package/src/components/drawer/drawer-footer.tsx +21 -0
- package/src/components/drawer/drawer-header.tsx +42 -0
- package/src/components/drawer/drawer-title.tsx +35 -0
- package/src/components/drawer/drawer-trigger.tsx +54 -0
- package/src/components/drawer/drawer.context.tsx +114 -0
- package/src/components/drawer/drawer.tsx +119 -0
- package/src/components/drawer/drawer.variants.test.ts +392 -0
- package/src/components/drawer/drawer.variants.ts +244 -0
- package/src/components/drawer/index.ts +41 -0
- package/src/components/drawer/use-drawer-pan.ts +108 -0
- package/src/components/empty-state/AGENTS.md +90 -0
- package/src/components/empty-state/empty-state-content.tsx +17 -0
- package/src/components/empty-state/empty-state-description.tsx +12 -0
- package/src/components/empty-state/empty-state-header.tsx +17 -0
- package/src/components/empty-state/empty-state-media.tsx +56 -0
- package/src/components/empty-state/empty-state-title.tsx +18 -0
- package/src/components/empty-state/empty-state.context.tsx +65 -0
- package/src/components/empty-state/empty-state.tsx +84 -0
- package/src/components/empty-state/empty-state.types.ts +14 -0
- package/src/components/empty-state/empty-state.variants.test.ts +245 -0
- package/src/components/empty-state/empty-state.variants.ts +132 -0
- package/src/components/empty-state/index.ts +20 -0
- package/src/components/fab/AGENTS.md +119 -0
- package/src/components/fab/fab-action.tsx +118 -0
- package/src/components/fab/fab-group.tsx +214 -0
- package/src/components/fab/fab-label.tsx +18 -0
- package/src/components/fab/fab.context.tsx +125 -0
- package/src/components/fab/fab.tsx +156 -0
- package/src/components/fab/fab.types.ts +17 -0
- package/src/components/fab/fab.variants.test.ts +274 -0
- package/src/components/fab/fab.variants.ts +234 -0
- package/src/components/fab/index.ts +37 -0
- package/src/components/feedback/AGENTS.md +95 -0
- package/src/components/feedback/feedback-action.tsx +16 -0
- package/src/components/feedback/feedback-cancel.tsx +34 -0
- package/src/components/feedback/feedback-close.tsx +21 -0
- package/src/components/feedback/feedback-content.tsx +24 -0
- package/src/components/feedback/feedback-field.tsx +70 -0
- package/src/components/feedback/feedback-footer.tsx +21 -0
- package/src/components/feedback/feedback-panel.tsx +54 -0
- package/src/components/feedback/feedback-submit.tsx +74 -0
- package/src/components/feedback/feedback-title.tsx +34 -0
- package/src/components/feedback/feedback-trigger.tsx +20 -0
- package/src/components/feedback/feedback.context.tsx +73 -0
- package/src/components/feedback/feedback.tsx +135 -0
- package/src/components/feedback/feedback.variants.test.ts +185 -0
- package/src/components/feedback/feedback.variants.ts +128 -0
- package/src/components/feedback/index.ts +24 -0
- package/src/components/field/AGENTS.md +19 -0
- package/src/components/field/field-label.tsx +19 -5
- package/src/components/field/field.context.tsx +17 -0
- package/src/components/field/field.tsx +15 -2
- package/src/components/field/field.variants.test.ts +24 -0
- package/src/components/field/field.variants.ts +20 -0
- package/src/components/field/index.ts +1 -0
- package/src/components/input/AGENTS.md +5 -0
- package/src/components/input/input.variants.test.ts +32 -0
- package/src/components/input/input.variants.ts +13 -1
- package/src/components/item/AGENTS.md +92 -0
- package/src/components/item/index.ts +25 -0
- package/src/components/item/item-actions.tsx +28 -0
- package/src/components/item/item-content.tsx +15 -0
- package/src/components/item/item-description.tsx +12 -0
- package/src/components/item/item-footer.tsx +13 -0
- package/src/components/item/item-group.tsx +32 -0
- package/src/components/item/item-header.tsx +13 -0
- package/src/components/item/item-media.tsx +37 -0
- package/src/components/item/item-separator.tsx +13 -0
- package/src/components/item/item-title.tsx +17 -0
- package/src/components/item/item.context.tsx +66 -0
- package/src/components/item/item.tsx +218 -0
- package/src/components/item/item.types.ts +12 -0
- package/src/components/item/item.variants.test.ts +263 -0
- package/src/components/item/item.variants.ts +200 -0
- package/src/components/kpi/AGENTS.md +125 -0
- package/src/components/kpi/index.ts +45 -0
- package/src/components/kpi/kpi-action.tsx +12 -0
- package/src/components/kpi/kpi-content.tsx +35 -0
- package/src/components/kpi/kpi-footer.tsx +52 -0
- package/src/components/kpi/kpi-group.tsx +70 -0
- package/src/components/kpi/kpi-header.tsx +25 -0
- package/src/components/kpi/kpi-icon.tsx +28 -0
- package/src/components/kpi/kpi-sparkline.tsx +145 -0
- package/src/components/kpi/kpi-stat.tsx +20 -0
- package/src/components/kpi/kpi-title.tsx +19 -0
- package/src/components/kpi/kpi-trend.tsx +105 -0
- package/src/components/kpi/kpi-value.tsx +32 -0
- package/src/components/kpi/kpi.context.tsx +111 -0
- package/src/components/kpi/kpi.tsx +154 -0
- package/src/components/kpi/kpi.types.ts +9 -0
- package/src/components/kpi/kpi.variants.test.ts +428 -0
- package/src/components/kpi/kpi.variants.ts +347 -0
- package/src/components/label/AGENTS.md +89 -0
- package/src/components/label/index.ts +10 -0
- package/src/components/label/label.tsx +97 -0
- package/src/components/label/label.variants.test.ts +176 -0
- package/src/components/label/label.variants.ts +123 -0
- package/src/components/menu/AGENTS.md +176 -0
- package/src/components/menu/index.ts +44 -0
- package/src/components/menu/menu-background.tsx +17 -0
- package/src/components/menu/menu-checkbox-item.tsx +57 -0
- package/src/components/menu/menu-content.tsx +303 -0
- package/src/components/menu/menu-item.tsx +162 -0
- package/src/components/menu/menu-label.tsx +14 -0
- package/src/components/menu/menu-radio-group.tsx +50 -0
- package/src/components/menu/menu-radio-item.tsx +57 -0
- package/src/components/menu/menu-separator.tsx +11 -0
- package/src/components/menu/menu-sub-content.tsx +72 -0
- package/src/components/menu/menu-sub-trigger.tsx +50 -0
- package/src/components/menu/menu-sub.tsx +75 -0
- package/src/components/menu/menu-trigger.tsx +57 -0
- package/src/components/menu/menu.context.tsx +214 -0
- package/src/components/menu/menu.tsx +88 -0
- package/src/components/menu/menu.types.ts +33 -0
- package/src/components/menu/menu.variants.test.ts +400 -0
- package/src/components/menu/menu.variants.ts +353 -0
- package/src/components/meter/AGENTS.md +135 -0
- package/src/components/meter/index.ts +43 -0
- package/src/components/meter/meter-fill.tsx +19 -0
- package/src/components/meter/meter-header.tsx +17 -0
- package/src/components/meter/meter-label.tsx +18 -0
- package/src/components/meter/meter-output.tsx +42 -0
- package/src/components/meter/meter-segments.tsx +83 -0
- package/src/components/meter/meter-track.tsx +16 -0
- package/src/components/meter/meter.context.tsx +62 -0
- package/src/components/meter/meter.tsx +286 -0
- package/src/components/meter/meter.types.ts +30 -0
- package/src/components/meter/meter.variants.test.ts +430 -0
- package/src/components/meter/meter.variants.ts +347 -0
- package/src/components/overlay/AGENTS.md +89 -0
- package/src/components/overlay/index.ts +27 -0
- package/src/components/overlay/overlay-portal.tsx +84 -0
- package/src/components/overlay/overlay-presence.test.ts +46 -0
- package/src/components/overlay/overlay-presence.ts +49 -0
- package/src/components/overlay/overlay-registry.test.ts +96 -0
- package/src/components/overlay/overlay-registry.ts +99 -0
- package/src/components/overlay/overlay-scrim.tsx +49 -0
- package/src/components/overlay/overlay.context.tsx +48 -0
- package/src/components/overlay/overlay.tsx +118 -0
- package/src/components/overlay/overlay.variants.test.ts +37 -0
- package/src/components/overlay/overlay.variants.ts +33 -0
- package/src/components/overlay/use-overlay-back-handler.ts +34 -0
- package/src/components/overlay/use-overlay-presence.ts +87 -0
- package/src/components/popover/AGENTS.md +138 -0
- package/src/components/popover/index.ts +48 -0
- package/src/components/popover/popover-anchor.tsx +49 -0
- package/src/components/popover/popover-arrow.tsx +89 -0
- package/src/components/popover/popover-close.tsx +73 -0
- package/src/components/popover/popover-content.tsx +235 -0
- package/src/components/popover/popover-description.tsx +18 -0
- package/src/components/popover/popover-title.tsx +33 -0
- package/src/components/popover/popover-trigger.tsx +56 -0
- package/src/components/popover/popover.context.tsx +71 -0
- package/src/components/popover/popover.position.test.ts +354 -0
- package/src/components/popover/popover.position.ts +286 -0
- package/src/components/popover/popover.tsx +123 -0
- package/src/components/popover/popover.variants.test.ts +135 -0
- package/src/components/popover/popover.variants.ts +85 -0
- package/src/components/popover/use-anchor-measure.ts +76 -0
- package/src/components/popover/use-anchored-content.ts +219 -0
- package/src/components/pressable/AGENTS.md +11 -1
- package/src/components/pressable/pressable.tsx +7 -2
- package/src/components/pressable/pressable.variants.test.ts +29 -0
- package/src/components/pressable/pressable.variants.ts +17 -0
- package/src/components/progress/AGENTS.md +120 -0
- package/src/components/progress/index.ts +40 -0
- package/src/components/progress/progress-fill.tsx +152 -0
- package/src/components/progress/progress-header.tsx +19 -0
- package/src/components/progress/progress-label.tsx +27 -0
- package/src/components/progress/progress-output.tsx +47 -0
- package/src/components/progress/progress-track.tsx +38 -0
- package/src/components/progress/progress.context.tsx +79 -0
- package/src/components/progress/progress.tsx +171 -0
- package/src/components/progress/progress.types.ts +23 -0
- package/src/components/progress/progress.variants.test.ts +335 -0
- package/src/components/progress/progress.variants.ts +284 -0
- package/src/components/progress-button/AGENTS.md +116 -0
- package/src/components/progress-button/index.ts +36 -0
- package/src/components/progress-button/progress-button-done.tsx +33 -0
- package/src/components/progress-button/progress-button-label.tsx +27 -0
- package/src/components/progress-button/progress-button.context.tsx +93 -0
- package/src/components/progress-button/progress-button.tsx +414 -0
- package/src/components/progress-button/progress-button.variants.test.ts +314 -0
- package/src/components/progress-button/progress-button.variants.ts +236 -0
- package/src/components/provider/AGENTS.md +38 -25
- package/src/components/provider/provider.tsx +38 -11
- package/src/components/radio/AGENTS.md +8 -10
- package/src/components/radio/radio.variants.test.ts +5 -6
- package/src/components/radio/radio.variants.ts +6 -7
- package/src/components/rating/AGENTS.md +143 -0
- package/src/components/rating/index.ts +43 -0
- package/src/components/rating/rating-output.tsx +39 -0
- package/src/components/rating/rating-star.tsx +70 -0
- package/src/components/rating/rating-stars.tsx +219 -0
- package/src/components/rating/rating.context.tsx +79 -0
- package/src/components/rating/rating.tsx +163 -0
- package/src/components/rating/rating.variants.test.ts +347 -0
- package/src/components/rating/rating.variants.ts +354 -0
- package/src/components/selection-mode/AGENTS.md +155 -0
- package/src/components/selection-mode/index.ts +43 -0
- package/src/components/selection-mode/selection-mode-action.tsx +65 -0
- package/src/components/selection-mode/selection-mode-bar.tsx +79 -0
- package/src/components/selection-mode/selection-mode-group.tsx +123 -0
- package/src/components/selection-mode/selection-mode-header.tsx +85 -0
- package/src/components/selection-mode/selection-mode-indicator.tsx +118 -0
- package/src/components/selection-mode/selection-mode-item.tsx +182 -0
- package/src/components/selection-mode/selection-mode.context.tsx +89 -0
- package/src/components/selection-mode/selection-mode.tsx +207 -0
- package/src/components/selection-mode/selection-mode.types.ts +36 -0
- package/src/components/selection-mode/selection-mode.variants.test.ts +292 -0
- package/src/components/selection-mode/selection-mode.variants.ts +329 -0
- package/src/components/skeleton/AGENTS.md +120 -0
- package/src/components/skeleton/index.ts +15 -0
- package/src/components/skeleton/skeleton-clock.ts +48 -0
- package/src/components/skeleton/skeleton-group.tsx +64 -0
- package/src/components/skeleton/skeleton-shimmer.tsx +80 -0
- package/src/components/skeleton/skeleton.context.tsx +45 -0
- package/src/components/skeleton/skeleton.tsx +227 -0
- package/src/components/skeleton/skeleton.variants.test.ts +245 -0
- package/src/components/skeleton/skeleton.variants.ts +222 -0
- package/src/components/slide-button/AGENTS.md +122 -0
- package/src/components/slide-button/index.ts +43 -0
- package/src/components/slide-button/slide-button-label.tsx +70 -0
- package/src/components/slide-button/slide-button-thumb.tsx +93 -0
- package/src/components/slide-button/slide-button.context.tsx +76 -0
- package/src/components/slide-button/slide-button.tsx +457 -0
- package/src/components/slide-button/slide-button.variants.test.ts +288 -0
- package/src/components/slide-button/slide-button.variants.ts +324 -0
- package/src/components/slider/index.ts +2 -0
- package/src/components/slider/slider-thumb.tsx +33 -1
- package/src/components/slider/slider.variants.test.ts +52 -0
- package/src/components/slider/slider.variants.ts +56 -2
- package/src/components/stack-card/AGENTS.md +169 -0
- package/src/components/stack-card/index.ts +37 -0
- package/src/components/stack-card/stack-card-action.tsx +66 -0
- package/src/components/stack-card/stack-card-actions.tsx +18 -0
- package/src/components/stack-card/stack-card-card.tsx +71 -0
- package/src/components/stack-card/stack-card-empty.tsx +30 -0
- package/src/components/stack-card/stack-card-slot.tsx +90 -0
- package/src/components/stack-card/stack-card-stamp.tsx +76 -0
- package/src/components/stack-card/stack-card.context.tsx +117 -0
- package/src/components/stack-card/stack-card.tsx +557 -0
- package/src/components/stack-card/stack-card.types.ts +26 -0
- package/src/components/stack-card/stack-card.variants.test.ts +355 -0
- package/src/components/stack-card/stack-card.variants.ts +360 -0
- package/src/components/steps/AGENTS.md +153 -0
- package/src/components/steps/index.ts +45 -0
- package/src/components/steps/steps-connector.tsx +31 -0
- package/src/components/steps/steps-description.tsx +26 -0
- package/src/components/steps/steps-indicator.tsx +106 -0
- package/src/components/steps/steps-item.tsx +225 -0
- package/src/components/steps/steps-panel.tsx +41 -0
- package/src/components/steps/steps-title.tsx +32 -0
- package/src/components/steps/steps-track.tsx +59 -0
- package/src/components/steps/steps.context.tsx +123 -0
- package/src/components/steps/steps.tsx +166 -0
- package/src/components/steps/steps.variants.test.ts +388 -0
- package/src/components/steps/steps.variants.ts +468 -0
- package/src/components/surface/AGENTS.md +82 -0
- package/src/components/surface/index.ts +15 -0
- package/src/components/surface/surface.context.tsx +56 -0
- package/src/components/surface/surface.tsx +87 -0
- package/src/components/surface/surface.variants.test.ts +227 -0
- package/src/components/surface/surface.variants.ts +165 -0
- package/src/components/swipe/AGENTS.md +140 -0
- package/src/components/swipe/index.ts +31 -0
- package/src/components/swipe/swipe-action.tsx +99 -0
- package/src/components/swipe/swipe-end.tsx +14 -0
- package/src/components/swipe/swipe-group.tsx +55 -0
- package/src/components/swipe/swipe-panel.tsx +55 -0
- package/src/components/swipe/swipe-start.tsx +15 -0
- package/src/components/swipe/swipe.context.tsx +135 -0
- package/src/components/swipe/swipe.tsx +393 -0
- package/src/components/swipe/swipe.types.ts +7 -0
- package/src/components/swipe/swipe.variants.test.ts +337 -0
- package/src/components/swipe/swipe.variants.ts +290 -0
- package/src/components/switch/switch.variants.ts +2 -2
- package/src/components/tabs/AGENTS.md +4 -3
- package/src/components/tabs/tabs.variants.test.ts +1 -1
- package/src/components/tabs/tabs.variants.ts +4 -6
- package/src/components/text/AGENTS.md +8 -2
- package/src/components/text/text.tsx +7 -0
- package/src/components/text/text.variants.test.ts +24 -1
- package/src/components/text/text.variants.ts +5 -0
- package/src/components/textarea/AGENTS.md +89 -0
- package/src/components/textarea/index.ts +16 -0
- package/src/components/textarea/textarea.tsx +157 -0
- package/src/components/textarea/textarea.variants.test.ts +219 -0
- package/src/components/textarea/textarea.variants.ts +171 -0
- package/src/components/toast/AGENTS.md +140 -0
- package/src/components/toast/index.ts +74 -0
- package/src/components/toast/toast-action.tsx +59 -0
- package/src/components/toast/toast-api.ts +32 -0
- package/src/components/toast/toast-close.tsx +44 -0
- package/src/components/toast/toast-content.tsx +11 -0
- package/src/components/toast/toast-description.tsx +11 -0
- package/src/components/toast/toast-indicator.tsx +34 -0
- package/src/components/toast/toast-item.tsx +308 -0
- package/src/components/toast/toast-title.tsx +13 -0
- package/src/components/toast/toast-viewport.tsx +135 -0
- package/src/components/toast/toast.context.tsx +69 -0
- package/src/components/toast/toast.store.test.ts +259 -0
- package/src/components/toast/toast.store.ts +338 -0
- package/src/components/toast/toast.timer.test.ts +64 -0
- package/src/components/toast/toast.timer.ts +51 -0
- package/src/components/toast/toast.tsx +132 -0
- package/src/components/toast/toast.variants.test.ts +393 -0
- package/src/components/toast/toast.variants.ts +304 -0
- package/src/components/toggle-button/AGENTS.md +106 -0
- package/src/components/toggle-button/index.ts +30 -0
- package/src/components/toggle-button/toggle-button-group.tsx +156 -0
- package/src/components/toggle-button/toggle-button-label.tsx +19 -0
- package/src/components/toggle-button/toggle-button.context.tsx +130 -0
- package/src/components/toggle-button/toggle-button.tsx +172 -0
- package/src/components/toggle-button/toggle-button.variants.test.ts +189 -0
- package/src/components/toggle-button/toggle-button.variants.ts +160 -0
- package/src/components/tooltip/AGENTS.md +133 -0
- package/src/components/tooltip/index.ts +28 -0
- package/src/components/tooltip/tooltip-arrow.tsx +40 -0
- package/src/components/tooltip/tooltip-content.tsx +179 -0
- package/src/components/tooltip/tooltip-description.tsx +19 -0
- package/src/components/tooltip/tooltip-text.tsx +21 -0
- package/src/components/tooltip/tooltip-title.tsx +20 -0
- package/src/components/tooltip/tooltip-trigger.tsx +103 -0
- package/src/components/tooltip/tooltip.context.tsx +68 -0
- package/src/components/tooltip/tooltip.tsx +213 -0
- package/src/components/tooltip/tooltip.variants.test.ts +231 -0
- package/src/components/tooltip/tooltip.variants.ts +177 -0
- package/src/hooks/use-calm-motion.tsx +31 -0
- package/src/lib/calm-motion.test.ts +17 -0
- package/src/lib/calm-motion.ts +23 -0
- package/src/lib/cn.parity.test.ts +62 -0
- package/src/lib/cn.test.ts +14 -0
- package/src/lib/cn.ts +11 -6
- package/src/no-gorhom.test.ts +54 -0
- package/src/styles/geometry.test.ts +1 -0
- package/src/styles/theme.css +4 -0
- package/src/styles/tokens.css +44 -0
- package/src/styles/tokens.test.ts +56 -0
- package/src/styles/tokens.ts +29 -1
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +0 -72
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +0 -135
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, useMemo } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import Animated, { ZoomIn } from "react-native-reanimated";
|
|
4
|
+
import { IconCheckmark1Small, IconCrossSmall } from "../../icons/central";
|
|
5
|
+
import { Icon, IconDefaultsProvider } from "../icon";
|
|
6
|
+
import { Spinner } from "../spinner";
|
|
7
|
+
import { Text } from "../text";
|
|
8
|
+
import { type StepsItemContextValue, useStepsItemPart } from "./steps.context";
|
|
9
|
+
import { resolveIndicatorForeground, resolveIndicatorGlyph, stepsVariants } from "./steps.variants";
|
|
10
|
+
|
|
11
|
+
export type StepsIndicatorChildrenProps = Pick<
|
|
12
|
+
StepsItemContextValue,
|
|
13
|
+
"step" | "status" | "isCurrent" | "isInvalid" | "isLoading" | "size" | "variant"
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
export type StepsIndicatorProps = Omit<ViewProps, "children"> & {
|
|
17
|
+
className?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Replaces what the circle draws. The circle, its size and its colours are
|
|
20
|
+
* still the step's, and an `Icon` inside inherits the glyph size and colour.
|
|
21
|
+
*/
|
|
22
|
+
children?: ReactNode | ((props: StepsIndicatorChildrenProps) => ReactNode);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The glyph's entrance. Short and un-sprung: a check lands, it does not bounce.
|
|
27
|
+
* Reduce motion is Reanimated's default `System`, which snaps it in — the state
|
|
28
|
+
* is carried by the glyph's presence, not by its motion.
|
|
29
|
+
*/
|
|
30
|
+
const GLYPH_ENTERING = ZoomIn.duration(180);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The circle: a spinner while loading, a cross while invalid, a check once
|
|
34
|
+
* completed, and otherwise the step's one-based number.
|
|
35
|
+
*
|
|
36
|
+
* Composed in automatically, so write it out only to restyle it or to draw
|
|
37
|
+
* something of your own inside the circle — an icon per step, say. Wherever it
|
|
38
|
+
* is written among a step's children, the step lifts it into the track between
|
|
39
|
+
* the connector halves; it never lands in the text column.
|
|
40
|
+
*
|
|
41
|
+
* **The circle is hidden from assistive technology.** The trigger merges its
|
|
42
|
+
* children into one element, and the numeral would otherwise lead the name —
|
|
43
|
+
* "2, Shipping" — restating the position the accessibility value already reads.
|
|
44
|
+
*
|
|
45
|
+
* **The number does not scale with the OS text size.** The circle is a fixed
|
|
46
|
+
* edge, so a scaled numeral would spill out of it, and the position it shows is
|
|
47
|
+
* already announced in the step's accessibility value — scaling would buy the
|
|
48
|
+
* reader nothing the screen reader has not said.
|
|
49
|
+
*
|
|
50
|
+
* The check and cross are Central Icons and the spinner is `Spinner`; all three
|
|
51
|
+
* take their size and colour from one `IconDefaultsProvider`, whose token
|
|
52
|
+
* {@link resolveIndicatorForeground} keeps equal to the number's class.
|
|
53
|
+
*/
|
|
54
|
+
export function StepsIndicator({ className, children, ...props }: StepsIndicatorProps): ReactElement {
|
|
55
|
+
const item = useStepsItemPart("Steps.Indicator");
|
|
56
|
+
const { step, status, isCurrent, isInvalid, isLoading, isDisabled, size, variant } = item;
|
|
57
|
+
|
|
58
|
+
const slots = stepsVariants({ isDisabled, isInvalid, size, status, variant });
|
|
59
|
+
const glyphClass = slots.glyph();
|
|
60
|
+
const labelClass = slots.indicatorLabel();
|
|
61
|
+
const color = resolveIndicatorForeground({ isInvalid, status, variant });
|
|
62
|
+
const iconDefaults = useMemo(() => ({ className: glyphClass ?? "", color }), [glyphClass, color]);
|
|
63
|
+
|
|
64
|
+
const renderGlyph = (): ReactNode => {
|
|
65
|
+
if (typeof children === "function") {
|
|
66
|
+
return children({ isCurrent, isInvalid, isLoading, size, status, step, variant });
|
|
67
|
+
}
|
|
68
|
+
if (children) return children;
|
|
69
|
+
|
|
70
|
+
const glyph = resolveIndicatorGlyph({ isInvalid, isLoading, status, step });
|
|
71
|
+
switch (glyph.kind) {
|
|
72
|
+
case "spinner":
|
|
73
|
+
return <Spinner />;
|
|
74
|
+
case "alert":
|
|
75
|
+
return (
|
|
76
|
+
<Animated.View entering={GLYPH_ENTERING} key="alert">
|
|
77
|
+
<Icon icon={IconCrossSmall} />
|
|
78
|
+
</Animated.View>
|
|
79
|
+
);
|
|
80
|
+
case "check":
|
|
81
|
+
return (
|
|
82
|
+
<Animated.View entering={GLYPH_ENTERING} key="check">
|
|
83
|
+
<Icon icon={IconCheckmark1Small} />
|
|
84
|
+
</Animated.View>
|
|
85
|
+
);
|
|
86
|
+
case "number":
|
|
87
|
+
return (
|
|
88
|
+
<Text allowFontScaling={false} className={labelClass}>
|
|
89
|
+
{glyph.label}
|
|
90
|
+
</Text>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<View
|
|
97
|
+
accessibilityElementsHidden
|
|
98
|
+
className={slots.indicator({ className })}
|
|
99
|
+
importantForAccessibility="no-hide-descendants"
|
|
100
|
+
{...props}
|
|
101
|
+
>
|
|
102
|
+
<IconDefaultsProvider value={iconDefaults}>{renderGlyph()}</IconDefaultsProvider>
|
|
103
|
+
</View>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
StepsIndicator.displayName = "DelacourUI.Steps.Indicator";
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { Children, isValidElement, type ReactElement, type ReactNode, useCallback, useMemo } from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { Pressable, type PressableProps } from "../pressable";
|
|
4
|
+
import { type StepsItemContextValue, StepsItemProvider, useStepsPart } from "./steps.context";
|
|
5
|
+
import {
|
|
6
|
+
resolveStepAccessibilityValue,
|
|
7
|
+
resolveStepInteraction,
|
|
8
|
+
resolveStepStatus,
|
|
9
|
+
stepsVariants,
|
|
10
|
+
} from "./steps.variants";
|
|
11
|
+
import { StepsIndicator } from "./steps-indicator";
|
|
12
|
+
import { StepsPanel } from "./steps-panel";
|
|
13
|
+
import { StepsTitle } from "./steps-title";
|
|
14
|
+
import { StepsRail, StepsTrack } from "./steps-track";
|
|
15
|
+
|
|
16
|
+
export type StepsItemProps = Omit<PressableProps, "asChild" | "busy" | "children" | "disabled" | "ref"> & {
|
|
17
|
+
/** This step's index, zero-based. The indicator draws it one-based. */
|
|
18
|
+
step: number;
|
|
19
|
+
/**
|
|
20
|
+
* Overrides whether the step reads as done. `true` completes it wherever it
|
|
21
|
+
* sits — a finished flow's last step. `false` leaves a passed step looking
|
|
22
|
+
* unfinished — a skipped optional one. Never demotes the current step.
|
|
23
|
+
*/
|
|
24
|
+
completed?: boolean;
|
|
25
|
+
/** The step cannot be pressed, and fades. A stepper that names `isDisabled` wins over this. */
|
|
26
|
+
isDisabled?: boolean;
|
|
27
|
+
/** The step has an error: a cross in the indicator and a destructive title. */
|
|
28
|
+
isInvalid?: boolean;
|
|
29
|
+
/** A spinner in the indicator. The step is announced busy and cannot be pressed. */
|
|
30
|
+
isLoading?: boolean;
|
|
31
|
+
children?: ReactNode;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* One step: its indicator, the connector halves either side of it, its title and
|
|
36
|
+
* description, and any `Steps.Panel`.
|
|
37
|
+
*
|
|
38
|
+
* The trigger is the control — the circle, the title and the space between them
|
|
39
|
+
* are one tap target and one accessibility element, whose name is the title and
|
|
40
|
+
* whose value is "Step 2 of 3, completed". It renders a `Pressable` when the
|
|
41
|
+
* stepper takes presses and a plain `View` when it is read-only, so a progress
|
|
42
|
+
* display is never announced as a row of buttons. A panel renders after the
|
|
43
|
+
* trigger, outside both.
|
|
44
|
+
*
|
|
45
|
+
* `className` styles the step's outer box; every other prop — `testID`,
|
|
46
|
+
* `onPress`, `haptic`, the accessibility props — reaches the trigger.
|
|
47
|
+
*
|
|
48
|
+
* Bare text is wrapped in a `Steps.Title`, and a `Steps.Indicator` is composed
|
|
49
|
+
* in when the children hold none — the shortest step that works is
|
|
50
|
+
* `<Steps.Item step={0}>Account</Steps.Item>`.
|
|
51
|
+
*/
|
|
52
|
+
export function StepsItem({
|
|
53
|
+
step,
|
|
54
|
+
completed,
|
|
55
|
+
isDisabled,
|
|
56
|
+
isInvalid = false,
|
|
57
|
+
isLoading = false,
|
|
58
|
+
feedback = "fade",
|
|
59
|
+
haptic,
|
|
60
|
+
onLongPress,
|
|
61
|
+
pressedOpacity,
|
|
62
|
+
pressedScale,
|
|
63
|
+
accessibilityState,
|
|
64
|
+
className,
|
|
65
|
+
children,
|
|
66
|
+
onPress,
|
|
67
|
+
...props
|
|
68
|
+
}: StepsItemProps): ReactElement {
|
|
69
|
+
const root = useStepsPart("Steps.Item");
|
|
70
|
+
const { value, count, orientation, size, variant, select } = root;
|
|
71
|
+
|
|
72
|
+
const status = resolveStepStatus({ completed, step, value });
|
|
73
|
+
const isCurrent = step === value;
|
|
74
|
+
const resolvedIsDisabled = root.isDisabled ?? isDisabled ?? false;
|
|
75
|
+
const isLast = step >= count - 1;
|
|
76
|
+
|
|
77
|
+
if (process.env.NODE_ENV !== "production" && count > 0 && (step < 0 || step >= count)) {
|
|
78
|
+
console.warn(`Steps.Item: step ${step} is outside the ${count} steps the root counted. Steps are zero-based.`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const interaction = resolveStepInteraction({
|
|
82
|
+
isDisabled: resolvedIsDisabled,
|
|
83
|
+
isLinear: root.isLinear,
|
|
84
|
+
isLoading,
|
|
85
|
+
isReadOnly: root.isReadOnly,
|
|
86
|
+
step,
|
|
87
|
+
value,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const context = useMemo<StepsItemContextValue>(
|
|
91
|
+
() => ({
|
|
92
|
+
isCurrent,
|
|
93
|
+
isDisabled: resolvedIsDisabled,
|
|
94
|
+
isInvalid,
|
|
95
|
+
isLast,
|
|
96
|
+
isLoading,
|
|
97
|
+
orientation,
|
|
98
|
+
size,
|
|
99
|
+
status,
|
|
100
|
+
step,
|
|
101
|
+
variant,
|
|
102
|
+
}),
|
|
103
|
+
[isCurrent, resolvedIsDisabled, isInvalid, isLast, isLoading, orientation, size, status, step, variant]
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const handlePress = useCallback(() => {
|
|
107
|
+
select(step);
|
|
108
|
+
onPress?.();
|
|
109
|
+
}, [onPress, select, step]);
|
|
110
|
+
|
|
111
|
+
const { indicator, content, panels } = useMemo(() => splitChildren(children), [children]);
|
|
112
|
+
const hasPanel = panels.length > 0;
|
|
113
|
+
const slots = stepsVariants({ hasPanel, isDisabled: resolvedIsDisabled, isLast, orientation, size });
|
|
114
|
+
|
|
115
|
+
const trigger = (
|
|
116
|
+
<>
|
|
117
|
+
<StepsTrack indicator={indicator} />
|
|
118
|
+
{content.length > 0 ? <View className={slots.content()}>{content}</View> : null}
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// A vertical panel sits beside a rail that carries the line on past it; a
|
|
123
|
+
// horizontal one simply follows the trigger.
|
|
124
|
+
const panelRow =
|
|
125
|
+
hasPanel && orientation === "vertical" ? (
|
|
126
|
+
<View className={slots.panelRow()}>
|
|
127
|
+
<StepsRail />
|
|
128
|
+
{panels}
|
|
129
|
+
</View>
|
|
130
|
+
) : (
|
|
131
|
+
panels
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const accessibilityValue = {
|
|
135
|
+
text: resolveStepAccessibilityValue({ count, isInvalid, isLoading, status, step }),
|
|
136
|
+
};
|
|
137
|
+
const state = { ...accessibilityState, selected: isCurrent };
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<StepsItemProvider value={context}>
|
|
141
|
+
<View className={slots.item({ className })}>
|
|
142
|
+
{interaction === "static" ? (
|
|
143
|
+
<View
|
|
144
|
+
accessibilityState={state}
|
|
145
|
+
accessibilityValue={accessibilityValue}
|
|
146
|
+
accessible
|
|
147
|
+
className={slots.trigger()}
|
|
148
|
+
{...props}
|
|
149
|
+
>
|
|
150
|
+
{trigger}
|
|
151
|
+
</View>
|
|
152
|
+
) : (
|
|
153
|
+
<Pressable
|
|
154
|
+
accessibilityState={state}
|
|
155
|
+
accessibilityValue={accessibilityValue}
|
|
156
|
+
busy={isLoading}
|
|
157
|
+
className={slots.trigger()}
|
|
158
|
+
disabled={interaction === "blocked" && !isLoading}
|
|
159
|
+
feedback={feedback}
|
|
160
|
+
haptic={haptic}
|
|
161
|
+
onLongPress={onLongPress}
|
|
162
|
+
onPress={handlePress}
|
|
163
|
+
pressedOpacity={pressedOpacity}
|
|
164
|
+
pressedScale={pressedScale}
|
|
165
|
+
{...props}
|
|
166
|
+
>
|
|
167
|
+
{trigger}
|
|
168
|
+
</Pressable>
|
|
169
|
+
)}
|
|
170
|
+
{panelRow}
|
|
171
|
+
</View>
|
|
172
|
+
</StepsItemProvider>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
StepsItem.displayName = "DelacourUI.Steps.Item";
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Lifts a `Steps.Indicator` and any `Steps.Panel` out of a step's children, and
|
|
179
|
+
* wraps bare text in a `Steps.Title`.
|
|
180
|
+
*
|
|
181
|
+
* Both places are structural — the indicator between the connector halves, a
|
|
182
|
+
* panel after the trigger — so each is pulled out wherever it was written rather
|
|
183
|
+
* than rendered in the text column.
|
|
184
|
+
* Consecutive strings and numbers collapse into one title, as `Radio` does, so
|
|
185
|
+
* `Step {n}` is one piece of text rather than two spaced apart.
|
|
186
|
+
*
|
|
187
|
+
* Lives here rather than in the root: it is the item that wraps its own
|
|
188
|
+
* children, and a helper in the root file would close a cycle. See AGENTS.md.
|
|
189
|
+
*/
|
|
190
|
+
function splitChildren(children: ReactNode): {
|
|
191
|
+
indicator: ReactNode | null;
|
|
192
|
+
content: ReactNode[];
|
|
193
|
+
panels: ReactNode[];
|
|
194
|
+
} {
|
|
195
|
+
let indicator: ReactNode | null = null;
|
|
196
|
+
const content: ReactNode[] = [];
|
|
197
|
+
const panels: ReactNode[] = [];
|
|
198
|
+
let run: (string | number)[] = [];
|
|
199
|
+
|
|
200
|
+
const flushRun = () => {
|
|
201
|
+
if (run.length === 0) return;
|
|
202
|
+
content.push(<StepsTitle key={`title-${content.length}`}>{run.join("")}</StepsTitle>);
|
|
203
|
+
run = [];
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
for (const child of Children.toArray(children)) {
|
|
207
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
208
|
+
run.push(child);
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
flushRun();
|
|
212
|
+
if (indicator === null && isValidElement(child) && child.type === StepsIndicator) {
|
|
213
|
+
indicator = child;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (isValidElement(child) && child.type === StepsPanel) {
|
|
217
|
+
panels.push(child);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
content.push(child);
|
|
221
|
+
}
|
|
222
|
+
flushRun();
|
|
223
|
+
|
|
224
|
+
return { content, indicator, panels };
|
|
225
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { useStepsItemPart } from "./steps.context";
|
|
4
|
+
import { stepsVariants } from "./steps.variants";
|
|
5
|
+
|
|
6
|
+
export type StepsPanelProps = ViewProps & { className?: string };
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Content for a step that sits below its title but outside its tap target — a
|
|
10
|
+
* form for the current step, a summary of a finished one.
|
|
11
|
+
*
|
|
12
|
+
* **It exists because the trigger is one accessibility element.** The trigger
|
|
13
|
+
* merges its indicator, title and description into a single stop, so a text
|
|
14
|
+
* field or a button inside it would be unreachable by VoiceOver, and its own
|
|
15
|
+
* tap gesture would sit inside the step's. The panel renders beside the
|
|
16
|
+
* trigger instead: every control in it is its own element, and a press on it
|
|
17
|
+
* never moves the stepper.
|
|
18
|
+
*
|
|
19
|
+
* Wherever it is written among a step's children, the step lifts it out and
|
|
20
|
+
* renders it after the trigger. In a vertical stepper the line carries on down
|
|
21
|
+
* a rail beside it, so the panel reads as part of its step. Show it
|
|
22
|
+
* conditionally — `{value === 1 ? <Steps.Panel>…</Steps.Panel> : null}` — there
|
|
23
|
+
* is no built-in "only while current", because a finished step's summary is as
|
|
24
|
+
* common a panel as the current step's form.
|
|
25
|
+
*/
|
|
26
|
+
export function StepsPanel({ className, ...props }: StepsPanelProps): ReactElement {
|
|
27
|
+
const item = useStepsItemPart("Steps.Panel");
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<View
|
|
31
|
+
className={stepsVariants({
|
|
32
|
+
hasPanel: true,
|
|
33
|
+
isLast: item.isLast,
|
|
34
|
+
orientation: item.orientation,
|
|
35
|
+
size: item.size,
|
|
36
|
+
}).panel({ className })}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
StepsPanel.displayName = "DelacourUI.Steps.Panel";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Text, type TextPresetProps } from "../text";
|
|
3
|
+
import { useStepsItemPart } from "./steps.context";
|
|
4
|
+
import { STEPS_TITLE_TEXT_SIZE, stepsVariants } from "./steps.variants";
|
|
5
|
+
|
|
6
|
+
export type StepsTitleProps = TextPresetProps;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The step's name — and, because the step is one accessibility element, its
|
|
10
|
+
* accessible name too.
|
|
11
|
+
*
|
|
12
|
+
* Renders `Text.Label` at the step's own size step rather than a scale of its
|
|
13
|
+
* own, the rule `Radio.Label` follows. Its colour follows the step: muted ahead
|
|
14
|
+
* of the value, destructive while invalid. Bare text inside a `Steps.Item` is
|
|
15
|
+
* wrapped in one automatically.
|
|
16
|
+
*/
|
|
17
|
+
export function StepsTitle({ className, size, ...props }: StepsTitleProps): ReactElement {
|
|
18
|
+
const item = useStepsItemPart("Steps.Title");
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Text.Label
|
|
22
|
+
className={stepsVariants({
|
|
23
|
+
isInvalid: item.isInvalid,
|
|
24
|
+
orientation: item.orientation,
|
|
25
|
+
status: item.status,
|
|
26
|
+
}).title({ className })}
|
|
27
|
+
size={size ?? STEPS_TITLE_TEXT_SIZE[item.size]}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
StepsTitle.displayName = "DelacourUI.Steps.Title";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { useStepsItemPart, useStepsPart } from "./steps.context";
|
|
4
|
+
import { isConnectorComplete, resolveStepConnectors, stepsVariants } from "./steps.variants";
|
|
5
|
+
import { StepsConnector } from "./steps-connector";
|
|
6
|
+
import { StepsIndicator } from "./steps-indicator";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A step's track: `[leading] [indicator] [trailing]`. Internal.
|
|
10
|
+
*
|
|
11
|
+
* The leading half joins the previous step, so it fills off that step's index
|
|
12
|
+
* rather than this one's — the two halves of one line must agree, and the value
|
|
13
|
+
* is the only thing both can see. See `isConnectorComplete`.
|
|
14
|
+
*/
|
|
15
|
+
export function StepsTrack({ indicator }: { indicator: ReactNode | null }): ReactElement {
|
|
16
|
+
const { value, count } = useStepsPart("Steps.Item");
|
|
17
|
+
const { step, orientation, size, isLast } = useStepsItemPart("Steps.Item");
|
|
18
|
+
const connectors = resolveStepConnectors({ count, orientation, step });
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<View className={stepsVariants({ isLast, orientation, size }).track()}>
|
|
22
|
+
{connectors.leading === "none" ? null : (
|
|
23
|
+
<StepsConnector
|
|
24
|
+
isComplete={isConnectorComplete(step - 1, value)}
|
|
25
|
+
orientation={orientation}
|
|
26
|
+
slot={connectors.leading}
|
|
27
|
+
/>
|
|
28
|
+
)}
|
|
29
|
+
{indicator ?? <StepsIndicator />}
|
|
30
|
+
{connectors.trailing === "none" ? null : (
|
|
31
|
+
<StepsConnector
|
|
32
|
+
isComplete={isConnectorComplete(step, value)}
|
|
33
|
+
orientation={orientation}
|
|
34
|
+
slot={connectors.trailing}
|
|
35
|
+
/>
|
|
36
|
+
)}
|
|
37
|
+
</View>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
StepsTrack.displayName = "DelacourUI.Steps.Item.Track";
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The column beside a vertical `Steps.Panel`, as wide as the indicator, that
|
|
44
|
+
* carries the trailing line on past the panel. Internal.
|
|
45
|
+
*/
|
|
46
|
+
export function StepsRail(): ReactElement {
|
|
47
|
+
const { value, count } = useStepsPart("Steps.Item");
|
|
48
|
+
const { step, orientation, size, isLast } = useStepsItemPart("Steps.Item");
|
|
49
|
+
const { trailing } = resolveStepConnectors({ count, orientation, step });
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<View className={stepsVariants({ hasPanel: true, isLast, orientation, size }).rail()}>
|
|
53
|
+
{trailing === "line" ? (
|
|
54
|
+
<StepsConnector isComplete={isConnectorComplete(step, value)} orientation={orientation} slot="line" />
|
|
55
|
+
) : null}
|
|
56
|
+
</View>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
StepsRail.displayName = "DelacourUI.Steps.Item.Rail";
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createContext, type ReactElement, type ReactNode, use } from "react";
|
|
2
|
+
import type { StepStatus, StepsOrientation, StepsSize, StepsVariant } from "./steps.variants";
|
|
3
|
+
|
|
4
|
+
export type StepsContextValue = {
|
|
5
|
+
/** The current step, zero-based. */
|
|
6
|
+
value: number;
|
|
7
|
+
/** How many `Steps.Item`s the root found among its direct children. `0` when it could not count them. */
|
|
8
|
+
count: number;
|
|
9
|
+
/** What a step calls on press. Stays quiet when the step is already current. */
|
|
10
|
+
select: (step: number) => void;
|
|
11
|
+
orientation: StepsOrientation;
|
|
12
|
+
size: StepsSize;
|
|
13
|
+
variant: StepsVariant;
|
|
14
|
+
/** Steps render as plain views and take no presses. */
|
|
15
|
+
isReadOnly: boolean;
|
|
16
|
+
/** Steps ahead of the value cannot be pressed. */
|
|
17
|
+
isLinear: boolean;
|
|
18
|
+
/** Raw rather than resolved, so one step can still disable itself — see `RadioGroupContextValue`. */
|
|
19
|
+
isDisabled?: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type StepsItemContextValue = {
|
|
23
|
+
/** This step's index, zero-based. */
|
|
24
|
+
step: number;
|
|
25
|
+
status: StepStatus;
|
|
26
|
+
/** Whether this step is the value — true even when `completed` paints it done. */
|
|
27
|
+
isCurrent: boolean;
|
|
28
|
+
isInvalid: boolean;
|
|
29
|
+
isLoading: boolean;
|
|
30
|
+
isDisabled: boolean;
|
|
31
|
+
isLast: boolean;
|
|
32
|
+
orientation: StepsOrientation;
|
|
33
|
+
size: StepsSize;
|
|
34
|
+
variant: StepsVariant;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const StepsContext = createContext<StepsContextValue | null>(null);
|
|
38
|
+
const StepsItemContext = createContext<StepsItemContextValue | null>(null);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Supplies the stepper's value and axes to its steps.
|
|
42
|
+
*
|
|
43
|
+
* Lives in its own module, importing nothing but React and types, so a part can
|
|
44
|
+
* read it without importing `./steps` — which would close a cycle and leave the
|
|
45
|
+
* context `undefined` on a cold start. See AGENTS.md rule 3.
|
|
46
|
+
*/
|
|
47
|
+
export function StepsProvider({ value, children }: { value: StepsContextValue; children: ReactNode }): ReactElement {
|
|
48
|
+
return <StepsContext value={value}>{children}</StepsContext>;
|
|
49
|
+
}
|
|
50
|
+
StepsProvider.displayName = "DelacourUI.Steps.Provider";
|
|
51
|
+
|
|
52
|
+
/** Supplies one step's settled state to its indicator, title and description. */
|
|
53
|
+
export function StepsItemProvider({
|
|
54
|
+
value,
|
|
55
|
+
children,
|
|
56
|
+
}: {
|
|
57
|
+
value: StepsItemContextValue;
|
|
58
|
+
children: ReactNode;
|
|
59
|
+
}): ReactElement {
|
|
60
|
+
return <StepsItemContext value={value}>{children}</StepsItemContext>;
|
|
61
|
+
}
|
|
62
|
+
StepsItemProvider.displayName = "DelacourUI.Steps.Item.Provider";
|
|
63
|
+
|
|
64
|
+
/** The enclosing stepper's context, or null outside a `<Steps>`. */
|
|
65
|
+
export function useStepsContext(): StepsContextValue | null {
|
|
66
|
+
return use(StepsContext);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reads the enclosing stepper's value and axes.
|
|
71
|
+
*
|
|
72
|
+
* For a custom control — a Continue button, a progress caption — that has to
|
|
73
|
+
* follow the stepper it sits beside. Throws outside one.
|
|
74
|
+
*/
|
|
75
|
+
export function useSteps(): StepsContextValue {
|
|
76
|
+
const context = useStepsContext();
|
|
77
|
+
if (!context) {
|
|
78
|
+
throw new Error("useSteps must be called inside a <Steps>.");
|
|
79
|
+
}
|
|
80
|
+
return context;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The enclosing step's settled state, or null outside a `<Steps.Item>`. */
|
|
84
|
+
export function useStepsItemContext(): StepsItemContextValue | null {
|
|
85
|
+
return use(StepsItemContext);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Reads the enclosing step's settled state.
|
|
90
|
+
*
|
|
91
|
+
* Lets a custom child — an indicator of your own, a status badge — style itself
|
|
92
|
+
* to match. Throws outside a `<Steps.Item>`.
|
|
93
|
+
*/
|
|
94
|
+
export function useStepsItem(): StepsItemContextValue {
|
|
95
|
+
const context = useStepsItemContext();
|
|
96
|
+
if (!context) {
|
|
97
|
+
throw new Error("useStepsItem must be called inside a <Steps.Item>.");
|
|
98
|
+
}
|
|
99
|
+
return context;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The enclosing stepper, for a part that cannot work without one.
|
|
104
|
+
*
|
|
105
|
+
* Internal: not re-exported from `index.ts`. A caller wants {@link useSteps},
|
|
106
|
+
* whose error names the hook rather than a part.
|
|
107
|
+
*/
|
|
108
|
+
export function useStepsPart(component: string): StepsContextValue {
|
|
109
|
+
const context = useStepsContext();
|
|
110
|
+
if (!context) {
|
|
111
|
+
throw new Error(`${component} must be rendered inside a <Steps>.`);
|
|
112
|
+
}
|
|
113
|
+
return context;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The enclosing step, for a part that cannot work without one. Internal. */
|
|
117
|
+
export function useStepsItemPart(component: string): StepsItemContextValue {
|
|
118
|
+
const context = useStepsItemContext();
|
|
119
|
+
if (!context) {
|
|
120
|
+
throw new Error(`${component} must be rendered inside a <Steps.Item>.`);
|
|
121
|
+
}
|
|
122
|
+
return context;
|
|
123
|
+
}
|