@rific/auto-paper 0.12.4 → 0.13.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 CHANGED
@@ -34,7 +34,7 @@ npm install expo-blur # frosted-glass BlurView
34
34
  npm install expo-navigation-bar # (>= 56.0.0, SDK 54+) auto-syncs Android nav bar icon style to the theme when BottomNavigation is mounted
35
35
  ```
36
36
 
37
- These integrations are opt-in: pass them to `Provider` (`expoBlur`, `navigationBar`, and `reanimated` - see the prop table below), and the relevant wrappers pick them up automatically. Omit them and you still get working fallbacks: `BlurView` renders its solid (non-blur) look, `BottomNavigation` simply skips nav-bar syncing, and `Dialog` treats `animatedStyle` as a no-op unless a reanimated module is injected.
37
+ These integrations are opt-in: pass them to `Provider` (`expoBlur`, `maskedView`, `navigationBar`, and `reanimated` - see the prop table below), and the relevant wrappers pick them up automatically. Omit them and you still get working fallbacks: `BlurView` renders its solid (non-blur) look, `FadingScrollView` scrolls without its fade on iOS and Android, `BottomNavigation` simply skips nav-bar syncing, and `Dialog` treats `animatedStyle` as a no-op unless a reanimated module is injected.
38
38
 
39
39
  The exported Redux slice has no dependency on `@reduxjs/toolkit`: it works with RTK stores, vanilla Redux, or no Redux at all.
40
40
 
@@ -266,6 +266,7 @@ export default function App() {
266
266
  | `children` | `ReactNode` | |
267
267
  | `defaults` | `PaperDefaults` | Prop defaults for wrapper components (see below) |
268
268
  | `expoBlur` | `ExpoBlurModule` | Injects `expo-blur` (`import * as ExpoBlur from 'expo-blur'`) so `<BlurView>` renders the real frosted-glass effect. Omit to always render its solid fallback. On Android, expo-blur only blurs when given a `blurTarget` and a `blurMethod` (`'dimezisBlurView'`, or `'dimezisBlurViewSdk31Plus'` on API 31+); without both, `<BlurView>` renders the solid fallback there too, rather than expo-blur's unblurred translucent tint. |
269
+ | `maskedView` | `MaskedViewModule` | Injects `@react-native-masked-view/masked-view` (`import MaskedView from '@react-native-masked-view/masked-view'`) so `<FadingScrollView>` fades its content out above a pinned footer on iOS and Android. Omit to have it scroll without the fade there; web needs nothing. |
269
270
  | `fontFamily` | `string` | Applied to every Paper typography variant (`bodyLarge`, `headlineMedium`, `labelSmall`, ...) via `configureFonts`'s flat-config mode - each variant keeps its own MD3 fontSize/lineHeight/weight, only fontFamily changes. Omit to keep each platform's system font. |
270
271
  | `navigationBar` | `ExpoNavigationBarModule` | Injects `expo-navigation-bar` (`import * as ExpoNavigationBar from 'expo-navigation-bar'`) so the Android nav bar icon style auto-syncs with the theme while a `BottomNavigation` is mounted. Omit (and don't pass `onNavBarChange`) to skip nav bar syncing entirely. |
271
272
  | `onNavBarChange` | `(color: string, dark: boolean) => void` | Overrides the built-in nav bar sync: called on Android when the theme changes while a `BottomNavigation` is mounted, instead of the automatic `navigationBar`-driven icon-style sync |
@@ -514,6 +515,26 @@ function SettingsDialog({ visible }: { visible: boolean }) {
514
515
 
515
516
  `DialogProps` is exported from `@rific/auto-paper` and extends `react-native-paper`'s `DialogProps` with the `blur` and `animatedStyle` fields. Use `PaperDialogProps` if you need the base paper type.
516
517
 
518
+ ### `FadingScrollView`
519
+
520
+ A vertical `ScrollView` whose content fades to transparent over `fadeHeight` (default `SCROLL_FADE_HEIGHT`, 24) at each edge while there is more to scroll past it: at the bottom until the end is reached (the cue that rows continue below a pinned footer, such as a dialog's Done button), and at the top once scrolled down (never at rest, so a header at the top is never dimmed). It masks the content instead of painting over it, so whatever is behind it (a blurred `Dialog`/`BlurView` surface or a solid one, light or dark) stays one continuous surface. A second `BlurView` as a frosted footer leaves a seam instead: a blur nested inside a blurred card can't frost the card's own content.
521
+
522
+ ```tsx
523
+ import MaskedView from '@react-native-masked-view/masked-view'
524
+ import { Dialog, FadingScrollView, Provider } from '@rific/auto-paper'
525
+
526
+ <Provider maskedView={MaskedView}>
527
+ <Dialog visible onDismiss={close}>
528
+ <Dialog.ScrollArea>
529
+ <FadingScrollView>{rows}</FadingScrollView>
530
+ </Dialog.ScrollArea>
531
+ <Dialog.Actions>{done}</Dialog.Actions>
532
+ </Dialog>
533
+ </Provider>
534
+ ```
535
+
536
+ On iOS and Android it needs `maskedView` injected into `Provider` (and React Native 0.77+, for the native gradient it masks with); without it, it scrolls with no fade. On web it uses a CSS `mask-image` and needs nothing (react-native-masked-view's web build doesn't mask). `style` goes on the outer box, so `flexGrow`/`flexShrink`/`alignSelf` work as on a plain `ScrollView`; every other prop, `onScroll`/`onLayout`/`onContentSizeChange` included, reaches the `ScrollView`.
537
+
517
538
  ### `IconButton`
518
539
 
519
540
  A thin wrapper around `react-native-paper`'s `IconButton` that adds a `variant` prop for applying theme-derived container and icon colors.
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as react_native_paper_lib_typescript_utils_forwardRef from 'react-native-paper/lib/typescript/utils/forwardRef';
2
2
  import * as react_native from 'react-native';
3
- import { View, ViewProps, StyleProp, ViewStyle, TextInput as TextInput$1, StatusBarProps } from 'react-native';
3
+ import { View, ViewProps, StyleProp, ViewStyle, ScrollViewProps, TextInput as TextInput$1, StatusBarProps } from 'react-native';
4
4
  import * as react_native_paper from 'react-native-paper';
5
5
  import { AppbarHeaderProps, MD3Theme, BottomNavigationRoute, BottomNavigationProps, BottomNavigation as BottomNavigation$1, ButtonProps, ChipProps as ChipProps$1, DialogProps as DialogProps$1, DialogTitleProps, DialogContentProps, DialogActionsProps, DialogScrollAreaProps, FABProps, IconButtonProps as IconButtonProps$1, MenuProps as MenuProps$1, MenuItemProps, TextInputProps } from 'react-native-paper';
6
6
  export { AppbarHeaderProps, BottomNavigationProps, BottomNavigationRoute, ButtonProps, FABProps, ChipProps as PaperChipProps, DialogProps as PaperDialogProps, IconButtonProps as PaperIconButtonProps, MenuProps as PaperMenuProps, TextInputProps } from 'react-native-paper';
7
7
  import * as react from 'react';
8
- import react__default, { RefObject, ReactNode, ComponentType } from 'react';
8
+ import react__default, { RefObject, ReactNode, ComponentType, ReactElement } from 'react';
9
9
  import * as _rific_core from '@rific/core';
10
10
  import { SettingsContextValue } from '@rific/core';
11
11
 
@@ -181,6 +181,18 @@ declare const Dialog: typeof DialogComponent;
181
181
 
182
182
  declare function FAB(props: FABProps): react.JSX.Element;
183
183
 
184
+ type MaskedViewModule = ComponentType<{
185
+ children?: ReactNode;
186
+ maskElement: ReactElement;
187
+ style?: StyleProp<ViewStyle>;
188
+ }>;
189
+ declare const SCROLL_FADE_HEIGHT = 24;
190
+ type FadingScrollViewProps = Omit<ScrollViewProps, 'style'> & {
191
+ style?: StyleProp<ViewStyle>;
192
+ fadeHeight?: number;
193
+ };
194
+ declare function FadingScrollView({ style, fadeHeight, onScroll, onLayout, onContentSizeChange, scrollEventThrottle, children, ...rest }: FadingScrollViewProps): react.JSX.Element;
195
+
184
196
  type Props = {
185
197
  value: ColorHarmony;
186
198
  onChange: (harmony: ColorHarmony) => void;
@@ -292,6 +304,7 @@ type NavBarContextType = {
292
304
  declare const useNavBarContext: () => NavBarContextType;
293
305
  declare const useBlurModule: () => ExpoBlurModule | undefined;
294
306
  declare const useReanimatedModule: () => ReanimatedModule | undefined;
307
+ declare const useMaskedViewModule: () => MaskedViewModule | undefined;
295
308
  type ProviderProps = {
296
309
  children: ReactNode;
297
310
  defaults?: PaperDefaults;
@@ -304,6 +317,8 @@ type ProviderProps = {
304
317
  * to keep each platform's system font (MD3's own default). */
305
318
  fontFamily?: string;
306
319
  initialValue?: Partial<ThemeSettings>;
320
+ /** Injects @react-native-masked-view/masked-view so `<FadingScrollView>` can fade its content out above a pinned footer on iOS and Android. Pass `import MaskedView from '@react-native-masked-view/masked-view'`; omit to have it scroll without the fade there (web never needs it). */
321
+ maskedView?: MaskedViewModule;
307
322
  /** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
308
323
  navigationBar?: ExpoNavigationBarModule;
309
324
  onChange?: (settings: ThemeSettings) => void;
@@ -314,7 +329,7 @@ type ProviderProps = {
314
329
  statusBarProps?: StatusBarProps;
315
330
  style?: StyleProp<ViewStyle>;
316
331
  };
317
- declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
332
+ declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
318
333
 
319
334
  declare const useAutoPaperTheme: () => AutoPaperTheme;
320
335
 
@@ -393,4 +408,4 @@ declare const isDarkColor: (value: string) => boolean;
393
408
 
394
409
  declare const resolveSeedColor: (color: string | TriadicPalette) => string;
395
410
 
396
- export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
411
+ export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, FadingScrollView, type FadingScrollViewProps, HarmonyPicker, IconButton, type IconButtonProps, type MaskedViewModule, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SCROLL_FADE_HEIGHT, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useMaskedViewModule, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as react_native_paper_lib_typescript_utils_forwardRef from 'react-native-paper/lib/typescript/utils/forwardRef';
2
2
  import * as react_native from 'react-native';
3
- import { View, ViewProps, StyleProp, ViewStyle, TextInput as TextInput$1, StatusBarProps } from 'react-native';
3
+ import { View, ViewProps, StyleProp, ViewStyle, ScrollViewProps, TextInput as TextInput$1, StatusBarProps } from 'react-native';
4
4
  import * as react_native_paper from 'react-native-paper';
5
5
  import { AppbarHeaderProps, MD3Theme, BottomNavigationRoute, BottomNavigationProps, BottomNavigation as BottomNavigation$1, ButtonProps, ChipProps as ChipProps$1, DialogProps as DialogProps$1, DialogTitleProps, DialogContentProps, DialogActionsProps, DialogScrollAreaProps, FABProps, IconButtonProps as IconButtonProps$1, MenuProps as MenuProps$1, MenuItemProps, TextInputProps } from 'react-native-paper';
6
6
  export { AppbarHeaderProps, BottomNavigationProps, BottomNavigationRoute, ButtonProps, FABProps, ChipProps as PaperChipProps, DialogProps as PaperDialogProps, IconButtonProps as PaperIconButtonProps, MenuProps as PaperMenuProps, TextInputProps } from 'react-native-paper';
7
7
  import * as react from 'react';
8
- import react__default, { RefObject, ReactNode, ComponentType } from 'react';
8
+ import react__default, { RefObject, ReactNode, ComponentType, ReactElement } from 'react';
9
9
  import * as _rific_core from '@rific/core';
10
10
  import { SettingsContextValue } from '@rific/core';
11
11
 
@@ -181,6 +181,18 @@ declare const Dialog: typeof DialogComponent;
181
181
 
182
182
  declare function FAB(props: FABProps): react.JSX.Element;
183
183
 
184
+ type MaskedViewModule = ComponentType<{
185
+ children?: ReactNode;
186
+ maskElement: ReactElement;
187
+ style?: StyleProp<ViewStyle>;
188
+ }>;
189
+ declare const SCROLL_FADE_HEIGHT = 24;
190
+ type FadingScrollViewProps = Omit<ScrollViewProps, 'style'> & {
191
+ style?: StyleProp<ViewStyle>;
192
+ fadeHeight?: number;
193
+ };
194
+ declare function FadingScrollView({ style, fadeHeight, onScroll, onLayout, onContentSizeChange, scrollEventThrottle, children, ...rest }: FadingScrollViewProps): react.JSX.Element;
195
+
184
196
  type Props = {
185
197
  value: ColorHarmony;
186
198
  onChange: (harmony: ColorHarmony) => void;
@@ -292,6 +304,7 @@ type NavBarContextType = {
292
304
  declare const useNavBarContext: () => NavBarContextType;
293
305
  declare const useBlurModule: () => ExpoBlurModule | undefined;
294
306
  declare const useReanimatedModule: () => ReanimatedModule | undefined;
307
+ declare const useMaskedViewModule: () => MaskedViewModule | undefined;
295
308
  type ProviderProps = {
296
309
  children: ReactNode;
297
310
  defaults?: PaperDefaults;
@@ -304,6 +317,8 @@ type ProviderProps = {
304
317
  * to keep each platform's system font (MD3's own default). */
305
318
  fontFamily?: string;
306
319
  initialValue?: Partial<ThemeSettings>;
320
+ /** Injects @react-native-masked-view/masked-view so `<FadingScrollView>` can fade its content out above a pinned footer on iOS and Android. Pass `import MaskedView from '@react-native-masked-view/masked-view'`; omit to have it scroll without the fade there (web never needs it). */
321
+ maskedView?: MaskedViewModule;
307
322
  /** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
308
323
  navigationBar?: ExpoNavigationBarModule;
309
324
  onChange?: (settings: ThemeSettings) => void;
@@ -314,7 +329,7 @@ type ProviderProps = {
314
329
  statusBarProps?: StatusBarProps;
315
330
  style?: StyleProp<ViewStyle>;
316
331
  };
317
- declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
332
+ declare function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps): react.JSX.Element;
318
333
 
319
334
  declare const useAutoPaperTheme: () => AutoPaperTheme;
320
335
 
@@ -393,4 +408,4 @@ declare const isDarkColor: (value: string) => boolean;
393
408
 
394
409
  declare const resolveSeedColor: (color: string | TriadicPalette) => string;
395
410
 
396
- export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, HarmonyPicker, IconButton, type IconButtonProps, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
411
+ export { Appbar, type AppearanceIcons, AppearancePicker, AutoAppearancePicker, AutoPalettePicker, type AutoPaperTheme, BlurView, type BlurViewProps, BottomNavigation, Button, Chip, type ChipProps, type ColorHarmony, ColorPicker, type ColorRoles, Dialog, type DialogProps, type ExpoBlurModule, type ExpoNavigationBarModule, FAB, FadingScrollView, type FadingScrollViewProps, HarmonyPicker, IconButton, type IconButtonProps, type MaskedViewModule, Menu, type MenuProps, PalettePicker, type PaletteWeights, type PaperDefaults, Provider, type ProviderProps, type RGB, type ReanimatedModule, SCROLL_FADE_HEIGHT, SEMANTIC_BASE_COLORS, type SeedColor, type SemanticColorRoles, TextInput, type ThemeAppearance, type ThemeBridgeProps, type ThemeSettings, ThemeSettingsContext, type ThemeSettingsContextType, type ThemeState, type TriadicPalette, createThemeReducer, defaultColors, defaultThemeSettings, getBlendedColor, getColorRoles, getContrastColor, getHex, getRgb, getThirdColor, getTintTextColor, getTonalColor, getTriadicPalette, isDarkColor, resolveSeedColor, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer, useAutoPaperTheme, useBlur, useBlurModule, useComputedTheme, useMaskedViewModule, useNavBarContext, usePaperDefaults, useReanimatedModule, useThemeBridgeProps, useThemeSettings };
package/dist/index.js CHANGED
@@ -41,11 +41,13 @@ __export(index_exports, {
41
41
  ColorPicker: () => ColorPicker,
42
42
  Dialog: () => Dialog,
43
43
  FAB: () => FAB,
44
+ FadingScrollView: () => FadingScrollView,
44
45
  HarmonyPicker: () => HarmonyPicker,
45
46
  IconButton: () => IconButton,
46
47
  Menu: () => Menu,
47
48
  PalettePicker: () => PalettePicker,
48
49
  Provider: () => Provider,
50
+ SCROLL_FADE_HEIGHT: () => SCROLL_FADE_HEIGHT,
49
51
  SEMANTIC_BASE_COLORS: () => SEMANTIC_BASE_COLORS,
50
52
  TextInput: () => TextInput,
51
53
  ThemeSettingsContext: () => ThemeSettingsContext,
@@ -73,6 +75,7 @@ __export(index_exports, {
73
75
  useBlur: () => useBlur,
74
76
  useBlurModule: () => useBlurModule,
75
77
  useComputedTheme: () => useComputedTheme,
78
+ useMaskedViewModule: () => useMaskedViewModule,
76
79
  useNavBarContext: () => useNavBarContext,
77
80
  usePaperDefaults: () => usePaperDefaults,
78
81
  useReanimatedModule: () => useReanimatedModule,
@@ -684,11 +687,13 @@ var BlurModuleContext = (0, import_react5.createContext)(void 0);
684
687
  var useBlurModule = () => (0, import_react5.useContext)(BlurModuleContext);
685
688
  var ReanimatedModuleContext = (0, import_react5.createContext)(void 0);
686
689
  var useReanimatedModule = () => (0, import_react5.useContext)(ReanimatedModuleContext);
687
- function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
688
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeProviderBody, { defaults, expoBlur, fontFamily, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
690
+ var MaskedViewModuleContext = (0, import_react5.createContext)(void 0);
691
+ var useMaskedViewModule = () => (0, import_react5.useContext)(MaskedViewModuleContext);
692
+ function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
693
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
689
694
  }
690
695
  var ThemeSettingsProvider = themeSettingsProvider;
691
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
696
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
692
697
  const { settings } = useThemeSettingsInternal();
693
698
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
694
699
  const theme = (0, import_react5.useMemo)(() => {
@@ -706,7 +711,7 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
706
711
  if (!theme) return null;
707
712
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react_native_paper4.Provider, { theme, children: [
708
713
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.StatusBar, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
709
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) })
714
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native_paper4.Portal.Host, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_native3.View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) })
710
715
  ] });
711
716
  }
712
717
  var styles = import_react_native3.StyleSheet.create({
@@ -1038,59 +1043,141 @@ function FAB(props) {
1038
1043
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_react_native_paper13.FAB, { ...defaults.FAB, ...props });
1039
1044
  }
1040
1045
 
1046
+ // src/components/FadingScrollView.tsx
1047
+ var import_react11 = require("react");
1048
+ var import_react_native9 = require("react-native");
1049
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1050
+ var SCROLL_FADE_HEIGHT = 24;
1051
+ function FadingScrollView({ style, fadeHeight = SCROLL_FADE_HEIGHT, onScroll, onLayout, onContentSizeChange, scrollEventThrottle = 16, children, ...rest }) {
1052
+ const MaskedView = useMaskedViewModule();
1053
+ const sizes = (0, import_react11.useRef)({ viewport: 0, content: 0, offset: 0 });
1054
+ const [edges, setEdges] = (0, import_react11.useState)(NO_EDGES);
1055
+ const update = (0, import_react11.useCallback)(() => {
1056
+ const { viewport, content, offset } = sizes.current;
1057
+ const above = offset > 1;
1058
+ const below = content - viewport - offset > 1;
1059
+ setEdges((prev) => prev.above === above && prev.below === below ? prev : { above, below });
1060
+ }, []);
1061
+ const scroll = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1062
+ import_react_native9.ScrollView,
1063
+ {
1064
+ ...rest,
1065
+ style: styles5.fill,
1066
+ scrollEventThrottle,
1067
+ onScroll: (e) => {
1068
+ sizes.current.offset = e.nativeEvent.contentOffset.y;
1069
+ update();
1070
+ onScroll?.(e);
1071
+ },
1072
+ onLayout: (e) => {
1073
+ sizes.current.viewport = e.nativeEvent.layout.height;
1074
+ update();
1075
+ onLayout?.(e);
1076
+ },
1077
+ onContentSizeChange: (width, height) => {
1078
+ sizes.current.content = height;
1079
+ update();
1080
+ onContentSizeChange?.(width, height);
1081
+ },
1082
+ children
1083
+ }
1084
+ );
1085
+ if (import_react_native9.Platform.OS === "web") return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native9.View, { style: [styles5.box, style, (edges.above || edges.below) && webFadeStyle(fadeHeight, edges)], children: scroll });
1086
+ if (!MaskedView) return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native9.View, { style: [styles5.box, style], children: scroll });
1087
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1088
+ MaskedView,
1089
+ {
1090
+ style: [styles5.box, style],
1091
+ maskElement: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native9.View, { style: styles5.fill, children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native9.View, { style: [{ height: fadeHeight }, edges.above ? FADE_IN_BAND : styles5.opaque] }),
1093
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native9.View, { style: styles5.fillOpaque }),
1094
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native9.View, { style: [{ height: fadeHeight }, edges.below ? FADE_OUT_BAND : styles5.opaque] })
1095
+ ] }),
1096
+ children: scroll
1097
+ }
1098
+ );
1099
+ }
1100
+ var FADE_IN_BAND = { experimental_backgroundImage: "linear-gradient(transparent, #000)" };
1101
+ var FADE_OUT_BAND = { experimental_backgroundImage: "linear-gradient(#000, transparent)" };
1102
+ var NO_EDGES = { above: false, below: false };
1103
+ function webFadeStyle(fadeHeight, edges) {
1104
+ const top = edges.above ? `transparent 0, #000 ${fadeHeight}px` : "#000 0";
1105
+ const bottom = edges.below ? `#000 calc(100% - ${fadeHeight}px), transparent 100%` : "#000 100%";
1106
+ const mask = `linear-gradient(to bottom, ${top}, ${bottom})`;
1107
+ return { maskImage: mask, WebkitMaskImage: mask };
1108
+ }
1109
+ var styles5 = import_react_native9.StyleSheet.create({
1110
+ // A plain ScrollView's own flex defaults, so it sizes the same in its parent.
1111
+ box: {
1112
+ flexGrow: 1,
1113
+ flexShrink: 1
1114
+ },
1115
+ fill: {
1116
+ flexGrow: 1,
1117
+ flexShrink: 1
1118
+ },
1119
+ fillOpaque: {
1120
+ backgroundColor: "#000",
1121
+ flexGrow: 1
1122
+ },
1123
+ opaque: {
1124
+ backgroundColor: "#000"
1125
+ }
1126
+ });
1127
+
1041
1128
  // src/components/IconButton.tsx
