@momo-kits/foundation 0.168.1-rn862.1 → 0.169.2
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/Application/BottomSheet.hooks.ts +195 -0
- package/Application/BottomSheet.parts.tsx +147 -0
- package/Application/BottomSheet.styles.ts +43 -0
- package/Application/BottomSheet.tsx +76 -279
- package/Application/BottomTab/ActiveIndicator.tsx +90 -0
- package/Application/BottomTab/BottomTabBar.styles.ts +65 -0
- package/Application/BottomTab/BottomTabBar.tsx +229 -276
- package/Application/BottomTab/BottomTabInset.tsx +14 -0
- package/Application/BottomTab/CurvedContainer.tsx +153 -0
- package/Application/BottomTab/CustomBottomTabItem.tsx +132 -108
- package/Application/BottomTab/DuoBarCompression.tsx +4 -12
- package/Application/BottomTab/DuoRailTabBar.tsx +227 -0
- package/Application/BottomTab/DuoTabBar.styles.ts +81 -0
- package/Application/BottomTab/DuoTabBar.tsx +29 -495
- package/Application/BottomTab/TabBarIcon.tsx +160 -72
- package/Application/BottomTab/TabScreen.tsx +164 -0
- package/Application/BottomTab/index.tsx +84 -351
- package/Application/BottomTab/metrics.ts +164 -0
- package/Application/BottomTab/tabItems.ts +46 -0
- package/Application/BottomTab/usePressLift.ts +32 -0
- package/Application/Components/DuoGlassBackground.tsx +7 -9
- package/Application/Components/HeaderAnimated.tsx +1 -1
- package/Application/Components/HeaderBackground.tsx +1 -1
- package/Application/Components/HeaderExtendHeader.tsx +17 -11
- package/Application/Components/HeaderRight.styles.ts +144 -0
- package/Application/Components/HeaderRight.tsx +6 -528
- package/Application/Components/HeaderTitle.shared.ts +43 -0
- package/Application/Components/HeaderTitle.styles.ts +125 -0
- package/Application/Components/HeaderTitle.tsx +12 -576
- package/Application/Components/HeaderToolkitAction.tsx +208 -0
- package/Application/Components/NavigationButton.tsx +9 -6
- package/Application/Components/SearchHeader.tsx +1 -1
- package/Application/Components/SwipeBackHaptic.tsx +95 -0
- package/Application/Components/TitleJourney.tsx +110 -0
- package/Application/Components/TitleLocation.tsx +73 -0
- package/Application/Components/TitleUser.tsx +304 -0
- package/Application/Components/useToolkitActions.tsx +233 -0
- package/Application/ModalIconButton.tsx +66 -0
- package/Application/ModalScreen.tsx +39 -18
- package/Application/Navigation.ts +1 -1
- package/Application/NavigationContainer.tsx +7 -8
- package/Application/StackScreen.tracking.ts +158 -0
- package/Application/StackScreen.tsx +10 -137
- package/Application/WidgetContainer.tsx +10 -3
- package/Application/__tests__/modalLayout.test.ts +128 -0
- package/Application/hooks.ts +137 -0
- package/Application/index.ts +13 -1
- package/Application/modalLayout.ts +177 -0
- package/Application/navigationOptions.tsx +217 -0
- package/Application/types.header.ts +116 -0
- package/Application/types.tabs.ts +48 -0
- package/Application/types.theme.ts +87 -0
- package/Application/types.ts +12 -244
- package/Application/utils.ts +107 -0
- package/Badge/BadgeRibbon.tsx +3 -3
- package/Badge/styles.ts +2 -0
- package/Button/index.tsx +16 -136
- package/Button/styles.ts +1 -1
- package/Button/utils.ts +139 -0
- package/CheckBox/index.tsx +1 -6
- package/Divider/DashDivider.tsx +10 -11
- package/Divider/index.tsx +18 -8
- package/Image/index.tsx +1 -46
- package/Input/Input.tsx +7 -8
- package/Input/InputDropDown.tsx +3 -4
- package/Input/InputMoney.tsx +38 -164
- package/Input/InputMoneyView.tsx +187 -0
- package/Input/InputOTP.tsx +1 -1
- package/Input/InputPhoneNumber.tsx +5 -1
- package/Input/InputSearch.tsx +2 -108
- package/Input/InputTextArea.tsx +1 -5
- package/Input/SearchTextTyping.tsx +104 -0
- package/Input/common.tsx +4 -4
- package/Input/moneyUtils.ts +17 -0
- package/Input/styles.ts +26 -2
- package/Layout/FloatingButton.tsx +22 -21
- package/Layout/ReservedRegions.tsx +21 -4
- package/Layout/Screen.tsx +74 -670
- package/Layout/Section.tsx +1 -2
- package/Layout/index.ts +10 -4
- package/Layout/layoutMetrics.ts +58 -0
- package/Layout/layoutTypes.ts +86 -0
- package/Layout/screenContent.tsx +90 -0
- package/Layout/screenFooter.tsx +53 -0
- package/Layout/screenHeader.tsx +315 -0
- package/Layout/screenQuickReturn.tsx +67 -0
- package/Layout/screenScroll.ts +110 -0
- package/Layout/screenTypes.ts +134 -0
- package/Layout/styles.ts +2 -0
- package/Layout/utils.ts +10 -144
- package/Loader/DotLoader.tsx +7 -3
- package/Loader/ProgressBar.tsx +2 -6
- package/Loader/Spinner.tsx +7 -3
- package/Loader/styles.ts +4 -0
- package/Popup/PopupNotify.tsx +70 -64
- package/Popup/PopupPromotion.tsx +5 -4
- package/Popup/types.ts +7 -0
- package/Popup/utils.ts +46 -24
- package/Radio/index.tsx +2 -10
- package/Radio/styles.ts +6 -0
- package/Skeleton/index.tsx +1 -9
- package/Skeleton/styles.ts +1 -0
- package/Tag/index.tsx +3 -3
- package/Text/index.tsx +11 -2
- package/Title/index.tsx +2 -9
- package/Title/styles.ts +6 -0
- package/package.json +21 -22
- package/Application/device.ts +0 -29
- package/Application/utils.tsx +0 -497
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { StackNavigationOptions } from '@react-navigation/stack';
|
|
3
|
+
import {
|
|
4
|
+
SharedValue,
|
|
5
|
+
useAnimatedReaction,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
withTiming,
|
|
8
|
+
} from 'react-native-reanimated';
|
|
9
|
+
import { DuoQuickReturnHeader } from '../Application/Components/DuoQuickReturnHeader';
|
|
10
|
+
|
|
11
|
+
export const useScreenQuickReturn = (
|
|
12
|
+
sharedValue: SharedValue<number>,
|
|
13
|
+
useQuickReturn: boolean,
|
|
14
|
+
heightHeader: number,
|
|
15
|
+
) => {
|
|
16
|
+
const headerHide = useSharedValue(0);
|
|
17
|
+
const lastScrollY = useSharedValue(0);
|
|
18
|
+
|
|
19
|
+
useAnimatedReaction(
|
|
20
|
+
() => sharedValue.value,
|
|
21
|
+
y => {
|
|
22
|
+
const delta = y - lastScrollY.value;
|
|
23
|
+
lastScrollY.value = y;
|
|
24
|
+
if (!useQuickReturn || y <= 0) {
|
|
25
|
+
headerHide.value = 0;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
headerHide.value = Math.min(
|
|
29
|
+
Math.max(headerHide.value + delta, 0),
|
|
30
|
+
heightHeader,
|
|
31
|
+
);
|
|
32
|
+
},
|
|
33
|
+
[useQuickReturn, heightHeader],
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const quickReturnOptions: StackNavigationOptions = useMemo(
|
|
37
|
+
() =>
|
|
38
|
+
useQuickReturn
|
|
39
|
+
? {
|
|
40
|
+
header: (props: any) => (
|
|
41
|
+
<DuoQuickReturnHeader {...props} offset={headerHide} />
|
|
42
|
+
),
|
|
43
|
+
}
|
|
44
|
+
: { header: undefined },
|
|
45
|
+
[useQuickReturn, headerHide],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* a half-shown quick return header settles fully in or out
|
|
50
|
+
*/
|
|
51
|
+
const snapQuickReturnHeader = (offsetY: number) => {
|
|
52
|
+
const hide = headerHide.value;
|
|
53
|
+
if (
|
|
54
|
+
!useQuickReturn ||
|
|
55
|
+
offsetY < heightHeader ||
|
|
56
|
+
hide <= 0 ||
|
|
57
|
+
hide >= heightHeader
|
|
58
|
+
) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
headerHide.value = withTiming(hide > heightHeader / 2 ? heightHeader : 0, {
|
|
62
|
+
duration: 200,
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return { quickReturnOptions, snapQuickReturnHeader };
|
|
67
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
NativeScrollEvent,
|
|
5
|
+
NativeSyntheticEvent,
|
|
6
|
+
ScrollViewProps,
|
|
7
|
+
StatusBar,
|
|
8
|
+
StatusBarStyle,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import { withTiming } from 'react-native-reanimated';
|
|
11
|
+
import Navigation from '../Application/Navigation';
|
|
12
|
+
import { AnimatedHeader, SearchHeaderProps } from '../Application/types';
|
|
13
|
+
import { Colors } from '../Consts';
|
|
14
|
+
|
|
15
|
+
type ScrollHandlerParams = {
|
|
16
|
+
scrollable: boolean;
|
|
17
|
+
animatedValue: Animated.Value;
|
|
18
|
+
sharedValue: any;
|
|
19
|
+
scrollViewProps?: ScrollViewProps;
|
|
20
|
+
animatedHeader?: AnimatedHeader;
|
|
21
|
+
inputSearchProps?: SearchHeaderProps;
|
|
22
|
+
currentTint: MutableRefObject<string | undefined>;
|
|
23
|
+
navigation?: Navigation;
|
|
24
|
+
snapQuickReturnHeader: (offsetY: number) => void;
|
|
25
|
+
ref: any;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const createScrollHandlers = ({
|
|
29
|
+
scrollable,
|
|
30
|
+
animatedValue,
|
|
31
|
+
sharedValue,
|
|
32
|
+
scrollViewProps,
|
|
33
|
+
animatedHeader,
|
|
34
|
+
inputSearchProps,
|
|
35
|
+
currentTint,
|
|
36
|
+
navigation,
|
|
37
|
+
snapQuickReturnHeader,
|
|
38
|
+
ref,
|
|
39
|
+
}: ScrollHandlerParams) => {
|
|
40
|
+
let handleScroll;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* animated when use scroll && animated value
|
|
44
|
+
*/
|
|
45
|
+
if (scrollable) {
|
|
46
|
+
handleScroll = Animated.event(
|
|
47
|
+
[
|
|
48
|
+
{
|
|
49
|
+
nativeEvent: {
|
|
50
|
+
contentOffset: { y: animatedValue as Animated.Value },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
{
|
|
55
|
+
useNativeDriver: true,
|
|
56
|
+
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
57
|
+
const offsetY = e.nativeEvent.contentOffset.y;
|
|
58
|
+
sharedValue.value = offsetY;
|
|
59
|
+
scrollViewProps?.onScroll?.(e);
|
|
60
|
+
if (animatedHeader) {
|
|
61
|
+
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
62
|
+
if (offsetY > 50) {
|
|
63
|
+
color = Colors.black_17;
|
|
64
|
+
}
|
|
65
|
+
if (color !== currentTint.current) {
|
|
66
|
+
currentTint.current = color;
|
|
67
|
+
navigation?.setOptions({
|
|
68
|
+
headerTintColor: color,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let barStyle: StatusBarStyle = 'dark-content';
|
|
72
|
+
if (currentTint.current === Colors.black_01) {
|
|
73
|
+
barStyle = 'light-content';
|
|
74
|
+
}
|
|
75
|
+
StatusBar.setBarStyle(barStyle, true);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* handle scroll end
|
|
85
|
+
* @param e
|
|
86
|
+
*/
|
|
87
|
+
const handleMomentumScrollEnd = (
|
|
88
|
+
e: NativeSyntheticEvent<NativeScrollEvent>,
|
|
89
|
+
) => {
|
|
90
|
+
snapQuickReturnHeader(e.nativeEvent.contentOffset.y);
|
|
91
|
+
scrollViewProps?.onMomentumScrollEnd?.(e);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
95
|
+
const offsetY = e.nativeEvent.contentOffset.y;
|
|
96
|
+
snapQuickReturnHeader(offsetY);
|
|
97
|
+
if (inputSearchProps && offsetY < 100 && offsetY > 0) {
|
|
98
|
+
Animated.timing(animatedValue, {
|
|
99
|
+
toValue: 0,
|
|
100
|
+
useNativeDriver: true,
|
|
101
|
+
duration: 300,
|
|
102
|
+
}).start();
|
|
103
|
+
sharedValue.value = withTiming(0, { duration: 300 });
|
|
104
|
+
ref?.scrollTo?.({ y: 0, animated: true });
|
|
105
|
+
}
|
|
106
|
+
scrollViewProps?.onScrollEndDrag?.(e);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return { handleScroll, handleScrollEnd, handleMomentumScrollEnd };
|
|
110
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React, { ReactNode, Ref } from 'react';
|
|
2
|
+
import { Animated, ScrollViewProps, ViewProps } from 'react-native';
|
|
3
|
+
import Navigation from '../Application/Navigation';
|
|
4
|
+
import {
|
|
5
|
+
AnimatedHeader,
|
|
6
|
+
ScreenTrackingParams,
|
|
7
|
+
SearchHeaderProps,
|
|
8
|
+
} from '../Application/types';
|
|
9
|
+
import { FloatingButtonProps } from './FloatingButton';
|
|
10
|
+
import { HeaderType } from './types';
|
|
11
|
+
import { InputRef } from '../Input';
|
|
12
|
+
|
|
13
|
+
export const ANIMATED_BANNER_HEIGHT = 210;
|
|
14
|
+
|
|
15
|
+
export interface ScreenProps extends ViewProps {
|
|
16
|
+
/**
|
|
17
|
+
* ReactNode representing the content of the screen.
|
|
18
|
+
*/
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Optional. Navigation object for handling navigation actions.
|
|
22
|
+
*/
|
|
23
|
+
navigation?: Navigation;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Optional. If `true`, enable keyboard avoiding view for the screen.
|
|
27
|
+
*/
|
|
28
|
+
enableKeyboardAvoidingView?: boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Optional. If `true`, the screen content is scrollable.
|
|
32
|
+
*/
|
|
33
|
+
scrollable?: boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional. Set type of header.
|
|
37
|
+
*/
|
|
38
|
+
headerType?: HeaderType;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Optional. Props for the animated component or bool used top navigation animated.
|
|
42
|
+
*/
|
|
43
|
+
animatedHeader?: AnimatedHeader;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Optional. Props for the underlying ScrollView component when `scrollable` is `true`.
|
|
47
|
+
*/
|
|
48
|
+
scrollViewProps?: ScrollViewProps;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Optional. ReactNode representing the header component of the screen.
|
|
52
|
+
*/
|
|
53
|
+
headerComponent?: ReactNode;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Optional. ReactNode representing the footer component of the screen.
|
|
57
|
+
*/
|
|
58
|
+
footerComponent?: ReactNode;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Optional. If `true`, the footer is inset by the standard horizontal margin
|
|
62
|
+
* (12pt each side). Set `false` for a footer that should span the full width —
|
|
63
|
+
* e.g. one that already carries its own horizontal padding.
|
|
64
|
+
* Defaults to `true`, on every platform.
|
|
65
|
+
*/
|
|
66
|
+
marginFooter?: boolean;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Optional. Background color of the screen.
|
|
70
|
+
*/
|
|
71
|
+
backgroundColor?: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Optional. layout card offset overlap header banner.
|
|
75
|
+
*/
|
|
76
|
+
layoutOffset?: -8 | -24 | -56 | number;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Optional. Width of the right header. Defaults to the toolkit width (73), or 0 when
|
|
80
|
+
* iPhone Duo floats the toolkit into its side bar.
|
|
81
|
+
*/
|
|
82
|
+
headerRightWidth?: 0 | 73 | 110 | number;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Optional. Props for the floating action button.
|
|
86
|
+
*/
|
|
87
|
+
floatingButtonProps?: Omit<FloatingButtonProps, 'animatedValue' | 'bottom'>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Optional. Keyboard vertical offset.
|
|
91
|
+
*/
|
|
92
|
+
keyboardVerticalOffset?: number;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Optional. If `true`, the screen content is using grid layout by span.
|
|
96
|
+
*/
|
|
97
|
+
useGridLayout?: boolean;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Optional. specs for input search.
|
|
101
|
+
*/
|
|
102
|
+
inputSearchProps?: SearchHeaderProps;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Optional. Ref for input search.
|
|
106
|
+
*/
|
|
107
|
+
inputSearchRef?: Ref<InputRef>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Optional. React Native animated value for header.
|
|
111
|
+
* Internally, Screen mirrors it into a reanimated shared value.
|
|
112
|
+
*/
|
|
113
|
+
animatedValue?: Animated.Value;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Optional. If `true`, use shadow header.
|
|
117
|
+
*/
|
|
118
|
+
useShadowHeader?: boolean;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Optional. Custom header Gradient Color.
|
|
122
|
+
*/
|
|
123
|
+
gradientColor?: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Optional. Custom headerBackground Image
|
|
127
|
+
*/
|
|
128
|
+
headerBackground?: string;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Optional. Custom tracking params
|
|
132
|
+
*/
|
|
133
|
+
trackingParams?: ScreenTrackingParams;
|
|
134
|
+
}
|
package/Layout/styles.ts
CHANGED
package/Layout/utils.ts
CHANGED
|
@@ -5,6 +5,13 @@ import {
|
|
|
5
5
|
SafeAreaInsetsContext,
|
|
6
6
|
} from 'react-native-safe-area-context';
|
|
7
7
|
import { Spacing } from '../Consts';
|
|
8
|
+
import { HeaderMetrics, DuoMetrics } from './layoutMetrics';
|
|
9
|
+
import {
|
|
10
|
+
ScreenLayout,
|
|
11
|
+
SideBarEdge,
|
|
12
|
+
SideBarLayout,
|
|
13
|
+
StatusBarClearance,
|
|
14
|
+
} from './layoutTypes';
|
|
8
15
|
import { ReservedRegion, ReservedRegionsContext } from './ReservedRegions';
|
|
9
16
|
|
|
10
17
|
/**
|
|
@@ -44,65 +51,6 @@ const validateChildren = (
|
|
|
44
51
|
}
|
|
45
52
|
};
|
|
46
53
|
|
|
47
|
-
/**
|
|
48
|
-
* Header geometry
|
|
49
|
-
* - barHeight: navigation bar row below the status bar
|
|
50
|
-
* - iPhoneMinHeight: header never shorter than this on iPhone (landscape, iPhone Duo)
|
|
51
|
-
* - statusBarMinInset: a smaller top inset means the status bar is not on the top edge
|
|
52
|
-
* - largeWidth / largeMargin: @react-navigation/elements Header gives its content row
|
|
53
|
-
* 5pt side margins on iOS from 414pt wide
|
|
54
|
-
*/
|
|
55
|
-
const HeaderMetrics = {
|
|
56
|
-
barHeight: 52,
|
|
57
|
-
iPhoneMinHeight: 72,
|
|
58
|
-
statusBarMinInset: 20,
|
|
59
|
-
largeWidth: 414,
|
|
60
|
-
largeMargin: 5,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* iPhone Duo geometry, iOS 27.1 (Xcode 27.1 simulator)
|
|
65
|
-
* - maxAspectRatio: both displays are near 1.42 (inner) and 1.45 (outer), every slab
|
|
66
|
-
* iPhone is 1.77 or taller
|
|
67
|
-
* - innerMinHeight: window height from which the inner display is in use (outer display
|
|
68
|
-
* is 466pt tall in landscape, inner display 669pt)
|
|
69
|
-
* - controlSize / itemPitch: vertical bar of 48pt circular controls, item groups as 48pt
|
|
70
|
-
* wide capsules at a 50pt pitch, 12pt from the strip's inner edge and 12pt apart
|
|
71
|
-
* - firstItemTop: with the status bar in the strip, the first control sits under the
|
|
72
|
-
* camera, clock and connectivity cluster (cluster ends near y 151 on the outer display,
|
|
73
|
-
* y 98 on the inner display in landscape)
|
|
74
|
-
* - outerHeaderHeight / outerCameraCenterY: on the outer display in portrait the header
|
|
75
|
-
* is 4pt taller than iPhoneMinHeight and its title lines up with the camera punchout
|
|
76
|
-
* (y 24–66 at the top of the strip)
|
|
77
|
-
* - edgeInset: on the outer display in landscape the strip holds no status bar, so its
|
|
78
|
-
* controls start this far from the top and stay this far above the bottom, unless a
|
|
79
|
-
* reserved region (the camera at either end) asks for more
|
|
80
|
-
* - innerStatusBarCenterY / innerStatusBarWidth: on the inner display in portrait the
|
|
81
|
-
* clock and connectivity cluster sits at the top right corner (x 550–643 of 669, clock
|
|
82
|
-
* digits centered on y 47); its reserved region, when reported, is taller than the glyphs
|
|
83
|
-
* - floatingTabBarHeight / floatingTabBarMargin / floatingTabBarInsetOverlap: the host
|
|
84
|
-
* app's iOS 26 system tab bar on the inner display in portrait, a 62pt Liquid Glass pill
|
|
85
|
-
* sized to its tabs with the QR as a pinned 62pt platter beside it, centered and at
|
|
86
|
-
* least 20pt from the side edges; it overlaps the home indicator inset by 13pt (21pt
|
|
87
|
-
* above the screen bottom with a 34pt inset)
|
|
88
|
-
*/
|
|
89
|
-
const DuoMetrics = {
|
|
90
|
-
maxAspectRatio: 1.6,
|
|
91
|
-
innerMinHeight: 600,
|
|
92
|
-
controlSize: 48,
|
|
93
|
-
itemPitch: 50,
|
|
94
|
-
firstItemTop: 170,
|
|
95
|
-
firstItemTopLandscape: 120,
|
|
96
|
-
outerHeaderHeight: 76,
|
|
97
|
-
outerCameraCenterY: 45,
|
|
98
|
-
edgeInset: 24,
|
|
99
|
-
innerStatusBarCenterY: 47,
|
|
100
|
-
innerStatusBarWidth: 120,
|
|
101
|
-
floatingTabBarHeight: 62,
|
|
102
|
-
floatingTabBarMargin: 20,
|
|
103
|
-
floatingTabBarInsetOverlap: 13,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
54
|
const NO_INSETS: EdgeInsets = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
107
55
|
|
|
108
56
|
/**
|
|
@@ -206,91 +154,6 @@ const getCenteredHeaderLayout = (centerY: number) => {
|
|
|
206
154
|
};
|
|
207
155
|
};
|
|
208
156
|
|
|
209
|
-
export type SideBarEdge = 'left' | 'right';
|
|
210
|
-
|
|
211
|
-
export type SideBarLayout = {
|
|
212
|
-
/**
|
|
213
|
-
* Edge holding the side bar, the one with the larger safe-area inset.
|
|
214
|
-
*/
|
|
215
|
-
edge: SideBarEdge;
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Width of the side bar column, the safe-area inset on that edge.
|
|
219
|
-
*/
|
|
220
|
-
width: number;
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Window y of the first side bar item, below the navigation bar and the side status bar.
|
|
224
|
-
*/
|
|
225
|
-
top: number;
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Room to keep free above the window bottom, clear of a camera at the bottom end.
|
|
229
|
-
*/
|
|
230
|
-
bottom: number;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
export type ScreenLayout = {
|
|
234
|
-
/**
|
|
235
|
-
* Window width, from useWindowDimensions.
|
|
236
|
-
*/
|
|
237
|
-
width: number;
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Window height, from useWindowDimensions.
|
|
241
|
-
*/
|
|
242
|
-
height: number;
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* `true` when running on iPhone Duo, outer or inner display.
|
|
246
|
-
*/
|
|
247
|
-
isIPhoneDuo: boolean;
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Raw safe-area insets.
|
|
251
|
-
*/
|
|
252
|
-
insets: EdgeInsets;
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Horizontal insets the screen content keeps clear of. 0 outside iPhone Duo.
|
|
256
|
-
*/
|
|
257
|
-
insetLeft: number;
|
|
258
|
-
insetRight: number;
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Width available to the screen content: window width minus the horizontal insets.
|
|
262
|
-
*/
|
|
263
|
-
contentWidth: number;
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Header height: the top safe-area inset plus the navigation bar row, at least 72pt on iPhone.
|
|
267
|
-
*/
|
|
268
|
-
headerHeight: number;
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Room above the navigation bar row for the status bar, the rest of headerHeight is
|
|
272
|
-
* the row, which centers the title vertically. 0 when the status bar is not on top.
|
|
273
|
-
*/
|
|
274
|
-
headerStatusBarHeight: number;
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Side column holding the back button and the toolkit, when iPhone Duo moves the
|
|
278
|
-
* bars to the side. `undefined` while the bars stay horizontal.
|
|
279
|
-
*/
|
|
280
|
-
sideBar?: SideBarLayout;
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Room the header row keeps from each side for the clock and battery clusters, when
|
|
284
|
-
* iPhone Duo puts the row on the status bar (inner display in portrait).
|
|
285
|
-
*/
|
|
286
|
-
statusBarClearance?: StatusBarClearance;
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
export type StatusBarClearance = {
|
|
290
|
-
left: number;
|
|
291
|
-
right: number;
|
|
292
|
-
};
|
|
293
|
-
|
|
294
157
|
/**
|
|
295
158
|
* Detect iPhone Duo from the window shape and the safe area: a phone idiom window
|
|
296
159
|
* near square, or a phone with asymmetric side insets (slab iPhones report equal
|
|
@@ -507,9 +370,12 @@ const useGridSystem = () => {
|
|
|
507
370
|
return { numberOfColumns };
|
|
508
371
|
};
|
|
509
372
|
|
|
373
|
+
export type { ScreenLayout, SideBarEdge, SideBarLayout, StatusBarClearance };
|
|
374
|
+
|
|
510
375
|
export {
|
|
511
376
|
DuoMetrics,
|
|
512
377
|
HeaderMetrics,
|
|
378
|
+
InnerEdgeStyles,
|
|
513
379
|
getHeaderMargin,
|
|
514
380
|
getHeaderRowOptions,
|
|
515
381
|
useGridSystem,
|
package/Loader/DotLoader.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import { Platform } from 'react-native';
|
|
2
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
3
3
|
import AnimatedLottieView from 'lottie-react-native';
|
|
4
4
|
import animation from '../Assets/DotAnimation.json';
|
|
5
5
|
import { ApplicationContext } from '../Context';
|
|
@@ -29,11 +29,15 @@ const DotLoader: FC<LoaderProps> = ({ color, style }) => {
|
|
|
29
29
|
return (
|
|
30
30
|
<AnimatedLottieView
|
|
31
31
|
source={source}
|
|
32
|
-
style={[
|
|
33
|
-
webStyle={Platform.OS === 'web' ?
|
|
32
|
+
style={[styles.size, style]}
|
|
33
|
+
webStyle={Platform.OS === 'web' ? styles.size : undefined}
|
|
34
34
|
autoPlay
|
|
35
35
|
/>
|
|
36
36
|
);
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const styles = StyleSheet.create({
|
|
40
|
+
size: { width: 52, height: 18 },
|
|
41
|
+
});
|
|
42
|
+
|
|
39
43
|
export default DotLoader;
|
package/Loader/ProgressBar.tsx
CHANGED
|
@@ -8,7 +8,6 @@ import Animated, {
|
|
|
8
8
|
import styles from './styles';
|
|
9
9
|
import { ProgressBarProps } from './types';
|
|
10
10
|
import { ApplicationContext } from '../Context';
|
|
11
|
-
import { Radius } from '../Consts';
|
|
12
11
|
|
|
13
12
|
const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
14
13
|
const { theme } = useContext(ApplicationContext);
|
|
@@ -32,11 +31,8 @@ const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
|
32
31
|
>
|
|
33
32
|
<Animated.View
|
|
34
33
|
style={[
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
borderRadius: Radius.XXS,
|
|
38
|
-
backgroundColor: theme.colors.primary,
|
|
39
|
-
},
|
|
34
|
+
styles.progressBar,
|
|
35
|
+
{ backgroundColor: theme.colors.primary },
|
|
40
36
|
animatedStyle,
|
|
41
37
|
]}
|
|
42
38
|
/>
|
package/Loader/Spinner.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import { Platform } from 'react-native';
|
|
2
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
3
3
|
import AnimatedLottieView from 'lottie-react-native';
|
|
4
4
|
import animation from '../Assets/SpinnerAnimation.json';
|
|
5
5
|
import { hexToRGBA } from './utils';
|
|
@@ -21,11 +21,15 @@ const Spinner: FC<LoaderProps> = ({ color, style }) => {
|
|
|
21
21
|
return (
|
|
22
22
|
<AnimatedLottieView
|
|
23
23
|
source={source}
|
|
24
|
-
style={[
|
|
25
|
-
webStyle={Platform.OS === 'web' ?
|
|
24
|
+
style={[styles.size, style]}
|
|
25
|
+
webStyle={Platform.OS === 'web' ? styles.size : undefined}
|
|
26
26
|
autoPlay
|
|
27
27
|
/>
|
|
28
28
|
);
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
const styles = StyleSheet.create({
|
|
32
|
+
size: { width: 24, height: 24 },
|
|
33
|
+
});
|
|
34
|
+
|
|
31
35
|
export default Spinner;
|