@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,152 @@
|
|
|
1
|
+
import { type ReactElement, useEffect } from "react";
|
|
2
|
+
import type { ViewProps } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
cancelAnimation,
|
|
5
|
+
Easing,
|
|
6
|
+
ReduceMotion,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
useReducedMotion,
|
|
9
|
+
useSharedValue,
|
|
10
|
+
withRepeat,
|
|
11
|
+
withTiming,
|
|
12
|
+
} from "react-native-reanimated";
|
|
13
|
+
import { useProgressPart } from "./progress.context";
|
|
14
|
+
import {
|
|
15
|
+
fillTranslate,
|
|
16
|
+
indeterminateSegment,
|
|
17
|
+
PROGRESS_FILL_DURATION_MS,
|
|
18
|
+
PROGRESS_INDETERMINATE_DURATION_MS,
|
|
19
|
+
PROGRESS_INDETERMINATE_SEGMENT,
|
|
20
|
+
PROGRESS_PULSE,
|
|
21
|
+
progressVariants,
|
|
22
|
+
} from "./progress.variants";
|
|
23
|
+
|
|
24
|
+
export type ProgressFillProps = Omit<ViewProps, "children" | "style"> & {
|
|
25
|
+
className?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The painted bar.
|
|
30
|
+
*
|
|
31
|
+
* **Everything it animates runs on the UI thread.** A new value is handed to
|
|
32
|
+
* `withTiming` once, from an effect, and the frames that follow never touch
|
|
33
|
+
* React — so an upload reporting ten times a second costs ten renders, not six
|
|
34
|
+
* hundred.
|
|
35
|
+
*
|
|
36
|
+
* **Determinate, it is a full-length bar parked left of the track** by what is
|
|
37
|
+
* still to go, and a `translateX` slides it in. A transform composites without a
|
|
38
|
+
* layout pass; an animated `width` would re-lay the bar out on every frame. The
|
|
39
|
+
* track clips what is parked.
|
|
40
|
+
*
|
|
41
|
+
* **Indeterminate, it is a segment sweeping across**, from wholly off the left
|
|
42
|
+
* edge to wholly off the right, so the loop's seam is never on screen. With the
|
|
43
|
+
* system's reduce-motion setting on, the sweep becomes the whole track breathing
|
|
44
|
+
* between two opacities instead: still visibly alive — which a status indicator
|
|
45
|
+
* must be, the same reasoning `Spinner` gives for opting out of the policy — but
|
|
46
|
+
* with nothing travelling across the screen.
|
|
47
|
+
*
|
|
48
|
+
* It is hidden until the track has been measured. A fill positioned against a
|
|
49
|
+
* width of 0 would flash at full length for the frame before layout lands.
|
|
50
|
+
*/
|
|
51
|
+
export function ProgressFill({ className, ...props }: ProgressFillProps): ReactElement {
|
|
52
|
+
const { ratio, isIndeterminate, color, size, trackSize } = useProgressPart("Progress.Fill");
|
|
53
|
+
const isReducedMotion = useReducedMotion();
|
|
54
|
+
|
|
55
|
+
// Seeded at the current ratio, so a bar that mounts at 60% is drawn at 60%
|
|
56
|
+
// rather than animating up from nothing every time a list row scrolls in.
|
|
57
|
+
const progress = useSharedValue(ratio);
|
|
58
|
+
const phase = useSharedValue(0);
|
|
59
|
+
const pulse = useSharedValue<number>(PROGRESS_PULSE.to);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (isIndeterminate) return;
|
|
63
|
+
progress.value = withTiming(ratio, {
|
|
64
|
+
duration: PROGRESS_FILL_DURATION_MS,
|
|
65
|
+
easing: Easing.out(Easing.cubic),
|
|
66
|
+
});
|
|
67
|
+
}, [isIndeterminate, progress, ratio]);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!isIndeterminate) return;
|
|
71
|
+
|
|
72
|
+
if (isReducedMotion) {
|
|
73
|
+
pulse.value = PROGRESS_PULSE.to;
|
|
74
|
+
pulse.value = withRepeat(
|
|
75
|
+
withTiming(PROGRESS_PULSE.from, {
|
|
76
|
+
duration: PROGRESS_PULSE.durationMs,
|
|
77
|
+
easing: Easing.inOut(Easing.ease),
|
|
78
|
+
// Under the default `System` policy the timing completes instantly
|
|
79
|
+
// while reduce motion is on, and `withRepeat(-1)` would spin a
|
|
80
|
+
// zero-length animation forever — a frozen bar, which reads as hung.
|
|
81
|
+
reduceMotion: ReduceMotion.Never,
|
|
82
|
+
}),
|
|
83
|
+
-1,
|
|
84
|
+
true,
|
|
85
|
+
undefined,
|
|
86
|
+
// The repeat has a reduce-motion policy of its own, separate from the
|
|
87
|
+
// timing's. Left at `System`, it declines to start a reversing loop at
|
|
88
|
+
// all and stops a forward one after a single pass.
|
|
89
|
+
ReduceMotion.Never
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
phase.value = 0;
|
|
93
|
+
phase.value = withRepeat(
|
|
94
|
+
withTiming(1, {
|
|
95
|
+
duration: PROGRESS_INDETERMINATE_DURATION_MS,
|
|
96
|
+
easing: Easing.inOut(Easing.ease),
|
|
97
|
+
reduceMotion: ReduceMotion.Never,
|
|
98
|
+
}),
|
|
99
|
+
-1,
|
|
100
|
+
false,
|
|
101
|
+
undefined,
|
|
102
|
+
ReduceMotion.Never
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Without this the repeat outlives the bar, or the switch back to a value.
|
|
107
|
+
return () => {
|
|
108
|
+
cancelAnimation(phase);
|
|
109
|
+
cancelAnimation(pulse);
|
|
110
|
+
pulse.value = PROGRESS_PULSE.to;
|
|
111
|
+
};
|
|
112
|
+
}, [isIndeterminate, isReducedMotion, phase, pulse]);
|
|
113
|
+
|
|
114
|
+
const fillStyle = useAnimatedStyle(() => {
|
|
115
|
+
const track = trackSize.value;
|
|
116
|
+
const isMeasured = track > 0;
|
|
117
|
+
|
|
118
|
+
// The same keys every frame, so Reanimated is never asked to swap a style's
|
|
119
|
+
// shape when the bar flips between modes.
|
|
120
|
+
if (isIndeterminate && !isReducedMotion) {
|
|
121
|
+
const segment = indeterminateSegment({
|
|
122
|
+
phase: phase.value,
|
|
123
|
+
segment: PROGRESS_INDETERMINATE_SEGMENT,
|
|
124
|
+
trackSize: track,
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
opacity: isMeasured ? 1 : 0,
|
|
128
|
+
transform: [{ translateX: segment.translate }],
|
|
129
|
+
width: segment.width,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (isIndeterminate) {
|
|
134
|
+
return { opacity: isMeasured ? pulse.value : 0, transform: [{ translateX: 0 }], width: track };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
opacity: isMeasured ? 1 : 0,
|
|
139
|
+
transform: [{ translateX: fillTranslate({ ratio: progress.value, trackSize: track }) }],
|
|
140
|
+
width: track,
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<Animated.View
|
|
146
|
+
className={progressVariants({ color, isIndeterminate, size }).fill({ className })}
|
|
147
|
+
style={fillStyle}
|
|
148
|
+
{...props}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
ProgressFill.displayName = "DelacourUI.Progress.Fill";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { useProgressPart } from "./progress.context";
|
|
4
|
+
import { progressVariants } from "./progress.variants";
|
|
5
|
+
|
|
6
|
+
export type ProgressHeaderProps = ViewProps;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The row above the track: a label at the start, a readout at the end.
|
|
10
|
+
*
|
|
11
|
+
* A part rather than a `View` the caller writes because every labelled bar
|
|
12
|
+
* wants exactly this row, and a hand-written one drifts — a missing `gap` lets a
|
|
13
|
+
* long label run into the percentage.
|
|
14
|
+
*/
|
|
15
|
+
export function ProgressHeader({ className, ...props }: ProgressHeaderProps): ReactElement {
|
|
16
|
+
const { color, size, isIndeterminate } = useProgressPart("Progress.Header");
|
|
17
|
+
return <View className={progressVariants({ color, isIndeterminate, size }).header({ className })} {...props} />;
|
|
18
|
+
}
|
|
19
|
+
ProgressHeader.displayName = "DelacourUI.Progress.Header";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Text, type TextPresetProps } from "../text";
|
|
3
|
+
import { useProgressPart } from "./progress.context";
|
|
4
|
+
import { PROGRESS_OUTPUT_TEXT_SIZE, progressVariants } from "./progress.variants";
|
|
5
|
+
|
|
6
|
+
export type ProgressLabelProps = TextPresetProps;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What the bar is measuring — "Uploading", "Storage".
|
|
10
|
+
*
|
|
11
|
+
* Renders `Text.Label` and names a size step, never a scale of its own, so the
|
|
12
|
+
* weight and colour stay in the preset. It is left visible to assistive
|
|
13
|
+
* technology: the root is one accessible element, and a label inside it is what
|
|
14
|
+
* VoiceOver and TalkBack read as that element's name.
|
|
15
|
+
*/
|
|
16
|
+
export function ProgressLabel({ className, size, ...props }: ProgressLabelProps): ReactElement {
|
|
17
|
+
const { color, size: progressSize, isIndeterminate } = useProgressPart("Progress.Label");
|
|
18
|
+
return (
|
|
19
|
+
<Text.Label
|
|
20
|
+
className={progressVariants({ color, isIndeterminate, size: progressSize }).label({ className })}
|
|
21
|
+
numberOfLines={1}
|
|
22
|
+
size={size ?? PROGRESS_OUTPUT_TEXT_SIZE[progressSize]}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
ProgressLabel.displayName = "DelacourUI.Progress.Label";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import { Text, type TextPresetProps } from "../text";
|
|
3
|
+
import { useProgressPart } from "./progress.context";
|
|
4
|
+
import type { ProgressRenderChildren } from "./progress.types";
|
|
5
|
+
import { PROGRESS_OUTPUT_TEXT_SIZE, progressVariants } from "./progress.variants";
|
|
6
|
+
|
|
7
|
+
export type ProgressOutputProps = Omit<TextPresetProps, "children"> & {
|
|
8
|
+
/** Custom content, or a function called with the bar's settled state. */
|
|
9
|
+
children?: ProgressRenderChildren;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The current value, formatted — `72%` by default.
|
|
14
|
+
*
|
|
15
|
+
* A function child is handed the bar's state, which is how a count reads
|
|
16
|
+
* `18 of 24` rather than a percentage.
|
|
17
|
+
*
|
|
18
|
+
* **Hidden from assistive technology on purpose.** The root already publishes
|
|
19
|
+
* the value as `accessibilityValue`, so a readout left visible inside it would be
|
|
20
|
+
* read twice — once as part of the element's name, once as its value.
|
|
21
|
+
*
|
|
22
|
+
* **An indeterminate bar has no value**, so the default readout renders nothing.
|
|
23
|
+
* A function child still runs, which is where a "Waiting…" belongs.
|
|
24
|
+
*/
|
|
25
|
+
export function ProgressOutput({ children, className, size, ...props }: ProgressOutputProps): ReactElement | null {
|
|
26
|
+
const { color, size: progressSize, isIndeterminate, renderProps } = useProgressPart("Progress.Output");
|
|
27
|
+
|
|
28
|
+
const content =
|
|
29
|
+
typeof children === "function"
|
|
30
|
+
? children(renderProps)
|
|
31
|
+
: (children ?? (isIndeterminate ? null : renderProps.formatted));
|
|
32
|
+
|
|
33
|
+
if (content === null || content === undefined) return null;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Text.Label
|
|
37
|
+
accessibilityElementsHidden
|
|
38
|
+
className={progressVariants({ color, isIndeterminate, size: progressSize }).output({ className })}
|
|
39
|
+
importantForAccessibility="no-hide-descendants"
|
|
40
|
+
size={size ?? PROGRESS_OUTPUT_TEXT_SIZE[progressSize]}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
{content}
|
|
44
|
+
</Text.Label>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
ProgressOutput.displayName = "DelacourUI.Progress.Output";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, useCallback } from "react";
|
|
2
|
+
import { type LayoutChangeEvent, View, type ViewProps } from "react-native";
|
|
3
|
+
import { useProgressPart } from "./progress.context";
|
|
4
|
+
import { progressVariants } from "./progress.variants";
|
|
5
|
+
|
|
6
|
+
export type ProgressTrackProps = Omit<ViewProps, "children"> & {
|
|
7
|
+
/** The fill. */
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The clipped, rounded groove the fill slides along.
|
|
13
|
+
*
|
|
14
|
+
* It measures its own width into the shared value the fill reads, because the
|
|
15
|
+
* fill is positioned by a transform in points and a transform cannot name a
|
|
16
|
+
* percentage. The clip is what turns a full-length bar parked off to the left
|
|
17
|
+
* into a bar that appears to grow — see `progressVariants`.
|
|
18
|
+
*/
|
|
19
|
+
export function ProgressTrack({ className, onLayout, ...props }: ProgressTrackProps): ReactElement {
|
|
20
|
+
const { color, size, isIndeterminate, trackSize } = useProgressPart("Progress.Track");
|
|
21
|
+
|
|
22
|
+
const handleLayout = useCallback(
|
|
23
|
+
(event: LayoutChangeEvent) => {
|
|
24
|
+
trackSize.value = event.nativeEvent.layout.width;
|
|
25
|
+
onLayout?.(event);
|
|
26
|
+
},
|
|
27
|
+
[onLayout, trackSize]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<View
|
|
32
|
+
className={progressVariants({ color, isIndeterminate, size }).track({ className })}
|
|
33
|
+
onLayout={handleLayout}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
ProgressTrack.displayName = "DelacourUI.Progress.Track";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createContext, type ReactElement, type ReactNode, use } from "react";
|
|
2
|
+
import type { SharedValue } from "react-native-reanimated";
|
|
3
|
+
import type { ProgressRenderProps } from "./progress.types";
|
|
4
|
+
import type { ProgressColor, ProgressSize } from "./progress.variants";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What a progress bar's parts read.
|
|
8
|
+
*
|
|
9
|
+
* **The track and the fill read only `ratio`, `isIndeterminate`, `color`, `size`
|
|
10
|
+
* and `trackSize`.** That is deliberate: a meter is the same track and the same
|
|
11
|
+
* fill with a different root deciding the ratio and the colour, so a root that
|
|
12
|
+
* publishes this shape can reuse `Progress.Track` and `Progress.Fill` as they
|
|
13
|
+
* stand. Nothing the parts draw reaches back to how the ratio was arrived at.
|
|
14
|
+
*/
|
|
15
|
+
export type ProgressContextValue = {
|
|
16
|
+
/** Where the value sits in the range, clamped to 0–1, on the JS thread. */
|
|
17
|
+
ratio: number;
|
|
18
|
+
/** Loops a segment along the track instead of showing a value. */
|
|
19
|
+
isIndeterminate: boolean;
|
|
20
|
+
color: ProgressColor;
|
|
21
|
+
size: ProgressSize;
|
|
22
|
+
/** The track's own width, from its `onLayout`. `0` until it has been measured. */
|
|
23
|
+
trackSize: SharedValue<number>;
|
|
24
|
+
/** The settled state `Progress.Output` formats and a render function is handed. */
|
|
25
|
+
renderProps: ProgressRenderProps;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ProgressContext = createContext<ProgressContextValue | null>(null);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Supplies one progress bar's state to its own parts.
|
|
32
|
+
*
|
|
33
|
+
* Lives in its own module, importing nothing but React and types, so a part can
|
|
34
|
+
* read it without importing `./progress` — that import would close a cycle, and
|
|
35
|
+
* Metro serves a partially initialised module for a cycle.
|
|
36
|
+
*/
|
|
37
|
+
export function ProgressProvider({
|
|
38
|
+
value,
|
|
39
|
+
children,
|
|
40
|
+
}: {
|
|
41
|
+
value: ProgressContextValue;
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
}): ReactElement {
|
|
44
|
+
return <ProgressContext value={value}>{children}</ProgressContext>;
|
|
45
|
+
}
|
|
46
|
+
ProgressProvider.displayName = "DelacourUI.Progress.Provider";
|
|
47
|
+
|
|
48
|
+
/** The enclosing progress bar's state, or null outside a `<Progress>`. */
|
|
49
|
+
export function useProgressContext(): ProgressContextValue | null {
|
|
50
|
+
return use(ProgressContext);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Reads the enclosing progress bar's state.
|
|
55
|
+
*
|
|
56
|
+
* For a custom readout or a custom fill that has to match the bar it sits in.
|
|
57
|
+
* Throws outside one — use {@link useProgressContext} where the bar is optional.
|
|
58
|
+
*/
|
|
59
|
+
export function useProgress(): ProgressContextValue {
|
|
60
|
+
const context = useProgressContext();
|
|
61
|
+
if (!context) {
|
|
62
|
+
throw new Error("useProgress must be called inside a <Progress>.");
|
|
63
|
+
}
|
|
64
|
+
return context;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The enclosing bar's state, for a compound part that cannot work without one.
|
|
69
|
+
*
|
|
70
|
+
* Internal: deliberately not re-exported from `index.ts`. A caller outside the
|
|
71
|
+
* library wants {@link useProgress}, whose error names the hook rather than a part.
|
|
72
|
+
*/
|
|
73
|
+
export function useProgressPart(component: string): ProgressContextValue {
|
|
74
|
+
const context = useProgressContext();
|
|
75
|
+
if (!context) {
|
|
76
|
+
throw new Error(`${component} must be rendered inside a <Progress>.`);
|
|
77
|
+
}
|
|
78
|
+
return context;
|
|
79
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode, useMemo } from "react";
|
|
2
|
+
import { View, type ViewProps } from "react-native";
|
|
3
|
+
import { useSharedValue } from "react-native-reanimated";
|
|
4
|
+
import { type ProgressContextValue, ProgressProvider } from "./progress.context";
|
|
5
|
+
import type { ProgressRenderProps } from "./progress.types";
|
|
6
|
+
import {
|
|
7
|
+
formatProgressValue,
|
|
8
|
+
PROGRESS_MAX_VALUE,
|
|
9
|
+
PROGRESS_MIN_VALUE,
|
|
10
|
+
type ProgressColor,
|
|
11
|
+
type ProgressSize,
|
|
12
|
+
progressRatio,
|
|
13
|
+
progressVariants,
|
|
14
|
+
resolveProgressAccessibility,
|
|
15
|
+
resolveProgressAxes,
|
|
16
|
+
} from "./progress.variants";
|
|
17
|
+
import { ProgressFill } from "./progress-fill";
|
|
18
|
+
import { ProgressHeader } from "./progress-header";
|
|
19
|
+
import { ProgressLabel } from "./progress-label";
|
|
20
|
+
import { ProgressOutput } from "./progress-output";
|
|
21
|
+
import { ProgressTrack } from "./progress-track";
|
|
22
|
+
|
|
23
|
+
export type ProgressProps = Omit<ViewProps, "children"> & {
|
|
24
|
+
/** How far along, between `minValue` and `maxValue`. Ignored while indeterminate. */
|
|
25
|
+
value?: number;
|
|
26
|
+
minValue?: number;
|
|
27
|
+
maxValue?: number;
|
|
28
|
+
/** Loops a segment along the track, for work whose length is not known yet. */
|
|
29
|
+
isIndeterminate?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Passed to `Intl.NumberFormat` by `Progress.Output` and the spoken value.
|
|
32
|
+
* With none, or a `percent` style, the readout is the share of the range.
|
|
33
|
+
*/
|
|
34
|
+
formatOptions?: Intl.NumberFormatOptions;
|
|
35
|
+
/** Replaces the formatted readout, on screen and to assistive technology. */
|
|
36
|
+
valueLabel?: string;
|
|
37
|
+
/** What the filled part of the track means. */
|
|
38
|
+
color?: ProgressColor;
|
|
39
|
+
size?: ProgressSize;
|
|
40
|
+
/** The anatomy. With none, the bar draws a bare track and fill. */
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function ProgressRoot({
|
|
45
|
+
value = PROGRESS_MIN_VALUE,
|
|
46
|
+
minValue = PROGRESS_MIN_VALUE,
|
|
47
|
+
maxValue = PROGRESS_MAX_VALUE,
|
|
48
|
+
isIndeterminate,
|
|
49
|
+
formatOptions,
|
|
50
|
+
valueLabel,
|
|
51
|
+
color,
|
|
52
|
+
size,
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
...props
|
|
56
|
+
}: ProgressProps): ReactElement {
|
|
57
|
+
const axes = resolveProgressAxes({ color, isIndeterminate, size });
|
|
58
|
+
const trackSize = useSharedValue(0);
|
|
59
|
+
|
|
60
|
+
const ratio = progressRatio(value, minValue, maxValue);
|
|
61
|
+
|
|
62
|
+
const formatted = useMemo(
|
|
63
|
+
() => valueLabel ?? formatProgressValue({ formatOptions, maxValue, minValue, value }),
|
|
64
|
+
[formatOptions, maxValue, minValue, value, valueLabel]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const accessibility = useMemo(
|
|
68
|
+
() =>
|
|
69
|
+
resolveProgressAccessibility({
|
|
70
|
+
formatOptions,
|
|
71
|
+
isIndeterminate: axes.isIndeterminate,
|
|
72
|
+
maxValue,
|
|
73
|
+
minValue,
|
|
74
|
+
value,
|
|
75
|
+
valueLabel,
|
|
76
|
+
}),
|
|
77
|
+
[axes.isIndeterminate, formatOptions, maxValue, minValue, value, valueLabel]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const renderProps = useMemo<ProgressRenderProps>(
|
|
81
|
+
() => ({ formatted, isIndeterminate: axes.isIndeterminate, maxValue, minValue, ratio, value }),
|
|
82
|
+
[axes.isIndeterminate, formatted, maxValue, minValue, ratio, value]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const context = useMemo<ProgressContextValue>(
|
|
86
|
+
() => ({
|
|
87
|
+
color: axes.color,
|
|
88
|
+
isIndeterminate: axes.isIndeterminate,
|
|
89
|
+
ratio,
|
|
90
|
+
renderProps,
|
|
91
|
+
size: axes.size,
|
|
92
|
+
trackSize,
|
|
93
|
+
}),
|
|
94
|
+
[axes.color, axes.isIndeterminate, axes.size, ratio, renderProps, trackSize]
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// A bare `<Progress value={40} />` draws the bar. A compound root with no
|
|
98
|
+
// default anatomy draws nothing at all, which is a failure with no error.
|
|
99
|
+
const content = children ?? (
|
|
100
|
+
<ProgressTrack>
|
|
101
|
+
<ProgressFill />
|
|
102
|
+
</ProgressTrack>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<ProgressProvider value={context}>
|
|
107
|
+
<View
|
|
108
|
+
accessibilityRole="progressbar"
|
|
109
|
+
accessibilityState={{ busy: accessibility.busy }}
|
|
110
|
+
accessibilityValue={accessibility.value}
|
|
111
|
+
accessible
|
|
112
|
+
className={progressVariants(axes).root({ className })}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
{content}
|
|
116
|
+
</View>
|
|
117
|
+
</ProgressProvider>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* How far a task has got — or, indeterminate, that it is under way.
|
|
123
|
+
*
|
|
124
|
+
* The anatomy is written out: a `Progress.Track` holding a `Progress.Fill`, with
|
|
125
|
+
* an optional `Progress.Header` above it carrying a `Progress.Label` and a
|
|
126
|
+
* `Progress.Output`. A bare `<Progress value={40} />` draws the track and fill on
|
|
127
|
+
* its own.
|
|
128
|
+
*
|
|
129
|
+
* **The fill animates on the UI thread.** A new value starts one timing and the
|
|
130
|
+
* frames never touch React. `isIndeterminate` loops a segment along the track
|
|
131
|
+
* instead, and with the system's reduce-motion setting on it breathes in place
|
|
132
|
+
* rather than travelling.
|
|
133
|
+
*
|
|
134
|
+
* **It is one accessible element with the `progressbar` role.** The range and
|
|
135
|
+
* the value are published as `accessibilityValue`, spoken as `72%` over 0–100
|
|
136
|
+
* and as `18 of 24` over any other range; a `Progress.Label` inside it is its
|
|
137
|
+
* name. An indeterminate bar reports `busy` and no value.
|
|
138
|
+
*
|
|
139
|
+
* It is not a control, so there is no disabled or invalid state and it takes
|
|
140
|
+
* nothing from an enclosing `Field`. A bar that has failed is `color="destructive"`.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* <Progress value={40} />
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* <Progress color="success" value={uploaded} maxValue={total} formatOptions={{ style: "unit", unit: "megabyte" }}>
|
|
147
|
+
* <Progress.Header>
|
|
148
|
+
* <Progress.Label>Uploading</Progress.Label>
|
|
149
|
+
* <Progress.Output />
|
|
150
|
+
* </Progress.Header>
|
|
151
|
+
* <Progress.Track>
|
|
152
|
+
* <Progress.Fill />
|
|
153
|
+
* </Progress.Track>
|
|
154
|
+
* </Progress>
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* <Progress accessibilityLabel="Syncing" isIndeterminate />
|
|
158
|
+
*/
|
|
159
|
+
export const Progress = Object.assign(ProgressRoot, {
|
|
160
|
+
/** The row above the track: a label at the start, a readout at the end. */
|
|
161
|
+
Header: ProgressHeader,
|
|
162
|
+
/** What the bar is measuring. Read as the bar's accessible name. */
|
|
163
|
+
Label: ProgressLabel,
|
|
164
|
+
/** The value, formatted. Give it a function to word it yourself. */
|
|
165
|
+
Output: ProgressOutput,
|
|
166
|
+
/** The clipped groove the fill slides along. */
|
|
167
|
+
Track: ProgressTrack,
|
|
168
|
+
/** The painted bar — a value, or a looping segment while indeterminate. */
|
|
169
|
+
Fill: ProgressFill,
|
|
170
|
+
displayName: "DelacourUI.Progress",
|
|
171
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What a render function inside a progress bar is handed.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the root's context and `Progress.Output`, which is what puts it here
|
|
7
|
+
* rather than in either one's own file — two consumers is the bar a
|
|
8
|
+
* `{name}.types.ts` entry has to clear. See AGENTS.md.
|
|
9
|
+
*/
|
|
10
|
+
export type ProgressRenderProps = {
|
|
11
|
+
/** The value as given, unclamped — the readout is the caller's to word. */
|
|
12
|
+
value: number;
|
|
13
|
+
minValue: number;
|
|
14
|
+
maxValue: number;
|
|
15
|
+
/** Where the value sits in the range, clamped to 0–1. */
|
|
16
|
+
ratio: number;
|
|
17
|
+
/** The readout's default text: `valueLabel`, or the formatted value. */
|
|
18
|
+
formatted: string;
|
|
19
|
+
isIndeterminate: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** A part whose children may be written out or computed from the bar's state. */
|
|
23
|
+
export type ProgressRenderChildren = ReactNode | ((props: ProgressRenderProps) => ReactNode);
|