1042
1129
  var import_react_native_paper14 = require("react-native-paper");
1043
- var import_jsx_runtime15 = require("react/jsx-runtime");
1130
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1044
1131
  function IconButton({ variant, iconColor, containerColor, ...props }) {
1045
1132
  const defaults = usePaperDefaults();
1046
1133
  const { colors } = (0, import_react_native_paper14.useTheme)();
1047
1134
  const variantContainerColor = variant === "primary" ? colors.primaryContainer : variant === "secondary" ? colors.secondaryContainer : variant === "tertiary" ? colors.tertiaryContainer : variant === "surface" ? colors.surface : void 0;
1048
1135
  const variantIconColor = variant === "primary" ? colors.onPrimaryContainer : variant === "secondary" ? colors.onSecondaryContainer : variant === "tertiary" ? colors.onTertiaryContainer : variant === "surface" ? colors.onSurface : void 0;
1049
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native_paper14.IconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper14.IconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
1050
1137
  }
1051
1138
 
1052
1139
  // src/components/Menu.tsx
1053
- var import_react11 = require("react");
1054
- var import_react_native9 = require("react-native");
1140
+ var import_react12 = require("react");
1141
+ var import_react_native10 = require("react-native");
1055
1142
  var import_react_native_paper15 = require("react-native-paper");
