@rific/auto-paper 0.12.2 → 0.12.3
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/dist/index.js +38 -9
- package/dist/index.mjs +39 -10
- package/package.json +1 -1
- package/src/components/Dialog.tsx +25 -9
- package/src/components/Menu.tsx +57 -6
package/dist/index.js
CHANGED
|
@@ -750,6 +750,8 @@ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacit
|
|
|
750
750
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
751
751
|
var ANIMATION_DURATION = 220;
|
|
752
752
|
var DIALOG_MARGIN = 32;
|
|
753
|
+
var AnimatedBlurView = import_react_native5.Animated.createAnimatedComponent(BlurView);
|
|
754
|
+
var AnimatedPressable = import_react_native5.Animated.createAnimatedComponent(import_react_native5.Pressable);
|
|
753
755
|
function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, dismissableBackButton = dismissable, visible, onDismiss, children, style, testID = "dialog" }) {
|
|
754
756
|
const blur = useBlur(blurProp);
|
|
755
757
|
const reanimated = useReanimatedModule();
|
|
@@ -793,9 +795,16 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
793
795
|
return child;
|
|
794
796
|
});
|
|
795
797
|
const borderRadius = (isV3 ? 7 : 1) * roundness;
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
798
|
+
const web = import_react_native5.Platform.OS === "web";
|
|
799
|
+
const fade = { opacity };
|
|
800
|
+
const styleOpacity = import_react_native5.StyleSheet.flatten(style)?.opacity;
|
|
801
|
+
const cardFade = typeof styleOpacity === "number" ? { opacity: import_react_native5.Animated.multiply(opacity, styleOpacity) } : fade;
|
|
802
|
+
const Backdrop = web ? AnimatedPressable : import_react_native5.Pressable;
|
|
803
|
+
const Card = web ? AnimatedBlurView : BlurView;
|
|
804
|
+
const card = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Card, { blur, style: [styles2.card, { borderRadius }, style, web && cardFade], testID: `${testID}-surface`, children: content });
|
|
805
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native_paper6.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native5.Animated.View, { accessibilityLiveRegion: "polite", accessibilityViewIsModal: true, pointerEvents: visible ? "box-none" : "none", style: [import_react_native5.StyleSheet.absoluteFill, !web && fade], testID, children: [
|
|
806
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Backdrop, { accessibilityLabel: "Close modal", accessibilityRole: "button", disabled: !dismissable, onPress: dismissable ? onDismiss : void 0, style: [import_react_native5.StyleSheet.absoluteFill, { backgroundColor: colors.backdrop }, web && fade], testID: `${testID}-backdrop` }),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native5.View, { pointerEvents: "box-none", style: [styles2.wrapper, { marginBottom: bottom, marginTop: top, paddingHorizontal: Math.max(left, right, DIALOG_MARGIN) }], testID: `${testID}-wrapper`, children: reanimated && animatedStyle ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(reanimated.View, { style: animatedStyle, testID: `${testID}-animated-wrapper`, children: card }) : card })
|
|
799
808
|
] }) });
|
|
800
809
|
}
|
|
801
810
|
DialogComponent.Title = import_react_native_paper6.Dialog.Title;
|
|
@@ -1036,25 +1045,45 @@ function IconButton({ variant, iconColor, containerColor, ...props }) {
|
|
|
1036
1045
|
}
|
|
1037
1046
|
|
|
1038
1047
|
// src/components/Menu.tsx
|
|
1048
|
+
var import_react11 = require("react");
|
|
1039
1049
|
var import_react_native9 = require("react-native");
|
|
1040
1050
|
var import_react_native_paper15 = require("react-native-paper");
|
|
1041
1051
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1052
|
+
var ANIMATION_DURATION2 = 250;
|
|
1053
|
+
var EASING = import_react_native9.Easing.bezier(0.4, 0, 0.2, 1);
|
|
1054
|
+
var DEFAULT_ELEVATION = 2;
|
|
1055
|
+
var SHADOW_HEIGHT = [0, 1, 2, 4, 6, 8];
|
|
1056
|
+
var SHADOW_RADIUS = [0, 3, 6, 8, 10, 12];
|
|
1057
|
+
var AnimatedBlurView2 = import_react_native9.Animated.createAnimatedComponent(BlurView);
|
|
1058
|
+
var FadingBlurView = ({ blur, children, elevation, theme: themeOverrides, visible }) => {
|
|
1059
|
+
const { animation, roundness } = (0, import_react_native_paper15.useTheme)(themeOverrides);
|
|
1060
|
+
const [opacity] = (0, import_react11.useState)(() => new import_react_native9.Animated.Value(0));
|
|
1061
|
+
const [closing, setClosing] = (0, import_react11.useState)(false);
|
|
1062
|
+
if (!visible && !closing) setClosing(true);
|
|
1063
|
+
(0, import_react11.useEffect)(() => {
|
|
1064
|
+
import_react_native9.Animated.timing(opacity, { duration: animation.scale * ANIMATION_DURATION2, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start();
|
|
1065
|
+
}, [animation.scale, closing, opacity]);
|
|
1066
|
+
const shadow = elevation > 0 ? { shadowColor: "#000000", shadowOffset: { height: SHADOW_HEIGHT[elevation], width: 0 }, shadowOpacity: 0.3, shadowRadius: SHADOW_RADIUS[elevation] } : null;
|
|
1067
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
|
|
1068
|
+
};
|
|
1042
1069
|
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
1043
1070
|
const blur = useBlur(blurProp);
|
|
1044
|
-
|
|
1071
|
+
const web = import_react_native9.Platform.OS === "web";
|
|
1072
|
+
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 }) });
|
|
1045
1073
|
};
|
|
1046
1074
|
var Menu = Object.assign(MenuComponent, {
|
|
1047
1075
|
Item: import_react_native_paper15.Menu.Item
|
|
1048
1076
|
});
|
|
1049
1077
|
var styles5 = import_react_native9.StyleSheet.create({
|
|
1050
|
-
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
1078
|
+
content: { backgroundColor: "transparent", paddingVertical: 0 },
|
|
1079
|
+
webSurface: { opacity: 1, shadowOpacity: 0 }
|
|
1051
1080
|
});
|
|
1052
1081
|
|
|
1053
1082
|
// src/components/TextInput.tsx
|
|
1054
|
-
var
|
|
1083
|
+
var import_react12 = require("react");
|
|
1055
1084
|
var import_react_native_paper16 = require("react-native-paper");
|
|
1056
1085
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1057
|
-
var TextInput = (0,
|
|
1086
|
+
var TextInput = (0, import_react12.forwardRef)(function TextInput2(props, ref) {
|
|
1058
1087
|
const defaults = usePaperDefaults();
|
|
1059
1088
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native_paper16.TextInput, { ref, ...defaults.TextInput, ...props });
|
|
1060
1089
|
});
|
|
@@ -1085,10 +1114,10 @@ var import_react_native_paper17 = require("react-native-paper");
|
|
|
1085
1114
|
var useAutoPaperTheme = () => (0, import_react_native_paper17.useTheme)();
|
|
1086
1115
|
|
|
1087
1116
|
// src/useThemeBridgeProps.ts
|
|
1088
|
-
var
|
|
1117
|
+
var import_react13 = require("react");
|
|
1089
1118
|
function useThemeBridgeProps(props) {
|
|
1090
1119
|
const { initialValue, onChange, onReady } = props;
|
|
1091
|
-
return (0,
|
|
1120
|
+
return (0, import_react13.useMemo)(() => ({ initialValue, onChange, onReady }), [initialValue, onChange, onReady]);
|
|
1092
1121
|
}
|
|
1093
1122
|
|
|
1094
1123
|
// src/utils/getThirdColor.ts
|
package/dist/index.mjs
CHANGED
|
@@ -416,7 +416,7 @@ import { Icon, TouchableRipple, useTheme as useTheme4 } from "react-native-paper
|
|
|
416
416
|
|
|
417
417
|
// src/components/Dialog.tsx
|
|
418
418
|
import React, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
419
|
-
import { Animated, BackHandler, Easing, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
419
|
+
import { Animated, BackHandler, Easing, Platform as Platform2, Pressable, StyleSheet as StyleSheet3, View as View3 } from "react-native";
|
|
420
420
|
import { Dialog as PaperDialog, Portal as Portal2, useTheme as useTheme3 } from "react-native-paper";
|
|
421
421
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
422
422
|
|
|
@@ -667,6 +667,8 @@ var BlurView = ({ blur = true, children, elevation, style, tintColor, tintOpacit
|
|
|
667
667
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
668
668
|
var ANIMATION_DURATION = 220;
|
|
669
669
|
var DIALOG_MARGIN = 32;
|
|
670
|
+
var AnimatedBlurView = Animated.createAnimatedComponent(BlurView);
|
|
671
|
+
var AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
670
672
|
function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, dismissableBackButton = dismissable, visible, onDismiss, children, style, testID = "dialog" }) {
|
|
671
673
|
const blur = useBlur(blurProp);
|
|
672
674
|
const reanimated = useReanimatedModule();
|
|
@@ -710,9 +712,16 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
710
712
|
return child;
|
|
711
713
|
});
|
|
712
714
|
const borderRadius = (isV3 ? 7 : 1) * roundness;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
715
|
+
const web = Platform2.OS === "web";
|
|
716
|
+
const fade = { opacity };
|
|
717
|
+
const styleOpacity = StyleSheet3.flatten(style)?.opacity;
|
|
718
|
+
const cardFade = typeof styleOpacity === "number" ? { opacity: Animated.multiply(opacity, styleOpacity) } : fade;
|
|
719
|
+
const Backdrop = web ? AnimatedPressable : Pressable;
|
|
720
|
+
const Card = web ? AnimatedBlurView : BlurView;
|
|
721
|
+
const card = /* @__PURE__ */ jsx6(Card, { blur, style: [styles2.card, { borderRadius }, style, web && cardFade], testID: `${testID}-surface`, children: content });
|
|
722
|
+
return /* @__PURE__ */ jsx6(Portal2, { children: /* @__PURE__ */ jsxs4(Animated.View, { accessibilityLiveRegion: "polite", accessibilityViewIsModal: true, pointerEvents: visible ? "box-none" : "none", style: [StyleSheet3.absoluteFill, !web && fade], testID, children: [
|
|
723
|
+
/* @__PURE__ */ jsx6(Backdrop, { accessibilityLabel: "Close modal", accessibilityRole: "button", disabled: !dismissable, onPress: dismissable ? onDismiss : void 0, style: [StyleSheet3.absoluteFill, { backgroundColor: colors.backdrop }, web && fade], testID: `${testID}-backdrop` }),
|
|
724
|
+
/* @__PURE__ */ jsx6(View3, { pointerEvents: "box-none", style: [styles2.wrapper, { marginBottom: bottom, marginTop: top, paddingHorizontal: Math.max(left, right, DIALOG_MARGIN) }], testID: `${testID}-wrapper`, children: reanimated && animatedStyle ? /* @__PURE__ */ jsx6(reanimated.View, { style: animatedStyle, testID: `${testID}-animated-wrapper`, children: card }) : card })
|
|
716
725
|
] }) });
|
|
717
726
|
}
|
|
718
727
|
DialogComponent.Title = PaperDialog.Title;
|
|
@@ -881,7 +890,7 @@ var AutoPalettePicker = (props) => {
|
|
|
881
890
|
|
|
882
891
|
// src/components/BottomNavigation.tsx
|
|
883
892
|
import { useEffect as useEffect4 } from "react";
|
|
884
|
-
import { Platform as
|
|
893
|
+
import { Platform as Platform3 } from "react-native";
|
|
885
894
|
import { BottomNavigation as PaperBottomNavigation, useTheme as useTheme6 } from "react-native-paper";
|
|
886
895
|
|
|
887
896
|
// src/navigation-bar.ts
|
|
@@ -896,7 +905,7 @@ function useNavigationBarSync() {
|
|
|
896
905
|
const theme = useTheme6();
|
|
897
906
|
const { navigationBar, onNavBarChange } = useNavBarContext();
|
|
898
907
|
useEffect4(() => {
|
|
899
|
-
if (
|
|
908
|
+
if (Platform3.OS !== "android") return;
|
|
900
909
|
if (onNavBarChange) onNavBarChange(theme.colors.surface, theme.dark);
|
|
901
910
|
else if (navigationBar) setNavigationBarStyle(navigationBar, theme.dark);
|
|
902
911
|
}, [theme, onNavBarChange, navigationBar]);
|
|
@@ -953,18 +962,38 @@ function IconButton({ variant, iconColor, containerColor, ...props }) {
|
|
|
953
962
|
}
|
|
954
963
|
|
|
955
964
|
// src/components/Menu.tsx
|
|
956
|
-
import {
|
|
957
|
-
import {
|
|
965
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
966
|
+
import { Animated as Animated2, Easing as Easing2, Platform as Platform4, StyleSheet as StyleSheet6 } from "react-native";
|
|
967
|
+
import { Menu as PaperMenu, useTheme as useTheme9 } from "react-native-paper";
|
|
958
968
|
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
969
|
+
var ANIMATION_DURATION2 = 250;
|
|
970
|
+
var EASING = Easing2.bezier(0.4, 0, 0.2, 1);
|
|
971
|
+
var DEFAULT_ELEVATION = 2;
|
|
972
|
+
var SHADOW_HEIGHT = [0, 1, 2, 4, 6, 8];
|
|
973
|
+
var SHADOW_RADIUS = [0, 3, 6, 8, 10, 12];
|
|
974
|
+
var AnimatedBlurView2 = Animated2.createAnimatedComponent(BlurView);
|
|
975
|
+
var FadingBlurView = ({ blur, children, elevation, theme: themeOverrides, visible }) => {
|
|
976
|
+
const { animation, roundness } = useTheme9(themeOverrides);
|
|
977
|
+
const [opacity] = useState6(() => new Animated2.Value(0));
|
|
978
|
+
const [closing, setClosing] = useState6(false);
|
|
979
|
+
if (!visible && !closing) setClosing(true);
|
|
980
|
+
useEffect5(() => {
|
|
981
|
+
Animated2.timing(opacity, { duration: animation.scale * ANIMATION_DURATION2, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start();
|
|
982
|
+
}, [animation.scale, closing, opacity]);
|
|
983
|
+
const shadow = elevation > 0 ? { shadowColor: "#000000", shadowOffset: { height: SHADOW_HEIGHT[elevation], width: 0 }, shadowOpacity: 0.3, shadowRadius: SHADOW_RADIUS[elevation] } : null;
|
|
984
|
+
return /* @__PURE__ */ jsx16(AnimatedBlurView2, { blur, style: [{ borderRadius: roundness, opacity }, shadow], children });
|
|
985
|
+
};
|
|
959
986
|
var MenuComponent = ({ blur: blurProp, children, ...props }) => {
|
|
960
987
|
const blur = useBlur(blurProp);
|
|
961
|
-
|
|
988
|
+
const web = Platform4.OS === "web";
|
|
989
|
+
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 }) });
|
|
962
990
|
};
|
|
963
991
|
var Menu = Object.assign(MenuComponent, {
|
|
964
992
|
Item: PaperMenu.Item
|
|
965
993
|
});
|
|
966
994
|
var styles5 = StyleSheet6.create({
|
|
967
|
-
content: { backgroundColor: "transparent", paddingVertical: 0 }
|
|
995
|
+
content: { backgroundColor: "transparent", paddingVertical: 0 },
|
|
996
|
+
webSurface: { opacity: 1, shadowOpacity: 0 }
|
|
968
997
|
});
|
|
969
998
|
|
|
970
999
|
// src/components/TextInput.tsx
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { type ComponentType, type ReactNode, useEffect, useState } from 'react'
|
|
2
|
-
import { Animated, BackHandler, Easing, Pressable, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
2
|
+
import { Animated, BackHandler, Easing, Platform, Pressable, type StyleProp, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
3
3
|
import { Dialog as PaperDialog, type DialogActionsProps, type DialogContentProps, type DialogProps as PaperDialogProps, type DialogScrollAreaProps, type DialogTitleProps, Portal, useTheme } from 'react-native-paper'
|
|
4
4
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
5
5
|
|
|
@@ -39,6 +39,9 @@ const ANIMATION_DURATION = 220
|
|
|
39
39
|
// Minimum breathing room between the card and the screen edge, clamped up from safe-area insets.
|
|
40
40
|
const DIALOG_MARGIN = 32
|
|
41
41
|
|
|
42
|
+
const AnimatedBlurView = Animated.createAnimatedComponent(BlurView)
|
|
43
|
+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
|
|
44
|
+
|
|
42
45
|
function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, dismissableBackButton = dismissable, visible, onDismiss, children, style, testID = 'dialog' }: DialogProps) {
|
|
43
46
|
const blur = useBlur(blurProp)
|
|
44
47
|
const reanimated = useReanimatedModule()
|
|
@@ -93,21 +96,34 @@ function DialogComponent({ animatedStyle, blur: blurProp, dismissable = true, di
|
|
|
93
96
|
|
|
94
97
|
const borderRadius = (isV3 ? 7 : 1) * roundness
|
|
95
98
|
|
|
99
|
+
// On web, an ancestor below opacity 1 cuts the card's CSS backdrop-filter off from the page behind
|
|
100
|
+
// it, and Chromium doesn't restore the blur once that ancestor fades back up to 1, so fading the
|
|
101
|
+
// whole layer would leave the open card see-through. There the fade runs on the backdrop and on the
|
|
102
|
+
// card (the backdrop-filter element) itself instead, off the same value. A numeric opacity in the
|
|
103
|
+
// card's own style still applies there, multiplied into the fade.
|
|
104
|
+
const web = Platform.OS === 'web'
|
|
105
|
+
const fade = { opacity }
|
|
106
|
+
const styleOpacity = StyleSheet.flatten(style)?.opacity
|
|
107
|
+
const cardFade = typeof styleOpacity === 'number' ? { opacity: Animated.multiply(opacity, styleOpacity) } : fade
|
|
108
|
+
const Backdrop = web ? AnimatedPressable : Pressable
|
|
109
|
+
const Card = web ? AnimatedBlurView : BlurView
|
|
110
|
+
const card = (
|
|
111
|
+
<Card blur={blur} style={[styles.card, { borderRadius }, style, web && cardFade]} testID={`${testID}-surface`}>
|
|
112
|
+
{content}
|
|
113
|
+
</Card>
|
|
114
|
+
)
|
|
115
|
+
|
|
96
116
|
return (
|
|
97
117
|
<Portal>
|
|
98
|
-
<Animated.View accessibilityLiveRegion='polite' accessibilityViewIsModal pointerEvents={visible ? 'box-none' : 'none'} style={[StyleSheet.absoluteFill,
|
|
99
|
-
<
|
|
118
|
+
<Animated.View accessibilityLiveRegion='polite' accessibilityViewIsModal pointerEvents={visible ? 'box-none' : 'none'} style={[StyleSheet.absoluteFill, !web && fade]} testID={testID}>
|
|
119
|
+
<Backdrop accessibilityLabel='Close modal' accessibilityRole='button' disabled={!dismissable} onPress={dismissable ? onDismiss : undefined} style={[StyleSheet.absoluteFill, { backgroundColor: colors.backdrop }, web && fade]} testID={`${testID}-backdrop`} />
|
|
100
120
|
<View pointerEvents='box-none' style={[styles.wrapper, { marginBottom: bottom, marginTop: top, paddingHorizontal: Math.max(left, right, DIALOG_MARGIN) }]} testID={`${testID}-wrapper`}>
|
|
101
121
|
{reanimated && animatedStyle ? (
|
|
102
122
|
<reanimated.View style={animatedStyle} testID={`${testID}-animated-wrapper`}>
|
|
103
|
-
|
|
104
|
-
{content}
|
|
105
|
-
</BlurView>
|
|
123
|
+
{card}
|
|
106
124
|
</reanimated.View>
|
|
107
125
|
) : (
|
|
108
|
-
|
|
109
|
-
{content}
|
|
110
|
-
</BlurView>
|
|
126
|
+
card
|
|
111
127
|
)}
|
|
112
128
|
</View>
|
|
113
129
|
</Animated.View>
|
package/src/components/Menu.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { StyleSheet } from 'react-native'
|
|
3
|
-
import { Menu as PaperMenu, type MenuItemProps, type MenuProps as PaperMenuProps } from 'react-native-paper'
|
|
1
|
+
import React, { type ReactNode, useEffect, useState } from 'react'
|
|
2
|
+
import { Animated, Easing, Platform, StyleSheet } from 'react-native'
|
|
3
|
+
import { type MD2Theme, type MD3Theme, Menu as PaperMenu, type MenuItemProps, type MenuProps as PaperMenuProps, useTheme } from 'react-native-paper'
|
|
4
4
|
|
|
5
5
|
import { useBlur } from '../BlurContext'
|
|
6
6
|
import { BlurView } from './BlurView'
|
|
@@ -9,11 +9,61 @@ export type MenuProps = PaperMenuProps & {
|
|
|
9
9
|
blur?: boolean
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// Mirror react-native-paper's Menu: its fade (ANIMATION_DURATION * theme.animation.scale, eased with
|
|
13
|
+
// EASING), its default elevation, and the MD3 shadow its Surface draws at each elevation level.
|
|
14
|
+
const ANIMATION_DURATION = 250
|
|
15
|
+
const EASING = Easing.bezier(0.4, 0, 0.2, 1)
|
|
16
|
+
const DEFAULT_ELEVATION = 2
|
|
17
|
+
const SHADOW_HEIGHT = [0, 1, 2, 4, 6, 8]
|
|
18
|
+
const SHADOW_RADIUS = [0, 3, 6, 8, 10, 12]
|
|
19
|
+
|
|
20
|
+
const AnimatedBlurView = Animated.createAnimatedComponent(BlurView)
|
|
21
|
+
|
|
22
|
+
type FadingBlurViewProps = {
|
|
23
|
+
blur: boolean
|
|
24
|
+
children: ReactNode
|
|
25
|
+
elevation: number
|
|
26
|
+
theme: PaperMenuProps['theme']
|
|
27
|
+
visible: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Paper fades the menu's Surface in and out. On web, an ancestor below opacity 1 cuts BlurView's CSS
|
|
31
|
+
// backdrop-filter off from the page behind it, and Chromium doesn't restore the blur once the Surface
|
|
32
|
+
// reaches 1, so the open menu stays see-through until something else repaints it. So on web the Surface
|
|
33
|
+
// is pinned at opacity 1 with its shadow hidden, and BlurView itself (the backdrop-filter element) runs
|
|
34
|
+
// Paper's fades and draws that shadow, so the two still fade together.
|
|
35
|
+
const FadingBlurView = ({ blur, children, elevation, theme: themeOverrides, visible }: FadingBlurViewProps) => {
|
|
36
|
+
const { animation, roundness } = useTheme<MD2Theme | MD3Theme>(themeOverrides)
|
|
37
|
+
const [opacity] = useState(() => new Animated.Value(0))
|
|
38
|
+
// Once Paper starts hiding the menu it unmounts this content when that fade-out ends, even if visible
|
|
39
|
+
// turns back on first, so a close is final here too rather than fading back in only to vanish.
|
|
40
|
+
const [closing, setClosing] = useState(false)
|
|
41
|
+
if (!visible && !closing) setClosing(true)
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
Animated.timing(opacity, { duration: animation.scale * ANIMATION_DURATION, easing: EASING, toValue: closing ? 0 : 1, useNativeDriver: false }).start()
|
|
45
|
+
}, [animation.scale, closing, opacity])
|
|
46
|
+
|
|
47
|
+
const shadow = elevation > 0 ? { shadowColor: '#000000', shadowOffset: { height: SHADOW_HEIGHT[elevation], width: 0 }, shadowOpacity: 0.3, shadowRadius: SHADOW_RADIUS[elevation] } : null
|
|
48
|
+
return (
|
|
49
|
+
<AnimatedBlurView blur={blur} style={[{ borderRadius: roundness, opacity }, shadow]}>
|
|
50
|
+
{children}
|
|
51
|
+
</AnimatedBlurView>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
12
55
|
const MenuComponent = ({ blur: blurProp, children, ...props }: MenuProps) => {
|
|
13
56
|
const blur = useBlur(blurProp)
|
|
57
|
+
const web = Platform.OS === 'web'
|
|
14
58
|
return (
|
|
15
|
-
<PaperMenu {...props} contentStyle={[styles.content, props.contentStyle]}>
|
|
16
|
-
|
|
59
|
+
<PaperMenu {...props} contentStyle={[styles.content, web && styles.webSurface, props.contentStyle]}>
|
|
60
|
+
{web ? (
|
|
61
|
+
<FadingBlurView blur={blur} elevation={props.mode === 'flat' ? 0 : (props.elevation ?? DEFAULT_ELEVATION)} theme={props.theme} visible={props.visible}>
|
|
62
|
+
{children}
|
|
63
|
+
</FadingBlurView>
|
|
64
|
+
) : (
|
|
65
|
+
<BlurView blur={blur}>{children}</BlurView>
|
|
66
|
+
)}
|
|
17
67
|
</PaperMenu>
|
|
18
68
|
)
|
|
19
69
|
}
|
|
@@ -23,5 +73,6 @@ export const Menu = Object.assign(MenuComponent, {
|
|
|
23
73
|
})
|
|
24
74
|
|
|
25
75
|
const styles = StyleSheet.create({
|
|
26
|
-
content: { backgroundColor: 'transparent', paddingVertical: 0 }
|
|
76
|
+
content: { backgroundColor: 'transparent', paddingVertical: 0 },
|
|
77
|
+
webSurface: { opacity: 1, shadowOpacity: 0 }
|
|
27
78
|
})
|