react-native-terra-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -0
- package/lib/module/components/avatar/Avatar.js +121 -0
- package/lib/module/components/avatar/Avatar.js.map +1 -0
- package/lib/module/components/avatar/index.js +4 -0
- package/lib/module/components/avatar/index.js.map +1 -0
- package/lib/module/components/avatar/types.js +4 -0
- package/lib/module/components/avatar/types.js.map +1 -0
- package/lib/module/components/box/Box.js +49 -0
- package/lib/module/components/box/Box.js.map +1 -0
- package/lib/module/components/box/index.js +4 -0
- package/lib/module/components/box/index.js.map +1 -0
- package/lib/module/components/box/utils.js +95 -0
- package/lib/module/components/box/utils.js.map +1 -0
- package/lib/module/components/button/Button.js +281 -0
- package/lib/module/components/button/Button.js.map +1 -0
- package/lib/module/components/button/index.js +4 -0
- package/lib/module/components/button/index.js.map +1 -0
- package/lib/module/components/header/Header.js +25 -0
- package/lib/module/components/header/Header.js.map +1 -0
- package/lib/module/components/header/header-buttons.js +65 -0
- package/lib/module/components/header/header-buttons.js.map +1 -0
- package/lib/module/components/header/index.js +7 -0
- package/lib/module/components/header/index.js.map +1 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js +335 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -0
- package/lib/module/components/header/variants/TitleHeader/index.js +184 -0
- package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -0
- package/lib/module/components/icon/Icon.js +53 -0
- package/lib/module/components/icon/Icon.js.map +1 -0
- package/lib/module/components/icon/index.js +4 -0
- package/lib/module/components/icon/index.js.map +1 -0
- package/lib/module/components/icon/utils.js +188 -0
- package/lib/module/components/icon/utils.js.map +1 -0
- package/lib/module/components/index.js +16 -0
- package/lib/module/components/index.js.map +1 -0
- package/lib/module/components/page-indicator/PageIndicator.js +49 -0
- package/lib/module/components/page-indicator/PageIndicator.js.map +1 -0
- package/lib/module/components/page-indicator/PageIndicatorRoot.js +24 -0
- package/lib/module/components/page-indicator/PageIndicatorRoot.js.map +1 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js +24 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js.map +1 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js +20 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js.map +1 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js +68 -0
- package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js.map +1 -0
- package/lib/module/components/page-indicator/index.js +5 -0
- package/lib/module/components/page-indicator/index.js.map +1 -0
- package/lib/module/components/page-indicator/utils.js +127 -0
- package/lib/module/components/page-indicator/utils.js.map +1 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js +50 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js.map +1 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js +165 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js.map +1 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js +60 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js.map +1 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/index.js +87 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/index.js.map +1 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/utils.js +28 -0
- package/lib/module/components/page-indicator/variants/DotIndicator/utils.js.map +1 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js +53 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js.map +1 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/index.js +76 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/index.js.map +1 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/utils.js +47 -0
- package/lib/module/components/page-indicator/variants/PillIndicator/utils.js.map +1 -0
- package/lib/module/components/portal/PortalContext.js +20 -0
- package/lib/module/components/portal/PortalContext.js.map +1 -0
- package/lib/module/components/portal/PortalHost.js +23 -0
- package/lib/module/components/portal/PortalHost.js.map +1 -0
- package/lib/module/components/portal/PortalProvider.js +40 -0
- package/lib/module/components/portal/PortalProvider.js.map +1 -0
- package/lib/module/components/portal/index.js +6 -0
- package/lib/module/components/portal/index.js.map +1 -0
- package/lib/module/components/screen/Screen.js +100 -0
- package/lib/module/components/screen/Screen.js.map +1 -0
- package/lib/module/components/screen/ScreenContext.js +66 -0
- package/lib/module/components/screen/ScreenContext.js.map +1 -0
- package/lib/module/components/screen/ScreenFlatList.js +75 -0
- package/lib/module/components/screen/ScreenFlatList.js.map +1 -0
- package/lib/module/components/screen/ScreenScrollView.js +82 -0
- package/lib/module/components/screen/ScreenScrollView.js.map +1 -0
- package/lib/module/components/screen/index.js +7 -0
- package/lib/module/components/screen/index.js.map +1 -0
- package/lib/module/components/screen/utils.js +13 -0
- package/lib/module/components/screen/utils.js.map +1 -0
- package/lib/module/components/slot/Slot.js +38 -0
- package/lib/module/components/slot/Slot.js.map +1 -0
- package/lib/module/components/slot/index.js +10 -0
- package/lib/module/components/slot/index.js.map +1 -0
- package/lib/module/components/slot/utils.js +55 -0
- package/lib/module/components/slot/utils.js.map +1 -0
- package/lib/module/components/spinner/Spinner.js +132 -0
- package/lib/module/components/spinner/Spinner.js.map +1 -0
- package/lib/module/components/spinner/index.js +4 -0
- package/lib/module/components/spinner/index.js.map +1 -0
- package/lib/module/components/surface/Surface.js +74 -0
- package/lib/module/components/surface/Surface.js.map +1 -0
- package/lib/module/components/surface/index.js +4 -0
- package/lib/module/components/surface/index.js.map +1 -0
- package/lib/module/components/text/Text.js +67 -0
- package/lib/module/components/text/Text.js.map +1 -0
- package/lib/module/components/text/index.js +4 -0
- package/lib/module/components/text/index.js.map +1 -0
- package/lib/module/components/toast/Toast.js +350 -0
- package/lib/module/components/toast/Toast.js.map +1 -0
- package/lib/module/components/toast/ToastProvider.js +272 -0
- package/lib/module/components/toast/ToastProvider.js.map +1 -0
- package/lib/module/components/toast/index.js +5 -0
- package/lib/module/components/toast/index.js.map +1 -0
- package/lib/module/components/toast/parts/ToastAnimatedItem.js +168 -0
- package/lib/module/components/toast/parts/ToastAnimatedItem.js.map +1 -0
- package/lib/module/components/toast/types.js +4 -0
- package/lib/module/components/toast/types.js.map +1 -0
- package/lib/module/components/toast/utils/controller.js +19 -0
- package/lib/module/components/toast/utils/controller.js.map +1 -0
- package/lib/module/context/ThemeProvider.js +66 -0
- package/lib/module/context/ThemeProvider.js.map +1 -0
- package/lib/module/context/index.js +4 -0
- package/lib/module/context/index.js.map +1 -0
- package/lib/module/index.js +8 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/theme/configure.js +152 -0
- package/lib/module/theme/configure.js.map +1 -0
- package/lib/module/theme/index.js +22 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/runtime.js +25 -0
- package/lib/module/theme/runtime.js.map +1 -0
- package/lib/module/theme/theme.js +121 -0
- package/lib/module/theme/theme.js.map +1 -0
- package/lib/module/theme/tokens/dark.js +109 -0
- package/lib/module/theme/tokens/dark.js.map +1 -0
- package/lib/module/theme/tokens/light.js +115 -0
- package/lib/module/theme/tokens/light.js.map +1 -0
- package/lib/module/theme/tokens/primitives.js +228 -0
- package/lib/module/theme/tokens/primitives.js.map +1 -0
- package/lib/module/theme/types.js +4 -0
- package/lib/module/theme/types.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/accent-utils.js +52 -0
- package/lib/module/utils/accent-utils.js.map +1 -0
- package/lib/module/utils/color-utils.js +53 -0
- package/lib/module/utils/color-utils.js.map +1 -0
- package/lib/module/utils/deep-merge.js +28 -0
- package/lib/module/utils/deep-merge.js.map +1 -0
- package/lib/module/utils/resolve-theme-color.js +20 -0
- package/lib/module/utils/resolve-theme-color.js.map +1 -0
- package/lib/module/utils/typography.js +13 -0
- package/lib/module/utils/typography.js.map +1 -0
- package/lib/module/utils/unflatten.js +35 -0
- package/lib/module/utils/unflatten.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/components/avatar/Avatar.d.ts +3 -0
- package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/index.d.ts +3 -0
- package/lib/typescript/src/components/avatar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar/types.d.ts +30 -0
- package/lib/typescript/src/components/avatar/types.d.ts.map +1 -0
- package/lib/typescript/src/components/box/Box.d.ts +12 -0
- package/lib/typescript/src/components/box/Box.d.ts.map +1 -0
- package/lib/typescript/src/components/box/index.d.ts +4 -0
- package/lib/typescript/src/components/box/index.d.ts.map +1 -0
- package/lib/typescript/src/components/box/utils.d.ts +39 -0
- package/lib/typescript/src/components/box/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/button/Button.d.ts +46 -0
- package/lib/typescript/src/components/button/Button.d.ts.map +1 -0
- package/lib/typescript/src/components/button/index.d.ts +3 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -0
- package/lib/typescript/src/components/header/Header.d.ts +22 -0
- package/lib/typescript/src/components/header/Header.d.ts.map +1 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts +16 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -0
- package/lib/typescript/src/components/header/index.d.ts +7 -0
- package/lib/typescript/src/components/header/index.d.ts.map +1 -0
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +25 -0
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +28 -0
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/icon/Icon.d.ts +22 -0
- package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -0
- package/lib/typescript/src/components/icon/index.d.ts +3 -0
- package/lib/typescript/src/components/icon/index.d.ts.map +1 -0
- package/lib/typescript/src/components/icon/utils.d.ts +4 -0
- package/lib/typescript/src/components/icon/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +14 -0
- package/lib/typescript/src/components/index.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts +48 -0
- package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/PageIndicatorRoot.d.ts +13 -0
- package/lib/typescript/src/components/page-indicator/PageIndicatorRoot.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts +14 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-core.d.ts +11 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-core.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-progress.d.ts +19 -0
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-progress.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/index.d.ts +4 -0
- package/lib/typescript/src/components/page-indicator/index.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/utils.d.ts +36 -0
- package/lib/typescript/src/components/page-indicator/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/InactiveDot.d.ts +14 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/InactiveDot.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.d.ts +11 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/TravelingPill.d.ts +15 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/TravelingPill.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts +32 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/utils.d.ts +14 -0
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.d.ts +16 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts +28 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts.map +1 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/utils.d.ts +21 -0
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/PortalContext.d.ts +19 -0
- package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/PortalHost.d.ts +7 -0
- package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/PortalProvider.d.ts +11 -0
- package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/index.d.ts +6 -0
- package/lib/typescript/src/components/portal/index.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/Screen.d.ts +53 -0
- package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenContext.d.ts +64 -0
- package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +26 -0
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +36 -0
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/index.d.ts +9 -0
- package/lib/typescript/src/components/screen/index.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/utils.d.ts +10 -0
- package/lib/typescript/src/components/screen/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/slot/Slot.d.ts +17 -0
- package/lib/typescript/src/components/slot/Slot.d.ts.map +1 -0
- package/lib/typescript/src/components/slot/index.d.ts +8 -0
- package/lib/typescript/src/components/slot/index.d.ts.map +1 -0
- package/lib/typescript/src/components/slot/utils.d.ts +17 -0
- package/lib/typescript/src/components/slot/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/spinner/Spinner.d.ts +18 -0
- package/lib/typescript/src/components/spinner/Spinner.d.ts.map +1 -0
- package/lib/typescript/src/components/spinner/index.d.ts +3 -0
- package/lib/typescript/src/components/spinner/index.d.ts.map +1 -0
- package/lib/typescript/src/components/surface/Surface.d.ts +31 -0
- package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -0
- package/lib/typescript/src/components/surface/index.d.ts +3 -0
- package/lib/typescript/src/components/surface/index.d.ts.map +1 -0
- package/lib/typescript/src/components/text/Text.d.ts +23 -0
- package/lib/typescript/src/components/text/Text.d.ts.map +1 -0
- package/lib/typescript/src/components/text/index.d.ts +3 -0
- package/lib/typescript/src/components/text/index.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/Toast.d.ts +71 -0
- package/lib/typescript/src/components/toast/Toast.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/ToastProvider.d.ts +4 -0
- package/lib/typescript/src/components/toast/ToastProvider.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/index.d.ts +5 -0
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/parts/ToastAnimatedItem.d.ts +18 -0
- package/lib/typescript/src/components/toast/parts/ToastAnimatedItem.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/types.d.ts +62 -0
- package/lib/typescript/src/components/toast/types.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/utils/controller.d.ts +5 -0
- package/lib/typescript/src/components/toast/utils/controller.d.ts.map +1 -0
- package/lib/typescript/src/context/ThemeProvider.d.ts +29 -0
- package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/src/context/index.d.ts +3 -0
- package/lib/typescript/src/context/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/configure.d.ts +47 -0
- package/lib/typescript/src/theme/configure.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +11 -0
- package/lib/typescript/src/theme/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/runtime.d.ts +7 -0
- package/lib/typescript/src/theme/runtime.d.ts.map +1 -0
- package/lib/typescript/src/theme/theme.d.ts +22 -0
- package/lib/typescript/src/theme/theme.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens/dark.d.ts +100 -0
- package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens/light.d.ts +106 -0
- package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens/primitives.d.ts +212 -0
- package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -0
- package/lib/typescript/src/theme/types.d.ts +273 -0
- package/lib/typescript/src/theme/types.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +4 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/accent-utils.d.ts +4 -0
- package/lib/typescript/src/utils/accent-utils.d.ts.map +1 -0
- package/lib/typescript/src/utils/color-utils.d.ts +11 -0
- package/lib/typescript/src/utils/color-utils.d.ts.map +1 -0
- package/lib/typescript/src/utils/deep-merge.d.ts +11 -0
- package/lib/typescript/src/utils/deep-merge.d.ts.map +1 -0
- package/lib/typescript/src/utils/resolve-theme-color.d.ts +12 -0
- package/lib/typescript/src/utils/resolve-theme-color.d.ts.map +1 -0
- package/lib/typescript/src/utils/typography.d.ts +6 -0
- package/lib/typescript/src/utils/typography.d.ts.map +1 -0
- package/lib/typescript/src/utils/unflatten.d.ts +12 -0
- package/lib/typescript/src/utils/unflatten.d.ts.map +1 -0
- package/package.json +137 -0
- package/src/components/avatar/Avatar.tsx +146 -0
- package/src/components/avatar/index.ts +8 -0
- package/src/components/avatar/types.ts +35 -0
- package/src/components/box/Box.tsx +59 -0
- package/src/components/box/index.ts +8 -0
- package/src/components/box/utils.ts +181 -0
- package/src/components/button/Button.tsx +313 -0
- package/src/components/button/index.ts +8 -0
- package/src/components/header/Header.ts +22 -0
- package/src/components/header/header-buttons.tsx +103 -0
- package/src/components/header/index.ts +11 -0
- package/src/components/header/variants/LargeTitleHeader/index.tsx +390 -0
- package/src/components/header/variants/TitleHeader/index.tsx +194 -0
- package/src/components/icon/Icon.tsx +74 -0
- package/src/components/icon/index.ts +2 -0
- package/src/components/icon/utils.tsx +147 -0
- package/src/components/index.ts +13 -0
- package/src/components/page-indicator/PageIndicator.tsx +100 -0
- package/src/components/page-indicator/PageIndicatorRoot.tsx +41 -0
- package/src/components/page-indicator/hooks/use-page-indicator-colors.ts +65 -0
- package/src/components/page-indicator/hooks/use-page-indicator-core.ts +34 -0
- package/src/components/page-indicator/hooks/use-page-indicator-progress.ts +122 -0
- package/src/components/page-indicator/index.ts +13 -0
- package/src/components/page-indicator/utils.ts +208 -0
- package/src/components/page-indicator/variants/DotIndicator/InactiveDot.tsx +70 -0
- package/src/components/page-indicator/variants/DotIndicator/LoadingOrbit.tsx +201 -0
- package/src/components/page-indicator/variants/DotIndicator/TravelingPill.tsx +80 -0
- package/src/components/page-indicator/variants/DotIndicator/index.tsx +145 -0
- package/src/components/page-indicator/variants/DotIndicator/utils.ts +28 -0
- package/src/components/page-indicator/variants/PillIndicator/PillIndicatorDot.tsx +107 -0
- package/src/components/page-indicator/variants/PillIndicator/index.tsx +149 -0
- package/src/components/page-indicator/variants/PillIndicator/utils.ts +63 -0
- package/src/components/portal/PortalContext.tsx +27 -0
- package/src/components/portal/PortalHost.tsx +20 -0
- package/src/components/portal/PortalProvider.tsx +44 -0
- package/src/components/portal/index.ts +5 -0
- package/src/components/screen/Screen.tsx +163 -0
- package/src/components/screen/ScreenContext.tsx +144 -0
- package/src/components/screen/ScreenFlatList.tsx +121 -0
- package/src/components/screen/ScreenScrollView.tsx +130 -0
- package/src/components/screen/index.ts +8 -0
- package/src/components/screen/utils.ts +17 -0
- package/src/components/slot/Slot.tsx +56 -0
- package/src/components/slot/index.ts +8 -0
- package/src/components/slot/utils.ts +61 -0
- package/src/components/spinner/Spinner.tsx +153 -0
- package/src/components/spinner/index.ts +2 -0
- package/src/components/surface/Surface.tsx +100 -0
- package/src/components/surface/index.ts +2 -0
- package/src/components/text/Text.tsx +115 -0
- package/src/components/text/index.ts +2 -0
- package/src/components/toast/Toast.tsx +477 -0
- package/src/components/toast/ToastProvider.tsx +403 -0
- package/src/components/toast/index.ts +26 -0
- package/src/components/toast/parts/ToastAnimatedItem.tsx +184 -0
- package/src/components/toast/types.ts +75 -0
- package/src/components/toast/utils/controller.ts +23 -0
- package/src/context/ThemeProvider.tsx +97 -0
- package/src/context/index.ts +2 -0
- package/src/index.tsx +6 -0
- package/src/theme/configure.ts +215 -0
- package/src/theme/index.ts +72 -0
- package/src/theme/runtime.ts +27 -0
- package/src/theme/theme.ts +152 -0
- package/src/theme/tokens/dark.ts +117 -0
- package/src/theme/tokens/light.ts +123 -0
- package/src/theme/tokens/primitives.ts +232 -0
- package/src/theme/types.ts +384 -0
- package/src/types.ts +1 -0
- package/src/utils/accent-utils.ts +58 -0
- package/src/utils/color-utils.ts +51 -0
- package/src/utils/deep-merge.ts +36 -0
- package/src/utils/resolve-theme-color.ts +27 -0
- package/src/utils/typography.ts +15 -0
- package/src/utils/unflatten.ts +36 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
type ReactNode,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
9
|
+
|
|
10
|
+
import Animated, {
|
|
11
|
+
type AnimatedRef,
|
|
12
|
+
type ScrollHandlerProcessed,
|
|
13
|
+
type SharedValue,
|
|
14
|
+
useAnimatedRef,
|
|
15
|
+
useAnimatedScrollHandler,
|
|
16
|
+
useSharedValue,
|
|
17
|
+
} from 'react-native-reanimated';
|
|
18
|
+
|
|
19
|
+
export type ScreenScrollRef = AnimatedRef<Animated.ScrollView>;
|
|
20
|
+
|
|
21
|
+
export interface ScreenContextValue {
|
|
22
|
+
/**
|
|
23
|
+
* Whether scroll containers apply `layout.screen.margin` padding.
|
|
24
|
+
* Overridable per `Screen.ScrollView` / `Screen.FlatList`.
|
|
25
|
+
*/
|
|
26
|
+
margins: boolean;
|
|
27
|
+
/** Whether a `Screen.Header` is present. Used by scroll containers to add top inset. */
|
|
28
|
+
hasHeader: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Live vertical scroll offset of the screen's scroll container, driven by
|
|
31
|
+
* `Screen.ScrollView` / `Screen.FlatList`. Headers (and consumer UI) read it
|
|
32
|
+
* to animate on scroll.
|
|
33
|
+
*/
|
|
34
|
+
scrollY: SharedValue<number>;
|
|
35
|
+
/**
|
|
36
|
+
* Scroll distance (px) over which the active header collapses — measured by
|
|
37
|
+
* the header (e.g. large-title portal height, parallax hero height). `0`
|
|
38
|
+
* disables snap and collapse-range animations (`Header.Title` screens).
|
|
39
|
+
*/
|
|
40
|
+
headerCollapseHeight: SharedValue<number>;
|
|
41
|
+
/**
|
|
42
|
+
* Native `snapToOffsets` for the scroll container. `[0, height]` when a
|
|
43
|
+
* collapsible header is active; `undefined` otherwise.
|
|
44
|
+
*/
|
|
45
|
+
headerSnapOffsets: number[] | undefined;
|
|
46
|
+
/** Updates collapse height for animations and native snap offsets. */
|
|
47
|
+
setHeaderCollapseHeight: (height: number) => void;
|
|
48
|
+
/** Shared scroll container ref — bound by `Screen.ScrollView` / `Screen.FlatList`. */
|
|
49
|
+
scrollRef: ScreenScrollRef;
|
|
50
|
+
/** Scroll handler that mirrors offset into `scrollY`. */
|
|
51
|
+
scrollHandler: ScrollHandlerProcessed<Record<string, unknown>>;
|
|
52
|
+
/** Whether the screen is hosted inside a tab navigator (affects edges). */
|
|
53
|
+
isInTabView: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const ScreenContext = createContext<ScreenContextValue | null>(null);
|
|
57
|
+
|
|
58
|
+
export interface ScreenScrollProviderProps {
|
|
59
|
+
children: ReactNode;
|
|
60
|
+
isInTabView: boolean;
|
|
61
|
+
/** Apply `layout.screen.margin` to scroll containers. Default true. */
|
|
62
|
+
margins?: boolean;
|
|
63
|
+
/** Whether a `Screen.Header` child is present. Default false. */
|
|
64
|
+
hasHeader?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Owns the screen's shared scroll ref, offset, snap offsets, and header collapse
|
|
69
|
+
* metrics. Header variants call {@link ScreenContextValue.setHeaderCollapseHeight};
|
|
70
|
+
* scroll containers bind the ref via {@link useScreen}.
|
|
71
|
+
*/
|
|
72
|
+
export function ScreenScrollProvider({
|
|
73
|
+
children,
|
|
74
|
+
isInTabView,
|
|
75
|
+
margins = true,
|
|
76
|
+
hasHeader = false,
|
|
77
|
+
}: ScreenScrollProviderProps) {
|
|
78
|
+
const scrollY = useSharedValue(0);
|
|
79
|
+
const headerCollapseHeight = useSharedValue(0);
|
|
80
|
+
const [headerSnapOffsets, setHeaderSnapOffsets] = useState<number[]>();
|
|
81
|
+
const scrollRef = useAnimatedRef<Animated.ScrollView>();
|
|
82
|
+
|
|
83
|
+
const setHeaderCollapseHeight = useCallback(
|
|
84
|
+
(height: number) => {
|
|
85
|
+
const rounded = Math.round(height);
|
|
86
|
+
headerCollapseHeight.value = rounded;
|
|
87
|
+
setHeaderSnapOffsets(rounded > 0 ? [0, rounded] : undefined);
|
|
88
|
+
},
|
|
89
|
+
[headerCollapseHeight]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const scrollHandler = useAnimatedScrollHandler({
|
|
93
|
+
onScroll: (event) => {
|
|
94
|
+
scrollY.value = event.contentOffset.y;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const value = useMemo<ScreenContextValue>(
|
|
99
|
+
() => ({
|
|
100
|
+
margins,
|
|
101
|
+
hasHeader,
|
|
102
|
+
scrollY,
|
|
103
|
+
headerCollapseHeight,
|
|
104
|
+
headerSnapOffsets,
|
|
105
|
+
setHeaderCollapseHeight,
|
|
106
|
+
scrollRef,
|
|
107
|
+
scrollHandler,
|
|
108
|
+
isInTabView,
|
|
109
|
+
}),
|
|
110
|
+
[
|
|
111
|
+
margins,
|
|
112
|
+
hasHeader,
|
|
113
|
+
scrollY,
|
|
114
|
+
headerCollapseHeight,
|
|
115
|
+
headerSnapOffsets,
|
|
116
|
+
setHeaderCollapseHeight,
|
|
117
|
+
scrollRef,
|
|
118
|
+
scrollHandler,
|
|
119
|
+
isInTabView,
|
|
120
|
+
]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<ScreenContext.Provider value={value}>{children}</ScreenContext.Provider>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Access the enclosing `Screen`'s shared scroll state, ref, and snap offsets.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const { scrollY, scrollRef, headerSnapOffsets } = useScreen();
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @throws if called outside a `Screen`.
|
|
137
|
+
*/
|
|
138
|
+
export function useScreen(): ScreenContextValue {
|
|
139
|
+
const context = useContext(ScreenContext);
|
|
140
|
+
if (!context) {
|
|
141
|
+
throw new Error('useScreen must be used within a Screen component');
|
|
142
|
+
}
|
|
143
|
+
return context;
|
|
144
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
type FunctionComponent,
|
|
4
|
+
isValidElement,
|
|
5
|
+
type ReactElement,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import {
|
|
8
|
+
type FlatListProps,
|
|
9
|
+
type NativeScrollEvent,
|
|
10
|
+
type NativeSyntheticEvent,
|
|
11
|
+
View,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
|
|
14
|
+
import Animated, {
|
|
15
|
+
type AnimatedRef,
|
|
16
|
+
useAnimatedScrollHandler,
|
|
17
|
+
useComposedEventHandler,
|
|
18
|
+
} from 'react-native-reanimated';
|
|
19
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
20
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
21
|
+
|
|
22
|
+
import { PortalHost } from '../portal';
|
|
23
|
+
import { useScreen } from './ScreenContext';
|
|
24
|
+
import { resolveScreenContentInsets } from './utils';
|
|
25
|
+
|
|
26
|
+
export interface ScreenFlatListProps<T>
|
|
27
|
+
extends Omit<FlatListProps<T>, 'CellRendererComponent'> {
|
|
28
|
+
/**
|
|
29
|
+
* Apply `layout.screen.margin` padding. Defaults to the enclosing `Screen`'s
|
|
30
|
+
* `margins` value.
|
|
31
|
+
*/
|
|
32
|
+
margins?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Extra bottom padding on the list content — clearance for a tab bar or home
|
|
35
|
+
* indicator. Defaults to the `layout.screen.margin.y` token when margins are
|
|
36
|
+
* enabled, otherwise `0`.
|
|
37
|
+
*/
|
|
38
|
+
bottomInset?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function renderListHeader(
|
|
42
|
+
ListHeaderComponent: FlatListProps<unknown>['ListHeaderComponent']
|
|
43
|
+
): ReactElement | null {
|
|
44
|
+
if (!ListHeaderComponent) return null;
|
|
45
|
+
if (isValidElement(ListHeaderComponent)) return ListHeaderComponent;
|
|
46
|
+
return createElement(ListHeaderComponent as FunctionComponent);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* `FlatList` variant of `Screen.ScrollView`: binds the screen's shared scroll
|
|
51
|
+
* ref, mirrors offset into `scrollY`, hosts the portal target above the list,
|
|
52
|
+
* and applies token-driven content padding.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* <Screen.FlatList data={items} renderItem={({ item }) => <Row item={item} />} />
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function ScreenFlatList<T>({
|
|
60
|
+
style,
|
|
61
|
+
contentContainerStyle,
|
|
62
|
+
ListHeaderComponent,
|
|
63
|
+
margins,
|
|
64
|
+
bottomInset,
|
|
65
|
+
onScroll,
|
|
66
|
+
...rest
|
|
67
|
+
}: ScreenFlatListProps<T>) {
|
|
68
|
+
const { theme } = useUnistyles();
|
|
69
|
+
const { top } = useSafeAreaInsets();
|
|
70
|
+
const {
|
|
71
|
+
scrollRef,
|
|
72
|
+
scrollHandler,
|
|
73
|
+
headerSnapOffsets,
|
|
74
|
+
margins: screenMargins,
|
|
75
|
+
hasHeader,
|
|
76
|
+
} = useScreen();
|
|
77
|
+
|
|
78
|
+
const margin = theme.layout.screen.margin;
|
|
79
|
+
const marginsEnabled = margins ?? screenMargins;
|
|
80
|
+
const compactHeaderHeight = hasHeader ? top + theme.layout.header.height : 0;
|
|
81
|
+
const { contentPadding } = resolveScreenContentInsets(
|
|
82
|
+
margin,
|
|
83
|
+
marginsEnabled,
|
|
84
|
+
bottomInset,
|
|
85
|
+
compactHeaderHeight
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const jsScrollHandler = useAnimatedScrollHandler(
|
|
89
|
+
(event) => {
|
|
90
|
+
if (onScroll)
|
|
91
|
+
onScroll(event as unknown as NativeSyntheticEvent<NativeScrollEvent>);
|
|
92
|
+
},
|
|
93
|
+
[onScroll]
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const composedHandler = useComposedEventHandler([
|
|
97
|
+
scrollHandler,
|
|
98
|
+
jsScrollHandler,
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
const headerComponent = (
|
|
102
|
+
<View>
|
|
103
|
+
<PortalHost />
|
|
104
|
+
{renderListHeader(ListHeaderComponent)}
|
|
105
|
+
</View>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<Animated.FlatList
|
|
110
|
+
ref={scrollRef as unknown as AnimatedRef<Animated.FlatList<T>>}
|
|
111
|
+
style={style}
|
|
112
|
+
contentContainerStyle={[contentPadding, contentContainerStyle]}
|
|
113
|
+
ListHeaderComponent={headerComponent}
|
|
114
|
+
showsVerticalScrollIndicator={false}
|
|
115
|
+
scrollEventThrottle={16}
|
|
116
|
+
snapToOffsets={headerSnapOffsets}
|
|
117
|
+
{...rest}
|
|
118
|
+
onScroll={composedHandler}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
type NativeScrollEvent,
|
|
4
|
+
type NativeSyntheticEvent,
|
|
5
|
+
type ScrollViewProps,
|
|
6
|
+
View,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
|
|
9
|
+
import Animated, {
|
|
10
|
+
type ScrollHandlerProcessed,
|
|
11
|
+
useAnimatedScrollHandler,
|
|
12
|
+
useComposedEventHandler,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
15
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
16
|
+
|
|
17
|
+
import { PortalHost } from '../portal';
|
|
18
|
+
import { useScreen } from './ScreenContext';
|
|
19
|
+
import { resolveScreenContentInsets } from './utils';
|
|
20
|
+
|
|
21
|
+
export interface ScreenScrollViewProps extends ScrollViewProps {
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Apply `layout.screen.margin` padding. Defaults to the enclosing `Screen`'s
|
|
25
|
+
* `margins` value.
|
|
26
|
+
*/
|
|
27
|
+
hasMargins?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Extra bottom padding on the scroll content — clearance for a tab bar or
|
|
30
|
+
* home indicator. Defaults to the `layout.screen.margin.y` token when margins
|
|
31
|
+
* are enabled, otherwise `0`.
|
|
32
|
+
*/
|
|
33
|
+
bottomInset?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Optional Reanimated scroll handler composed with the screen's own scroll
|
|
36
|
+
* tracking and any JS `onScroll` callback.
|
|
37
|
+
*/
|
|
38
|
+
scrollHandler?: ScrollHandlerProcessed<Record<string, unknown>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Scrollable content container for a `Screen`. Binds the screen's shared scroll
|
|
43
|
+
* ref, mirrors offset into `scrollY`, hosts the portal target at the top (where
|
|
44
|
+
* header content is injected), and applies token-driven content padding.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* <Screen.ScrollView>
|
|
49
|
+
* <Text>Scrollable content</Text>
|
|
50
|
+
* </Screen.ScrollView>
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function ScreenScrollView({
|
|
54
|
+
children,
|
|
55
|
+
style,
|
|
56
|
+
contentContainerStyle,
|
|
57
|
+
hasMargins,
|
|
58
|
+
bottomInset,
|
|
59
|
+
scrollHandler: externalScrollHandler,
|
|
60
|
+
onScroll: onScrollProp,
|
|
61
|
+
horizontal = false,
|
|
62
|
+
...rest
|
|
63
|
+
}: ScreenScrollViewProps) {
|
|
64
|
+
const { theme } = useUnistyles();
|
|
65
|
+
const { top } = useSafeAreaInsets();
|
|
66
|
+
const {
|
|
67
|
+
scrollRef,
|
|
68
|
+
scrollHandler: screenScrollHandler,
|
|
69
|
+
headerSnapOffsets,
|
|
70
|
+
margins: screenMargins,
|
|
71
|
+
hasHeader,
|
|
72
|
+
} = useScreen();
|
|
73
|
+
|
|
74
|
+
const jsScrollHandler = useAnimatedScrollHandler(
|
|
75
|
+
onScrollProp
|
|
76
|
+
? {
|
|
77
|
+
onScroll: (event) => {
|
|
78
|
+
onScrollProp({
|
|
79
|
+
nativeEvent: event,
|
|
80
|
+
} as unknown as NativeSyntheticEvent<NativeScrollEvent>);
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
: {}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const handlers: ScrollHandlerProcessed<Record<string, unknown>>[] = [
|
|
87
|
+
screenScrollHandler,
|
|
88
|
+
...(externalScrollHandler ? [externalScrollHandler] : []),
|
|
89
|
+
...(onScrollProp ? [jsScrollHandler] : []),
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
const composedHandler = useComposedEventHandler(handlers);
|
|
93
|
+
|
|
94
|
+
const margin = theme.layout.screen.margin;
|
|
95
|
+
const marginsEnabled = hasMargins ?? screenMargins;
|
|
96
|
+
const compactHeaderHeight = hasHeader ? top + theme.layout.header.height : 0;
|
|
97
|
+
const { contentPadding } = resolveScreenContentInsets(
|
|
98
|
+
margin,
|
|
99
|
+
marginsEnabled,
|
|
100
|
+
bottomInset,
|
|
101
|
+
compactHeaderHeight
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<Animated.ScrollView
|
|
106
|
+
ref={scrollRef}
|
|
107
|
+
style={style}
|
|
108
|
+
horizontal={horizontal}
|
|
109
|
+
showsVerticalScrollIndicator={false}
|
|
110
|
+
scrollEventThrottle={16}
|
|
111
|
+
contentContainerStyle={contentPadding}
|
|
112
|
+
snapToOffsets={headerSnapOffsets}
|
|
113
|
+
{...rest}
|
|
114
|
+
onScroll={composedHandler}
|
|
115
|
+
>
|
|
116
|
+
<View>
|
|
117
|
+
<PortalHost />
|
|
118
|
+
</View>
|
|
119
|
+
<View
|
|
120
|
+
style={[
|
|
121
|
+
contentContainerStyle,
|
|
122
|
+
{ flexDirection: horizontal ? 'row' : 'column' },
|
|
123
|
+
]}
|
|
124
|
+
>
|
|
125
|
+
{/* content spacing */}
|
|
126
|
+
{children}
|
|
127
|
+
</View>
|
|
128
|
+
</Animated.ScrollView>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { ScreenHeaderProps, ScreenProps } from './Screen';
|
|
2
|
+
export { Screen } from './Screen';
|
|
3
|
+
export type { ScreenContextValue } from './ScreenContext';
|
|
4
|
+
export { useScreen } from './ScreenContext';
|
|
5
|
+
export type { ScreenFlatListProps } from './ScreenFlatList';
|
|
6
|
+
export { ScreenFlatList } from './ScreenFlatList';
|
|
7
|
+
export type { ScreenScrollViewProps } from './ScreenScrollView';
|
|
8
|
+
export { ScreenScrollView } from './ScreenScrollView';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LayoutTokens } from '#theme/types';
|
|
2
|
+
|
|
3
|
+
export function resolveScreenContentInsets(
|
|
4
|
+
margin: LayoutTokens['screen']['margin'],
|
|
5
|
+
margins: boolean,
|
|
6
|
+
bottomInset?: number,
|
|
7
|
+
topInset?: number
|
|
8
|
+
) {
|
|
9
|
+
return {
|
|
10
|
+
contentPadding: {
|
|
11
|
+
flexGrow: 1 as const,
|
|
12
|
+
paddingTop: topInset ?? 0,
|
|
13
|
+
paddingHorizontal: margins ? margin.x : 0,
|
|
14
|
+
paddingBottom: bottomInset ?? (margins ? margin.y : 0),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cloneElement,
|
|
3
|
+
isValidElement,
|
|
4
|
+
type ReactElement,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
type Ref,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
import { composeRefs, mergeProps } from './utils';
|
|
10
|
+
|
|
11
|
+
type AnyProps = Record<string, unknown>;
|
|
12
|
+
|
|
13
|
+
export interface SlotProps {
|
|
14
|
+
/** Props (incl. pre-merged `style` and optional `ref`) to merge onto the child. */
|
|
15
|
+
slotProps: AnyProps;
|
|
16
|
+
/** Name used in dev warnings. */
|
|
17
|
+
ownerName?: string;
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Radix-style Slot: merges `slotProps` onto a single child element instead of
|
|
23
|
+
* rendering a wrapper. Composes `on*` handlers (child first), `style`
|
|
24
|
+
* (`[slotStyle, childStyle]`), and refs.
|
|
25
|
+
*/
|
|
26
|
+
export function Slot({
|
|
27
|
+
slotProps,
|
|
28
|
+
ownerName = 'Component',
|
|
29
|
+
children,
|
|
30
|
+
}: SlotProps): ReactElement | null {
|
|
31
|
+
if (!isValidElement(children)) {
|
|
32
|
+
if (__DEV__ && children != null) {
|
|
33
|
+
console.error(
|
|
34
|
+
`[${ownerName}] \`asChild\` expects exactly one React element child.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const child = children as ReactElement;
|
|
41
|
+
const childProps = (child.props ?? {}) as AnyProps;
|
|
42
|
+
|
|
43
|
+
const { ref: slotRef, ...restSlotProps } = slotProps;
|
|
44
|
+
const { ref: childRef, ...restChildProps } = childProps;
|
|
45
|
+
|
|
46
|
+
const merged = mergeProps(restSlotProps, restChildProps);
|
|
47
|
+
const composedRef = composeRefs(
|
|
48
|
+
slotRef as Ref<unknown> | undefined,
|
|
49
|
+
childRef as Ref<unknown> | undefined
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return cloneElement(child, {
|
|
53
|
+
...merged,
|
|
54
|
+
ref: composedRef,
|
|
55
|
+
} as Partial<unknown> & AnyProps);
|
|
56
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level composition helpers for advanced use cases (custom primitives,
|
|
3
|
+
* `asChild` patterns). Most apps should use component APIs directly.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type { SlotProps } from './Slot';
|
|
7
|
+
export { Slot } from './Slot';
|
|
8
|
+
export { composeRefs, mergeProps } from './utils';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { MutableRefObject, Ref } from 'react';
|
|
2
|
+
|
|
3
|
+
function assignRef<T>(ref: Ref<T> | undefined, value: T | null): void {
|
|
4
|
+
if (typeof ref === 'function') {
|
|
5
|
+
ref(value);
|
|
6
|
+
} else if (ref != null) {
|
|
7
|
+
(ref as MutableRefObject<T | null>).current = value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Composes multiple refs so each receives the node. */
|
|
12
|
+
export function composeRefs<T>(...refs: (Ref<T> | undefined)[]): Ref<T> {
|
|
13
|
+
return (node: T | null) => {
|
|
14
|
+
for (const ref of refs) {
|
|
15
|
+
assignRef(ref, node);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type AnyProps = Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
const isEventHandler = (key: string): boolean => /^on[A-Z]/.test(key);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Merges slot props with the child's own props (child wins), following the
|
|
26
|
+
* Radix Slot rules:
|
|
27
|
+
* - `on*` handlers are composed (child runs first, then slot),
|
|
28
|
+
* - `style` is merged as `[slotStyle, childStyle]`,
|
|
29
|
+
* - all other child props override slot props.
|
|
30
|
+
*
|
|
31
|
+
* `ref`/`style` are intentionally not handled here — the caller composes refs
|
|
32
|
+
* and passes a pre-merged `style` via `slotProps`.
|
|
33
|
+
*/
|
|
34
|
+
export function mergeProps(
|
|
35
|
+
slotProps: AnyProps,
|
|
36
|
+
childProps: AnyProps
|
|
37
|
+
): AnyProps {
|
|
38
|
+
const merged: AnyProps = { ...slotProps };
|
|
39
|
+
|
|
40
|
+
for (const key of Object.keys(childProps)) {
|
|
41
|
+
const slotValue = slotProps[key];
|
|
42
|
+
const childValue = childProps[key];
|
|
43
|
+
|
|
44
|
+
if (isEventHandler(key)) {
|
|
45
|
+
if (typeof slotValue === 'function' && typeof childValue === 'function') {
|
|
46
|
+
merged[key] = (...args: unknown[]) => {
|
|
47
|
+
(childValue as (...a: unknown[]) => void)(...args);
|
|
48
|
+
(slotValue as (...a: unknown[]) => void)(...args);
|
|
49
|
+
};
|
|
50
|
+
} else {
|
|
51
|
+
merged[key] = childValue ?? slotValue;
|
|
52
|
+
}
|
|
53
|
+
} else if (key === 'style') {
|
|
54
|
+
merged.style = [slotValue, childValue].filter(Boolean);
|
|
55
|
+
} else {
|
|
56
|
+
merged[key] = childValue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return merged;
|
|
61
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { memo, useEffect, useId } from 'react';
|
|
2
|
+
|
|
3
|
+
import Animated, {
|
|
4
|
+
cancelAnimation,
|
|
5
|
+
Easing,
|
|
6
|
+
useAnimatedStyle,
|
|
7
|
+
useSharedValue,
|
|
8
|
+
withRepeat,
|
|
9
|
+
withTiming,
|
|
10
|
+
} from 'react-native-reanimated';
|
|
11
|
+
import Svg, { Defs, G, LinearGradient, Path, Stop } from 'react-native-svg';
|
|
12
|
+
|
|
13
|
+
import { useUnistyles } from 'react-native-unistyles';
|
|
14
|
+
|
|
15
|
+
import type { ColorToken } from '#theme/types';
|
|
16
|
+
import { resolveThemeColor } from '#utils/resolve-theme-color';
|
|
17
|
+
|
|
18
|
+
export type SpinnerSize = 'sm' | 'md' | 'lg';
|
|
19
|
+
|
|
20
|
+
export interface SpinnerProps {
|
|
21
|
+
/** Spinner diameter preset. Defaults to `'md'`. */
|
|
22
|
+
size?: SpinnerSize;
|
|
23
|
+
/** Theme color token or raw CSS color. Defaults to the accent color. */
|
|
24
|
+
color?: ColorToken;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const DIMENSION: Record<SpinnerSize, number> = { sm: 16, md: 24, lg: 32 };
|
|
28
|
+
|
|
29
|
+
const ROTATION_DURATION = 900;
|
|
30
|
+
|
|
31
|
+
// HeroUI spinner arcs — two gradient-filled segments in a 24×24 viewBox.
|
|
32
|
+
const LEFT_ARC =
|
|
33
|
+
'M8.749.021a1.5 1.5 0 0 1 .497 2.958A7.5 7.5 0 0 0 3 10.375a7.5 7.5 0 0 0 7.5 7.5v3c-5.799 0-10.5-4.7-10.5-10.5C0 5.23 3.726.865 8.749.021';
|
|
34
|
+
const RIGHT_ARC =
|
|
35
|
+
'M15.392 2.673a1.5 1.5 0 0 1 2.119-.115A10.48 10.48 0 0 1 21 10.375c0 5.8-4.701 10.5-10.5 10.5v-3a7.5 7.5 0 0 0 5.007-13.084a1.5 1.5 0 0 1-.115-2.118';
|
|
36
|
+
const ARC_TRANSFORM = 'translate(1.5 1.625)';
|
|
37
|
+
|
|
38
|
+
interface SpinnerIconProps {
|
|
39
|
+
size: number;
|
|
40
|
+
color: string;
|
|
41
|
+
gradientId1: string;
|
|
42
|
+
gradientId2: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SpinnerIcon = memo(function SpinnerIcon({
|
|
46
|
+
size,
|
|
47
|
+
color,
|
|
48
|
+
gradientId1,
|
|
49
|
+
gradientId2,
|
|
50
|
+
}: SpinnerIconProps) {
|
|
51
|
+
return (
|
|
52
|
+
<Svg width={size} height={size} viewBox="0 0 24 24">
|
|
53
|
+
<Defs>
|
|
54
|
+
<LinearGradient
|
|
55
|
+
id={gradientId1}
|
|
56
|
+
x1="50%"
|
|
57
|
+
x2="50%"
|
|
58
|
+
y1="5.271%"
|
|
59
|
+
y2="91.793%"
|
|
60
|
+
>
|
|
61
|
+
<Stop offset="0%" stopColor={color} />
|
|
62
|
+
<Stop offset="100%" stopColor={color} stopOpacity={0.55} />
|
|
63
|
+
</LinearGradient>
|
|
64
|
+
<LinearGradient
|
|
65
|
+
id={gradientId2}
|
|
66
|
+
x1="50%"
|
|
67
|
+
x2="50%"
|
|
68
|
+
y1="15.24%"
|
|
69
|
+
y2="87.15%"
|
|
70
|
+
>
|
|
71
|
+
<Stop offset="0%" stopColor={color} stopOpacity={0} />
|
|
72
|
+
<Stop offset="100%" stopColor={color} stopOpacity={0.55} />
|
|
73
|
+
</LinearGradient>
|
|
74
|
+
</Defs>
|
|
75
|
+
<G fill="none">
|
|
76
|
+
<Path
|
|
77
|
+
d={LEFT_ARC}
|
|
78
|
+
fill={`url(#${gradientId1})`}
|
|
79
|
+
transform={ARC_TRANSFORM}
|
|
80
|
+
/>
|
|
81
|
+
<Path
|
|
82
|
+
d={RIGHT_ARC}
|
|
83
|
+
fill={`url(#${gradientId2})`}
|
|
84
|
+
transform={ARC_TRANSFORM}
|
|
85
|
+
/>
|
|
86
|
+
</G>
|
|
87
|
+
</Svg>
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Animated loading spinner. Used by `Button` when `isLoading` is set.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```tsx
|
|
96
|
+
* <Spinner size="md" color="content.accent" />
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export function Spinner({ size = 'md', color = 'text.accent' }: SpinnerProps) {
|
|
100
|
+
const { theme } = useUnistyles();
|
|
101
|
+
const id = useId();
|
|
102
|
+
const gradientId1 = `spinner-def-1-${id}`;
|
|
103
|
+
const gradientId2 = `spinner-def-2-${id}`;
|
|
104
|
+
const resolvedColor =
|
|
105
|
+
resolveThemeColor(color, theme) ??
|
|
106
|
+
(theme.color as unknown as Record<string, string | undefined>)[
|
|
107
|
+
'text.accent'
|
|
108
|
+
] ??
|
|
109
|
+
'';
|
|
110
|
+
|
|
111
|
+
const dim = DIMENSION[size];
|
|
112
|
+
const rotation = useSharedValue(0);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
// Fast refresh can remount without running cleanup — cancel any stale loop first.
|
|
116
|
+
cancelAnimation(rotation);
|
|
117
|
+
rotation.value = 0;
|
|
118
|
+
rotation.value = withRepeat(
|
|
119
|
+
withTiming(360, {
|
|
120
|
+
duration: ROTATION_DURATION,
|
|
121
|
+
easing: Easing.linear,
|
|
122
|
+
}),
|
|
123
|
+
-1,
|
|
124
|
+
false
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return () => {
|
|
128
|
+
cancelAnimation(rotation);
|
|
129
|
+
rotation.value = 0;
|
|
130
|
+
};
|
|
131
|
+
}, [rotation]);
|
|
132
|
+
|
|
133
|
+
const animStyle = useAnimatedStyle(() => ({
|
|
134
|
+
transform: [{ rotate: `${rotation.value}deg` }],
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Animated.View
|
|
139
|
+
accessible
|
|
140
|
+
accessibilityRole="progressbar"
|
|
141
|
+
accessibilityLabel="Loading"
|
|
142
|
+
collapsable={false}
|
|
143
|
+
style={[{ width: dim, height: dim }, animStyle]}
|
|
144
|
+
>
|
|
145
|
+
<SpinnerIcon
|
|
146
|
+
size={dim}
|
|
147
|
+
color={resolvedColor}
|
|
148
|
+
gradientId1={gradientId1}
|
|
149
|
+
gradientId2={gradientId2}
|
|
150
|
+
/>
|
|
151
|
+
</Animated.View>
|
|
152
|
+
);
|
|
153
|
+
}
|