1056
- var import_jsx_runtime16 = require("react/jsx-runtime");
1143
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1057
1144
  var ANIMATION_DURATION2 = 250;
1058
- var EASING = import_react_native9.Easing.bezier(0.4, 0, 0.2, 1);
1145
+ var EASING = import_react_native10.Easing.bezier(0.4, 0, 0.2, 1);
1059
1146
  var DEFAULT_ELEVATION = 2;
1060
1147
  var SHADOW_HEIGHT = [0, 1, 2, 4, 6, 8];
1061
1148
  var SHADOW_RADIUS = [0, 3, 6, 8, 10, 12];
1062
- var AnimatedBlurView2 = import_react_native9.Animated.createAnimatedComponent(BlurView);
1149
+ var AnimatedBlurView2 = import_react_native10.Animated.createAnimatedComponent(BlurView);
1063
1150
  var FadingBlurView = ({ blur, children, elevation, theme: themeOverrides, visible }) => {
1064
1151
  const { animation, roundness } = (0, import_react_native_paper15.useTheme)(themeOverrides);
1065
- const [opacity] = (0, import_react11.useState)(() => new import_react_native9.Animated.Value(0));
1066
- const [closing, setClosing] = (0, import_react11.useState)(false);
1152
+ const [opacity] = (0, import_react12.useState)(() => new import_react_native10.Animated.Value(0));
1153
+ const [closing, setClosing] = (0, import_react12.useState)(false);
1067
1154
  if (!visible && !closing) setClosing(true);
1068
- (0, import_react11.useEffect)(() => {
1069
- import_react_native9.Animated.timing(opacity, { duration: animation.scale * ANIMATION_DURATION2, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start();
1155
+ (0, import_react12.useEffect)(() => {
1156
+ import_react_native10.Animated.timing(opacity, { duration: animation.scale * ANIMATION_DURATION2, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start();
1070
1157
  }, [animation.scale, closing, opacity]);
1071
1158
  const shadow = elevation > 0 ? { shadowColor: "#000000", shadowOffset: { height: SHADOW_HEIGHT[elevation], width: 0 }, shadowOpacity: 0.3, shadowRadius: SHADOW_RADIUS[elevation] } : null;
1072
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
1159
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
1073
1160
  };
1074
1161
  var MenuComponent = ({ blur: blurProp, children, ...props }) => {
1075
1162
  const blur = useBlur(blurProp);
1076
- const web = import_react_native9.Platform.OS === "web";
1077
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_native_paper15.Menu, { ...props, contentStyle: [styles5.content, web && styles5.webSurface, props.contentStyle], children: web ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(FadingBlurView, { blur, elevation: props.mode === "flat" ? 0 : props.elevation ?? DEFAULT_ELEVATION, theme: props.theme, visible: props.visible, children }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BlurView, { blur, children }) });
1163
+ const web = import_react_native10.Platform.OS === "web";
1164
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper15.Menu, { ...props, contentStyle: [styles6.content, web && styles6.webSurface, props.contentStyle], children: web ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FadingBlurView, { blur, elevation: props.mode === "flat" ? 0 : props.elevation ?? DEFAULT_ELEVATION, theme: props.theme, visible: props.visible, children }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BlurView, { blur, children }) });
1078
1165
  };
1079
1166
  var Menu = Object.assign(MenuComponent, {
1080
1167
  Item: import_react_native_paper15.Menu.Item
1081
1168
  });
1082
- var styles5 = import_react_native9.StyleSheet.create({
1169
+ var styles6 = import_react_native10.StyleSheet.create({
1083
1170
  content: { backgroundColor: "transparent", paddingVertical: 0 },
1084
1171
  webSurface: { opacity: 1, shadowOpacity: 0 }
1085
1172
  });
1086
1173
 
1087
1174
  // src/components/TextInput.tsx
1088
- var import_react12 = require("react");
1175
+ var import_react13 = require("react");
1089
1176
  var import_react_native_paper16 = require("react-native-paper");
1090
- var import_jsx_runtime17 = require("react/jsx-runtime");
1091
- var TextInput = (0, import_react12.forwardRef)(function TextInput2(props, ref) {
1177
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1178
+ var TextInput = (0, import_react13.forwardRef)(function TextInput2(props, ref) {
1092
1179
  const defaults = usePaperDefaults();
1093
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper16.TextInput, { ref, ...defaults.TextInput, ...props });
1180
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native_paper16.TextInput, { ref, ...defaults.TextInput, ...props });
1094
1181
  });
1095
1182
 
1096
1183
  // src/redux/themeSlice.ts
@@ -1119,10 +1206,10 @@ var import_react_native_paper17 = require("react-native-paper");
1119
1206
  var useAutoPaperTheme = () => (0, import_react_native_paper17.useTheme)();
1120
1207
 
1121
1208
  // src/useThemeBridgeProps.ts
1122
- var import_react13 = require("react");
1209
+ var import_react14 = require("react");
1123
1210
  function useThemeBridgeProps(props) {
1124
1211
  const { initialValue, onChange, onReady } = props;
1125
- return (0, import_react13.useMemo)(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady]);
1212
+ return (0, import_react14.useMemo)(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady]);
1126
1213
  }
1127
1214
 
1128
1215
  // src/utils/getThirdColor.ts
@@ -1219,11 +1306,13 @@ var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
1219
1306
  ColorPicker,
1220
1307
  Dialog,
1221
1308
  FAB,
1309
+ FadingScrollView,
1222
1310
  HarmonyPicker,
1223
1311
  IconButton,
1224
1312
  Menu,
1225
1313
  PalettePicker,
1226
1314
  Provider,
1315
+ SCROLL_FADE_HEIGHT,
1227
1316
  SEMANTIC_BASE_COLORS,
1228
1317
  TextInput,
1229
1318
  ThemeSettingsContext,
@@ -1251,6 +1340,7 @@ var getTintTextColor = (tintColor, backdropColor, tintOpacity) => {
1251
1340
  useBlur,
1252
1341
  useBlurModule,
1253
1342
  useComputedTheme,
1343
+ useMaskedViewModule,
1254
1344
  useNavBarContext,
1255
1345
  usePaperDefaults,
1256
1346
  useReanimatedModule,
package/dist/index.mjs CHANGED
@@ -601,11 +601,13 @@ var BlurModuleContext = createContext2(void 0);
601
601
  var useBlurModule = () => useContext3(BlurModuleContext);
602
602
  var ReanimatedModuleContext = createContext2(void 0);
603
603
  var useReanimatedModule = () => useContext3(ReanimatedModuleContext);
604
- function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
605
- return /* @__PURE__ */ jsx4(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ jsx4(ThemeProviderBody, { defaults, expoBlur, fontFamily, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
604
+ var MaskedViewModuleContext = createContext2(void 0);
605
+ var useMaskedViewModule = () => useContext3(MaskedViewModuleContext);
606
+ function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
607
+ return /* @__PURE__ */ jsx4(ThemeSettingsProvider, { initialValue, onChange, children: /* @__PURE__ */ jsx4(ThemeProviderBody, { defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style, children }) });
606
608
  }
607
609
  var ThemeSettingsProvider = themeSettingsProvider;
608
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
610
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }) {
609
611
  const { settings } = useThemeSettingsInternal();
610
612
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony);
611
613
  const theme = useMemo(() => {
@@ -623,7 +625,7 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
623
625
  if (!theme) return null;
624
626
  return /* @__PURE__ */ jsxs2(PaperProvider, { theme, children: [
625
627
  /* @__PURE__ */ jsx4(StatusBar2, { backgroundColor: theme.colors.background, barStyle: theme.dark ? "light-content" : "dark-content", ...statusBarProps }),
626
- /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx4(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx4(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx4(Portal.Host, { children: /* @__PURE__ */ jsx4(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) })
628
+ /* @__PURE__ */ jsx4(PaperDefaultsContext.Provider, { value: defaults ?? {}, children: /* @__PURE__ */ jsx4(NavBarContext.Provider, { value: { navigationBar, onNavBarChange }, children: /* @__PURE__ */ jsx4(BlurModuleContext.Provider, { value: expoBlur, children: /* @__PURE__ */ jsx4(ReanimatedModuleContext.Provider, { value: reanimated, children: /* @__PURE__ */ jsx4(MaskedViewModuleContext.Provider, { value: maskedView, children: /* @__PURE__ */ jsx4(Portal.Host, { children: /* @__PURE__ */ jsx4(View, { style: [styles.flex, { backgroundColor: theme.colors.background }, style], children }) }) }) }) }) }) })
627
629
  ] });
628
630
  }
629
631
  var styles = StyleSheet.create({
@@ -955,22 +957,104 @@ function FAB(props) {
955
957
  return /* @__PURE__ */ jsx14(PaperFAB, { ...defaults.FAB, ...props });
956
958
  }
957
959
 
960
+ // src/components/FadingScrollView.tsx
961
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState6 } from "react";
962
+ import { Platform as Platform4, ScrollView, StyleSheet as StyleSheet6, View as View6 } from "react-native";
963
+ import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
964
+ var SCROLL_FADE_HEIGHT = 24;
965
+ function FadingScrollView({ style, fadeHeight = SCROLL_FADE_HEIGHT, onScroll, onLayout, onContentSizeChange, scrollEventThrottle = 16, children, ...rest }) {
966
+ const MaskedView = useMaskedViewModule();
967
+ const sizes = useRef2({ viewport: 0, content: 0, offset: 0 });
968
+ const [edges, setEdges] = useState6(NO_EDGES);
969
+ const update = useCallback2(() => {
970
+ const { viewport, content, offset } = sizes.current;
971
+ const above = offset > 1;
972
+ const below = content - viewport - offset > 1;
973
+ setEdges((prev) => prev.above === above && prev.below === below ? prev : { above, below });
974
+ }, []);
975
+ const scroll = /* @__PURE__ */ jsx15(
976
+ ScrollView,
977
+ {
978
+ ...rest,
979
+ style: styles5.fill,
980
+ scrollEventThrottle,
981
+ onScroll: (e) => {
982
+ sizes.current.offset = e.nativeEvent.contentOffset.y;
983
+ update();
984
+ onScroll?.(e);
985
+ },
986
+ onLayout: (e) => {
987
+ sizes.current.viewport = e.nativeEvent.layout.height;
988
+ update();
989
+ onLayout?.(e);
990
+ },
991
+ onContentSizeChange: (width, height) => {
992
+ sizes.current.content = height;
993
+ update();
994
+ onContentSizeChange?.(width, height);
995
+ },
996
+ children
997
+ }
998
+ );
999
+ if (Platform4.OS === "web") return /* @__PURE__ */ jsx15(View6, { style: [styles5.box, style, (edges.above || edges.below) && webFadeStyle(fadeHeight, edges)], children: scroll });
1000
+ if (!MaskedView) return /* @__PURE__ */ jsx15(View6, { style: [styles5.box, style], children: scroll });
1001
+ return /* @__PURE__ */ jsx15(
1002
+ MaskedView,
1003
+ {
1004
+ style: [styles5.box, style],
1005
+ maskElement: /* @__PURE__ */ jsxs7(View6, { style: styles5.fill, children: [
1006
+ /* @__PURE__ */ jsx15(View6, { style: [{ height: fadeHeight }, edges.above ? FADE_IN_BAND : styles5.opaque] }),
1007
+ /* @__PURE__ */ jsx15(View6, { style: styles5.fillOpaque }),
1008
+ /* @__PURE__ */ jsx15(View6, { style: [{ height: fadeHeight }, edges.below ? FADE_OUT_BAND : styles5.opaque] })
1009
+ ] }),
1010
+ children: scroll
1011
+ }
1012
+ );
1013
+ }
1014
+ var FADE_IN_BAND = { experimental_backgroundImage: "linear-gradient(transparent, #000)" };
1015
+ var FADE_OUT_BAND = { experimental_backgroundImage: "linear-gradient(#000, transparent)" };
1016
+ var NO_EDGES = { above: false, below: false };
1017
+ function webFadeStyle(fadeHeight, edges) {
1018
+ const top = edges.above ? `transparent 0, #000 ${fadeHeight}px` : "#000 0";
1019
+ const bottom = edges.below ? `#000 calc(100% - ${fadeHeight}px), transparent 100%` : "#000 100%";
1020
+ const mask = `linear-gradient(to bottom, ${top}, ${bottom})`;
1021
+ return { maskImage: mask, WebkitMaskImage: mask };
1022
+ }
1023
+ var styles5 = StyleSheet6.create({
1024
+ // A plain ScrollView's own flex defaults, so it sizes the same in its parent.
1025
+ box: {
1026
+ flexGrow: 1,
1027
+ flexShrink: 1
1028
+ },
1029
+ fill: {
1030
+ flexGrow: 1,
1031
+ flexShrink: 1
1032
+ },
1033
+ fillOpaque: {
1034
+ backgroundColor: "#000",
1035
+ flexGrow: 1
1036
+ },
1037
+ opaque: {
1038
+ backgroundColor: "#000"
1039
+ }
1040
+ });
1041
+
958
1042
  // src/components/IconButton.tsx
959
1043
  import { IconButton as PaperIconButton, useTheme as useTheme8 } from "react-native-paper";
960
- import { jsx as jsx15 } from "react/jsx-runtime";
1044
+ import { jsx as jsx16 } from "react/jsx-runtime";
961
1045
  function IconButton({ variant, iconColor, containerColor, ...props }) {
962
1046
  const defaults = usePaperDefaults();
963
1047
  const { colors } = useTheme8();
964
1048
  const variantContainerColor = variant === "primary" ? colors.primaryContainer : variant === "secondary" ? colors.secondaryContainer : variant === "tertiary" ? colors.tertiaryContainer : variant === "surface" ? colors.surface : void 0;
965
1049
  const variantIconColor = variant === "primary" ? colors.onPrimaryContainer : variant === "secondary" ? colors.onSecondaryContainer : variant === "tertiary" ? colors.onTertiaryContainer : variant === "surface" ? colors.onSurface : void 0;
966
- return /* @__PURE__ */ jsx15(PaperIconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
1050
+ return /* @__PURE__ */ jsx16(PaperIconButton, { ...defaults.IconButton, containerColor: containerColor ?? variantContainerColor, iconColor: iconColor ?? variantIconColor, ...props });
967
1051
  }
968
1052
 
969
1053
  // src/components/Menu.tsx
970
- import { useEffect as useEffect5, useState as useState6 } from "react";
971
- import { Animated as Animated2, Easing as Easing2, Platform as Platform4, StyleSheet as StyleSheet6 } from "react-native";
1054
+ import { useEffect as useEffect5, useState as useState7 } from "react";
1055
+ import { Animated as Animated2, Easing as Easing2, Platform as Platform5, StyleSheet as StyleSheet7 } from "react-native";
972
1056
  import { Menu as PaperMenu, useTheme as useTheme9 } from "react-native-paper";
973
- import { jsx as jsx16 } from "react/jsx-runtime";
1057
+ import { jsx as jsx17 } from "react/jsx-runtime";
974
1058
  var ANIMATION_DURATION2 = 250;
975
1059
  var EASING = Easing2.bezier(0.4, 0, 0.2, 1);
976
1060
  var DEFAULT_ELEVATION = 2;
@@ -979,24 +1063,24 @@ var SHADOW_RADIUS = [0, 3, 6, 8, 10, 12];
979
1063
  var AnimatedBlurView2 = Animated2.createAnimatedComponent(BlurView);
980
1064
  var FadingBlurView = ({ blur, children, elevation, theme: themeOverrides, visible }) => {
981
1065
  const { animation, roundness } = useTheme9(themeOverrides);
982
- const [opacity] = useState6(() => new Animated2.Value(0));
983
- const [closing, setClosing] = useState6(false);
1066
+ const [opacity] = useState7(() => new Animated2.Value(0));
1067
+ const [closing, setClosing] = useState7(false);
984
1068
  if (!visible && !closing) setClosing(true);
985
1069
  useEffect5(() => {
986
1070
  Animated2.timing(opacity, { duration: animation.scale * ANIMATION_DURATION2, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start();
987
1071
  }, [animation.scale, closing, opacity]);
988
1072
  const shadow = elevation > 0 ? { shadowColor: "#000000", shadowOffset: { height: SHADOW_HEIGHT[elevation], width: 0 }, shadowOpacity: 0.3, shadowRadius: SHADOW_RADIUS[elevation] } : null;
989
- return /* @__PURE__ */ jsx16(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
1073
+ return /* @__PURE__ */ jsx17(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
990
1074
  };
991
1075
  var MenuComponent = ({ blur: blurProp, children, ...props }) => {
992
1076
  const blur = useBlur(blurProp);
993
- const web = Platform4.OS === "web";
994
- return /* @__PURE__ */ jsx16(PaperMenu, { ...props, contentStyle: [styles5.content, web && styles5.webSurface, props.contentStyle], children: web ? /* @__PURE__ */ jsx16(FadingBlurView, { blur, elevation: props.mode === "flat" ? 0 : props.elevation ?? DEFAULT_ELEVATION, theme: props.theme, visible: props.visible, children }) : /* @__PURE__ */ jsx16(BlurView, { blur, children }) });
1077
+ const web = Platform5.OS === "web";
1078
+ return /* @__PURE__ */ jsx17(PaperMenu, { ...props, contentStyle: [styles6.content, web && styles6.webSurface, props.contentStyle], children: web ? /* @__PURE__ */ jsx17(FadingBlurView, { blur, elevation: props.mode === "flat" ? 0 : props.elevation ?? DEFAULT_ELEVATION, theme: props.theme, visible: props.visible, children }) : /* @__PURE__ */ jsx17(BlurView, { blur, children }) });
995
1079
  };
996
1080
  var Menu = Object.assign(MenuComponent, {
997
1081
  Item: PaperMenu.Item
998
1082
  });
999
- var styles5 = StyleSheet6.create({
1083
+ var styles6 = StyleSheet7.create({
1000
1084
  content: { backgroundColor: "transparent", paddingVertical: 0 },
1001
1085
  webSurface: { opacity: 1, shadowOpacity: 0 }
1002
1086
  });
@@ -1004,10 +1088,10 @@ var styles5 = StyleSheet6.create({
1004
1088
  // src/components/TextInput.tsx
1005
1089
  import { forwardRef } from "react";
1006
1090
  import { TextInput as PaperTextInput } from "react-native-paper";
1007
- import { jsx as jsx17 } from "react/jsx-runtime";
1091
+ import { jsx as jsx18 } from "react/jsx-runtime";
1008
1092
  var TextInput = forwardRef(function TextInput2(props, ref) {
1009
1093
  const defaults = usePaperDefaults();
1010
- return /* @__PURE__ */ jsx17(PaperTextInput, { ref, ...defaults.TextInput, ...props });
1094
+ return /* @__PURE__ */ jsx18(PaperTextInput, { ref, ...defaults.TextInput, ...props });
1011
1095
  });
1012
1096
 
1013
1097
  // src/redux/themeSlice.ts
@@ -1135,11 +1219,13 @@ export {
1135
1219
  ColorPicker,
1136
1220
  Dialog,
1137
1221
  FAB,
1222
+ FadingScrollView,
1138
1223
  HarmonyPicker,
1139
1224
  IconButton,
1140
1225
  Menu,
1141
1226
  PalettePicker,
1142
1227
  Provider,
1228
+ SCROLL_FADE_HEIGHT,
1143
1229
  SEMANTIC_BASE_COLORS,
1144
1230
  TextInput,
1145
1231
  ThemeSettingsContext,
@@ -1167,6 +1253,7 @@ export {
1167
1253
  useBlur,
1168
1254
  useBlurModule,
1169
1255
  useComputedTheme,
1256
+ useMaskedViewModule,
1170
1257
  useNavBarContext,
1171
1258
  usePaperDefaults,
1172
1259
  useReanimatedModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rific/auto-paper",
3
- "version": "0.12.4",
3
+ "version": "0.13.0",
4
4
  "description": "Adaptive react-native-paper theming: triadic palette from a seed color + system/light/dark appearance",
5
5
  "keywords": [
6
6
  "react-native",
@@ -71,6 +71,7 @@
71
71
  "@infinitetoken/eslint-config": "0.2.0",
72
72
  "@infinitetoken/jest-config": "^0.4.0",
73
73
  "@infinitetoken/tsconfig": "^0.5.0",
74
+ "@react-native-masked-view/masked-view": "^0.3.2",
74
75
  "@reduxjs/toolkit": "^2.0.0",
75
76
  "@rific/core": "^0.2.0",
76
77
  "@testing-library/dom": "^10.4.1",
@@ -94,6 +95,7 @@
94
95
  "typescript": "^6.0.3"
95
96
  },
96
97
  "peerDependencies": {
98
+ "@react-native-masked-view/masked-view": ">=0.3.0",
97
99
  "@rific/core": ">=0.1.1",
98
100
  "expo-blur": ">=56.0.0",
99
101
  "expo-navigation-bar": ">=56.0.0",
@@ -103,6 +105,9 @@
103
105
  "react-native-safe-area-context": "*"
104
106
  },
105
107
  "peerDependenciesMeta": {
108
+ "@react-native-masked-view/masked-view": {
109
+ "optional": true
110
+ },
106
111
  "expo-blur": {
107
112
  "optional": true
108
113
  },
@@ -4,6 +4,7 @@ import { configureFonts, Portal, Provider as PaperProvider } from 'react-native-
4
4
 
5
5
  import type { ExpoBlurModule } from './components/BlurView'
6
6
  import type { ReanimatedModule } from './components/Dialog'
7
+ import type { MaskedViewModule } from './components/FadingScrollView'
7
8
  import type { ExpoNavigationBarModule } from './navigation-bar'
8
9
  import { type PaperDefaults, PaperDefaultsContext } from './PaperDefaultsContext'
9
10
  import { type ThemeSettings, themeSettingsProvider, useThemeSettingsInternal } from './ThemeSettingsContext'
@@ -24,6 +25,9 @@ export const useBlurModule = () => useContext(BlurModuleContext)
24
25
  const ReanimatedModuleContext = createContext<ReanimatedModule | undefined>(undefined)
25
26
  export const useReanimatedModule = () => useContext(ReanimatedModuleContext)
26
27
 
28
+ const MaskedViewModuleContext = createContext<MaskedViewModule | undefined>(undefined)
29
+ export const useMaskedViewModule = () => useContext(MaskedViewModuleContext)
30
+
27
31
  export type ProviderProps = {
28
32
  children: ReactNode
29
33
  defaults?: PaperDefaults
@@ -36,6 +40,8 @@ export type ProviderProps = {
36
40
  * to keep each platform's system font (MD3's own default). */
37
41
  fontFamily?: string
38
42
  initialValue?: Partial<ThemeSettings>
43
+ /** Injects @react-native-masked-view/masked-view so `<FadingScrollView>` can fade its content out above a pinned footer on iOS and Android. Pass `import MaskedView from '@react-native-masked-view/masked-view'`; omit to have it scroll without the fade there (web never needs it). */
44
+ maskedView?: MaskedViewModule
39
45
  /** Injects expo-navigation-bar so the Android nav bar icon style auto-syncs with the theme. Pass `import * as ExpoNavigationBar from 'expo-navigation-bar'`; omit (and don't pass onNavBarChange) to skip nav bar syncing entirely. */
40
46
  navigationBar?: ExpoNavigationBarModule
41
47
  onChange?: (settings: ThemeSettings) => void
@@ -53,10 +59,10 @@ export type ProviderProps = {
53
59
  // wrapper for the settings half; ThemeProviderBody (an actual descendant, reached via
54
60
  // useThemeSettingsInternal()) does all the theme-computation/gating work the single combined
55
61
  // component used to do directly.
56
- export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
62
+ export function Provider({ children, defaults, expoBlur, fontFamily, initialValue, maskedView, navigationBar, onChange, onNavBarChange, onReady, reanimated, statusBarProps, style }: ProviderProps) {
57
63
  return (
58
64
  <ThemeSettingsProvider initialValue={initialValue} onChange={onChange}>
59
- <ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBarProps={statusBarProps} style={style}>
65
+ <ThemeProviderBody defaults={defaults} expoBlur={expoBlur} fontFamily={fontFamily} maskedView={maskedView} navigationBar={navigationBar} onNavBarChange={onNavBarChange} onReady={onReady} reanimated={reanimated} statusBarProps={statusBarProps} style={style}>
60
66
  {children}
61
67
  </ThemeProviderBody>
62
68
  </ThemeSettingsProvider>
@@ -67,7 +73,7 @@ const ThemeSettingsProvider = themeSettingsProvider
67
73
 
68
74
  type ThemeProviderBodyProps = Omit<ProviderProps, 'initialValue' | 'onChange'>
69
75
 
70
- function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }: ThemeProviderBodyProps) {
76
+ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, maskedView, navigationBar, onNavBarChange, onReady, reanimated, statusBarProps, style }: ThemeProviderBodyProps) {
71
77
  const { settings } = useThemeSettingsInternal()
72
78
 
73
79
  const computedTheme = useComputedTheme(settings.appearance, settings.color, settings.harmony)
@@ -99,17 +105,19 @@ function ThemeProviderBody({ children, defaults, expoBlur, fontFamily, navigatio
99
105
  <NavBarContext.Provider value={{ navigationBar, onNavBarChange }}>
100
106
  <BlurModuleContext.Provider value={expoBlur}>
101
107
  <ReanimatedModuleContext.Provider value={reanimated}>
102
- {/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
108
+ <MaskedViewModuleContext.Provider value={maskedView}>
109
+ {/* PaperProvider's own PortalHost sits above every context provided here, and Paper's
103
110
  Portal only carries its theme and settings across, so anything rendered through a
104
111
  Portal (this package's Dialog, Paper's Menu and Tooltip, or anything an app wraps
105
- in <Portal> itself) would otherwise lose expoBlur, reanimated, defaults and the nav
112
+ in <Portal> itself) would otherwise lose expoBlur, reanimated, maskedView, defaults and the nav
106
113
  bar hooks: Dialog/Menu's blur silently fell back to solid. This nested host is the
107
114
  nearest one for every Portal under Provider, so their content mounts inside all of
108
115
  these contexts instead. It wraps the styled View, not the other way round, so
109
116
  portals still cover the full area regardless of `style`. */}
110
- <Portal.Host>
111
- <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
112
- </Portal.Host>
117
+ <Portal.Host>
118
+ <View style={[styles.flex, { backgroundColor: theme.colors.background }, style]}>{children}</View>
119
+ </Portal.Host>
120
+ </MaskedViewModuleContext.Provider>
113
121
  </ReanimatedModuleContext.Provider>
114
122
  </BlurModuleContext.Provider>
115
123
  </NavBarContext.Provider>
@@ -0,0 +1,127 @@
1
+ import { type ComponentType, type ReactElement, type ReactNode, useCallback, useRef, useState } from 'react'
2
+ import { type LayoutChangeEvent, type NativeScrollEvent, type NativeSyntheticEvent, Platform, ScrollView, type ScrollViewProps, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
3
+
4
+ import { useMaskedViewModule } from '../ThemeProvider'
5
+
6
+ // Minimal local shape of @react-native-masked-view/masked-view's default export, the same way
7
+ // ExpoBlurModule stands in for expo-blur: the optional peer's real types are never imported, so a
8
+ // consumer that never installed it doesn't need them. <Provider maskedView={MaskedView}> hands it in.
9
+ export type MaskedViewModule = ComponentType<{ children?: ReactNode; maskElement: ReactElement; style?: StyleProp<ViewStyle> }>
10
+
11
+ // How far in from each edge a FadingScrollView's content fades out while there is more past that edge.
12
+ export const SCROLL_FADE_HEIGHT = 24
13
+
14
+ export type FadingScrollViewProps = Omit<ScrollViewProps, 'style'> & {
15
+ // Placed on the outer box (the flex item), so a caller's flexGrow/flexShrink/alignSelf work as they
16
+ // did on a plain ScrollView.
17
+ style?: StyleProp<ViewStyle>
18
+ fadeHeight?: number
19
+ }
20
+
21
+ // A vertical ScrollView whose content fades to transparent along an edge while there is more to scroll
22
+ // past it: at the bottom, the cue that rows continue below a pinned footer, like a dialog's Done
23
+ // button; at the top, the same once rows have scrolled up past the edge (Jay, 2026-10-15: one treatment
24
+ // at both ends instead of a hard cut at the top). It masks
25
+ // the content itself rather than painting anything over it, so whatever is behind (a blurred
26
+ // Dialog/BlurView surface, a plain one, light or dark) shows through unchanged: no band, no seam. (A
27
+ // second BlurView as a frosted footer was tried in @tastic/hud first and left a seam on iOS and web: a
28
+ // blur nested in a blurred card can't frost the card's own content.) Once the end is reached the fade
29
+ // goes away, and the top fade only shows once scrolled down, so neither the last row nor the first
30
+ // (a dialog's header, say) is ever dimmed at rest.
31
+ //
32
+ // iOS/Android: a MaskedView (from <Provider maskedView={...}>) whose top and bottom bands are React
33
+ // Native's native linear gradient (experimental_backgroundImage). Without one passed in, content just scrolls
34
+ // with no fade. Web: react-native-masked-view's web build renders only the mask element (it doesn't
35
+ // mask), so the outer box gets a CSS mask-image instead, which react-native-web passes through; no
36
+ // module needed there.
37
+ export function FadingScrollView({ style, fadeHeight = SCROLL_FADE_HEIGHT, onScroll, onLayout, onContentSizeChange, scrollEventThrottle = 16, children, ...rest }: FadingScrollViewProps) {
38
+ const MaskedView = useMaskedViewModule()
39
+ // Sizes live in a ref and only the yes/no answers are state, so scrolling re-renders nothing until a
40
+ // fade has to appear or go.
41
+ const sizes = useRef({ viewport: 0, content: 0, offset: 0 })
42
+ const [edges, setEdges] = useState(NO_EDGES)
43
+ const update = useCallback(() => {
44
+ const { viewport, content, offset } = sizes.current
45
+ const above = offset > 1
46
+ const below = content - viewport - offset > 1
47
+ setEdges((prev) => (prev.above === above && prev.below === below ? prev : { above, below }))
48
+ }, [])
49
+
50
+ const scroll = (
51
+ <ScrollView
52
+ {...rest}
53
+ style={styles.fill}
54
+ scrollEventThrottle={scrollEventThrottle}
55
+ onScroll={(e: NativeSyntheticEvent<NativeScrollEvent>) => {
56
+ sizes.current.offset = e.nativeEvent.contentOffset.y
57
+ update()
58
+ onScroll?.(e)
59
+ }}
60
+ onLayout={(e: LayoutChangeEvent) => {
61
+ sizes.current.viewport = e.nativeEvent.layout.height
62
+ update()
63
+ onLayout?.(e)
64
+ }}
65
+ onContentSizeChange={(width: number, height: number) => {
66
+ sizes.current.content = height
67
+ update()
68
+ onContentSizeChange?.(width, height)
69
+ }}
70
+ >
71
+ {children}
72
+ </ScrollView>
73
+ )
74
+
75
+ if (Platform.OS === 'web') return <View style={[styles.box, style, (edges.above || edges.below) && webFadeStyle(fadeHeight, edges)]}>{scroll}</View>
76
+ if (!MaskedView) return <View style={[styles.box, style]}>{scroll}</View>
77
+
78
+ return (
79
+ <MaskedView
80
+ style={[styles.box, style]}
81
+ maskElement={
82
+ <View style={styles.fill}>
83
+ <View style={[{ height: fadeHeight }, edges.above ? FADE_IN_BAND : styles.opaque]} />
84
+ <View style={styles.fillOpaque} />
85
+ <View style={[{ height: fadeHeight }, edges.below ? FADE_OUT_BAND : styles.opaque]} />
86
+ </View>
87
+ }
88
+ >
89
+ {scroll}
90
+ </MaskedView>
91
+ )
92
+ }
93
+
94
+ // React Native's native linear gradient (0.77+, the New Architecture; the fleet's apps are on 0.86), not
95
+ // in this package's own 0.76 dev types, hence the cast. A mask's alpha is what shows: opaque keeps the
96
+ // content, transparent hides it.
97
+ const FADE_IN_BAND = { experimental_backgroundImage: 'linear-gradient(transparent, #000)' } as unknown as ViewStyle
98
+ const FADE_OUT_BAND = { experimental_backgroundImage: 'linear-gradient(#000, transparent)' } as unknown as ViewStyle
99
+
100
+ const NO_EDGES = { above: false, below: false }
101
+
102
+ // mask-image isn't in React Native's style types, but react-native-web passes it to the DOM as is.
103
+ function webFadeStyle(fadeHeight: number, edges: { above: boolean; below: boolean }): ViewStyle {
104
+ const top = edges.above ? `transparent 0, #000 ${fadeHeight}px` : '#000 0'
105
+ const bottom = edges.below ? `#000 calc(100% - ${fadeHeight}px), transparent 100%` : '#000 100%'
106
+ const mask = `linear-gradient(to bottom, ${top}, ${bottom})`
107
+ return { maskImage: mask, WebkitMaskImage: mask } as unknown as ViewStyle
108
+ }
109
+
110
+ const styles = StyleSheet.create({
111
+ // A plain ScrollView's own flex defaults, so it sizes the same in its parent.
112
+ box: {
113
+ flexGrow: 1,
114
+ flexShrink: 1
115
+ },
116
+ fill: {
117
+ flexGrow: 1,
118
+ flexShrink: 1
119
+ },
120
+ fillOpaque: {
121
+ backgroundColor: '#000',
122
+ flexGrow: 1
123
+ },
124
+ opaque: {
125
+ backgroundColor: '#000'
126
+ }
127
+ })
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ export { ColorPicker, defaultColors } from './components/ColorPicker'
15
15
  export type { DialogProps, ReanimatedModule } from './components/Dialog'
16
16
  export { Dialog } from './components/Dialog'
17
17
  export { FAB } from './components/FAB'
18
+ export type { FadingScrollViewProps, MaskedViewModule } from './components/FadingScrollView'
19
+ export { FadingScrollView, SCROLL_FADE_HEIGHT } from './components/FadingScrollView'
18
20
  export { HarmonyPicker } from './components/HarmonyPicker'
19
21
  export type { IconButtonProps } from './components/IconButton'
20
22
  export { IconButton } from './components/IconButton'
@@ -29,7 +31,7 @@ export { usePaperDefaults } from './PaperDefaultsContext'
29
31
  export type { ThemeState } from './redux/themeSlice'
30
32
  export { createThemeReducer, selectThemeAppearance, selectThemeBlur, selectThemeColor, selectThemeHarmony, themeActions, themeReducer } from './redux/themeSlice'
31
33
  export type { ProviderProps } from './ThemeProvider'
32
- export { Provider, useBlurModule, useNavBarContext, useReanimatedModule } from './ThemeProvider'
34
+ export { Provider, useBlurModule, useMaskedViewModule, useNavBarContext, useReanimatedModule } from './ThemeProvider'
33
35
  export type { ThemeSettings, ThemeSettingsContextType } from './ThemeSettingsContext'
34
36
  export { defaultThemeSettings, ThemeSettingsContext } from './ThemeSettingsContext'
35
37
  export type { AutoPaperTheme, SemanticColorRoles, ThemeAppearance } from './useComputedTheme'