@momo-kits/foundation 0.169.5-beta.1 → 0.169.6-beta.1
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.tsx +5 -11
- package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
- package/Application/BottomTab/BottomTabBar.tsx +11 -25
- package/Application/BottomTab/CurvedContainer.tsx +8 -2
- package/Application/BottomTab/CustomBottomTabItem.tsx +9 -8
- package/Application/BottomTab/DuoRailTabBar.tsx +0 -4
- package/Application/BottomTab/DuoTabBar.tsx +15 -4
- package/Application/BottomTab/metrics.ts +26 -3
- package/Application/ModalScreen.tsx +1 -7
- package/Application/NavigationContainer.tsx +0 -2
- package/Application/StackScreen.tsx +1 -4
- package/Application/types.tabs.ts +0 -1
- package/package.json +1 -1
- package/Application/KitAdoption/AdoptionBadge.tsx +0 -119
- package/Application/KitAdoption/AdoptionProbe.tsx +0 -145
- package/Application/KitAdoption/AdoptionStore.ts +0 -80
- package/Application/KitAdoption/Authorship/utils.ts +0 -137
- package/Application/KitAdoption/constants.ts +0 -118
- package/Application/KitAdoption/index.ts +0 -5
- package/Application/KitAdoption/styles.ts +0 -55
- package/Application/KitAdoption/types.ts +0 -48
- package/Application/KitAdoption/utils.ts +0 -375
- package/Application/__tests__/KitAdoption.integration.test.tsx +0 -235
- package/Application/__tests__/KitAdoption.test.ts +0 -283
- package/Application/__tests__/KitAdoptionAuthorship.test.ts +0 -132
- package/Application/__tests__/KitAdoptionStore.test.ts +0 -110
|
@@ -18,7 +18,6 @@ import Animated, {
|
|
|
18
18
|
} from 'react-native-reanimated';
|
|
19
19
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
20
20
|
import { DUO_ORIENTATIONS, styles } from './BottomSheet.styles';
|
|
21
|
-
import { AdoptionProbe } from './KitAdoption';
|
|
22
21
|
import {
|
|
23
22
|
useBottomSheetLayout,
|
|
24
23
|
useDuoKeyboardLift,
|
|
@@ -174,16 +173,11 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
174
173
|
);
|
|
175
174
|
|
|
176
175
|
const screen = (
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{...props}
|
|
183
|
-
{...props.route.params}
|
|
184
|
-
onRequestClose={onRequestClose}
|
|
185
|
-
/>
|
|
186
|
-
</AdoptionProbe>
|
|
176
|
+
<Screen
|
|
177
|
+
{...props}
|
|
178
|
+
{...props.route.params}
|
|
179
|
+
onRequestClose={onRequestClose}
|
|
180
|
+
/>
|
|
187
181
|
);
|
|
188
182
|
|
|
189
183
|
return (
|
|
@@ -2,6 +2,20 @@ import { StyleSheet } from 'react-native';
|
|
|
2
2
|
import { Colors, Spacing } from '../../Consts';
|
|
3
3
|
import { CTA_SHADOW, TabBarMetrics } from './metrics';
|
|
4
4
|
|
|
5
|
+
const ITEM_HEIGHT =
|
|
6
|
+
TabBarMetrics.indicatorHeight +
|
|
7
|
+
TabBarMetrics.itemIconLabelGap +
|
|
8
|
+
TabBarMetrics.labelHeight;
|
|
9
|
+
const CONTENT_HEIGHT = TabBarMetrics.height - 2 * TabBarMetrics.paddingVertical;
|
|
10
|
+
// the items are centered in the content, so their label starts this far below the content top
|
|
11
|
+
const ITEM_LABEL_TOP =
|
|
12
|
+
(CONTENT_HEIGHT - ITEM_HEIGHT) / 2 +
|
|
13
|
+
TabBarMetrics.indicatorHeight +
|
|
14
|
+
TabBarMetrics.itemIconLabelGap;
|
|
15
|
+
// the center action column is placed so its label starts on the same line as the tab labels
|
|
16
|
+
const CTA_TOP =
|
|
17
|
+
ITEM_LABEL_TOP - TabBarMetrics.ctaLabelGap - TabBarMetrics.ctaSize;
|
|
18
|
+
|
|
5
19
|
export const styles = StyleSheet.create({
|
|
6
20
|
bar: {
|
|
7
21
|
position: 'absolute',
|
|
@@ -37,7 +51,7 @@ export const styles = StyleSheet.create({
|
|
|
37
51
|
position: 'absolute',
|
|
38
52
|
left: 0,
|
|
39
53
|
right: 0,
|
|
40
|
-
top:
|
|
54
|
+
top: CTA_TOP,
|
|
41
55
|
alignItems: 'center',
|
|
42
56
|
gap: TabBarMetrics.ctaLabelGap,
|
|
43
57
|
},
|
|
@@ -46,6 +60,9 @@ export const styles = StyleSheet.create({
|
|
|
46
60
|
width: TabBarMetrics.ctaSize,
|
|
47
61
|
height: TabBarMetrics.ctaSize,
|
|
48
62
|
borderRadius: TabBarMetrics.ctaSize / 2,
|
|
63
|
+
// Figma Icon Button border: white 60% hairline
|
|
64
|
+
borderWidth: 1,
|
|
65
|
+
borderColor: `rgba(255, 255, 255, ${TabBarMetrics.ctaBorderOpacity})`,
|
|
49
66
|
alignItems: 'center',
|
|
50
67
|
justifyContent: 'center',
|
|
51
68
|
},
|
|
@@ -17,7 +17,6 @@ import { CurvedContainer } from './CurvedContainer';
|
|
|
17
17
|
import { ActiveIndicator } from './ActiveIndicator';
|
|
18
18
|
import { getHorizontalBarWidth, TabBarMetrics, TabBarMotion } from './metrics';
|
|
19
19
|
import { getTabItems, TabItem } from './tabItems';
|
|
20
|
-
import { FixedScaleText } from './TabBarIcon';
|
|
21
20
|
import { styles } from './BottomTabBar.styles';
|
|
22
21
|
|
|
23
22
|
type Props = BottomTabBarProps & {
|
|
@@ -68,8 +67,7 @@ const useCenterActionPress = () => {
|
|
|
68
67
|
*/
|
|
69
68
|
const CenterAction: React.FC<{
|
|
70
69
|
floatingButton: FloatingButtonProps;
|
|
71
|
-
|
|
72
|
-
}> = ({ floatingButton, fixedFontScale }) => {
|
|
70
|
+
}> = ({ floatingButton }) => {
|
|
73
71
|
const { theme } = useContext(ApplicationContext);
|
|
74
72
|
const context = useContext<any>(MiniAppContext);
|
|
75
73
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
@@ -113,11 +111,7 @@ const CenterAction: React.FC<{
|
|
|
113
111
|
)}
|
|
114
112
|
</Animated.View>
|
|
115
113
|
<View style={styles.ctaLabel}>
|
|
116
|
-
{
|
|
117
|
-
<FixedScaleText>{labelText}</FixedScaleText>
|
|
118
|
-
) : (
|
|
119
|
-
labelText
|
|
120
|
-
)}
|
|
114
|
+
{labelText}
|
|
121
115
|
{labelContent && (
|
|
122
116
|
<View pointerEvents={'none'} style={styles.ctaLabelOverlay}>
|
|
123
117
|
{labelContent}
|
|
@@ -139,14 +133,12 @@ const HorizontalTabBar: React.FC<{
|
|
|
139
133
|
activeTintColor: string;
|
|
140
134
|
inactiveTintColor: string;
|
|
141
135
|
floatingButton?: FloatingButtonProps;
|
|
142
|
-
fixedFontScale?: boolean;
|
|
143
136
|
}> = ({
|
|
144
137
|
items,
|
|
145
138
|
width,
|
|
146
139
|
activeTintColor,
|
|
147
140
|
inactiveTintColor,
|
|
148
141
|
floatingButton,
|
|
149
|
-
fixedFontScale,
|
|
150
142
|
}) => {
|
|
151
143
|
const withCta = hasCenterAction(floatingButton, items.length);
|
|
152
144
|
const [layouts, setLayouts] = useState<
|
|
@@ -173,7 +165,6 @@ const HorizontalTabBar: React.FC<{
|
|
|
173
165
|
onLongPress={item.onLongPress}
|
|
174
166
|
activeTintColor={activeTintColor}
|
|
175
167
|
inactiveTintColor={inactiveTintColor}
|
|
176
|
-
fixedFontScale={fixedFontScale}
|
|
177
168
|
/>
|
|
178
169
|
));
|
|
179
170
|
|
|
@@ -183,10 +174,7 @@ const HorizontalTabBar: React.FC<{
|
|
|
183
174
|
return (
|
|
184
175
|
<>
|
|
185
176
|
{renderItems(items.slice(0, middle))}
|
|
186
|
-
<CenterAction
|
|
187
|
-
floatingButton={floatingButton}
|
|
188
|
-
fixedFontScale={fixedFontScale}
|
|
189
|
-
/>
|
|
177
|
+
<CenterAction floatingButton={floatingButton} />
|
|
190
178
|
{renderItems(items.slice(middle))}
|
|
191
179
|
</>
|
|
192
180
|
);
|
|
@@ -194,21 +182,17 @@ const HorizontalTabBar: React.FC<{
|
|
|
194
182
|
return renderItems(items);
|
|
195
183
|
};
|
|
196
184
|
|
|
197
|
-
const customContainer = withCta && floatingButton?.container;
|
|
198
|
-
|
|
199
185
|
return (
|
|
200
186
|
<View
|
|
201
187
|
style={[styles.container, { width }]}
|
|
202
188
|
accessibilityLabel={'bottom_tab_bar'}
|
|
203
189
|
>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
/>
|
|
211
|
-
)}
|
|
190
|
+
<CurvedContainer
|
|
191
|
+
width={width}
|
|
192
|
+
height={TabBarMetrics.height}
|
|
193
|
+
radius={TabBarMetrics.height / 2}
|
|
194
|
+
notchCenterX={withCta ? width / 2 : undefined}
|
|
195
|
+
/>
|
|
212
196
|
<View style={styles.content}>
|
|
213
197
|
<ActiveIndicator
|
|
214
198
|
axis={'x'}
|
|
@@ -241,6 +225,8 @@ export default function BottomTabBar(props: Props) {
|
|
|
241
225
|
window.height,
|
|
242
226
|
insets.left,
|
|
243
227
|
insets.right,
|
|
228
|
+
state.routes.length,
|
|
229
|
+
withCta,
|
|
244
230
|
);
|
|
245
231
|
|
|
246
232
|
return (
|
|
@@ -3,7 +3,7 @@ import { StyleSheet } from 'react-native';
|
|
|
3
3
|
import Svg, { Defs, FeGaussianBlur, Filter, Path } from 'react-native-svg';
|
|
4
4
|
import { Colors } from '../../Consts';
|
|
5
5
|
import { ApplicationContext } from '../../Context';
|
|
6
|
-
import { NOTCH_CURVES, NOTCH_HALF_WIDTH } from './metrics';
|
|
6
|
+
import { NOTCH_CURVES, NOTCH_HALF_WIDTH, TabBarMetrics } from './metrics';
|
|
7
7
|
|
|
8
8
|
type Props = {
|
|
9
9
|
width: number;
|
|
@@ -137,7 +137,13 @@ const CurvedContainer: React.FC<Props> = ({
|
|
|
137
137
|
filter={'url(#keyShadow)'}
|
|
138
138
|
/>
|
|
139
139
|
)}
|
|
140
|
-
<Path
|
|
140
|
+
<Path
|
|
141
|
+
d={path}
|
|
142
|
+
fill={theme.colors.background.surface}
|
|
143
|
+
fillOpacity={TabBarMetrics.backgroundOpacity}
|
|
144
|
+
stroke={theme.colors.border.default}
|
|
145
|
+
strokeWidth={1}
|
|
146
|
+
/>
|
|
141
147
|
</Svg>
|
|
142
148
|
);
|
|
143
149
|
};
|
|
@@ -10,10 +10,10 @@ import Animated, {
|
|
|
10
10
|
useSharedValue,
|
|
11
11
|
withTiming,
|
|
12
12
|
} from 'react-native-reanimated';
|
|
13
|
-
import {
|
|
13
|
+
import { TabBarIcon } from './TabBarIcon';
|
|
14
14
|
import { Text } from '../../Text';
|
|
15
15
|
import { MiniAppContext } from '../../Context';
|
|
16
|
-
import { Colors
|
|
16
|
+
import { Colors } from '../../Consts';
|
|
17
17
|
import { TabBarMetrics, TabBarMotion } from './metrics';
|
|
18
18
|
|
|
19
19
|
type IconRenderer = (props: {
|
|
@@ -31,8 +31,6 @@ type TabItemContentProps = {
|
|
|
31
31
|
badge?: string | number;
|
|
32
32
|
// rail item (.Nav/Duo/Item) instead of the horizontal one (.Nav/itemTab)
|
|
33
33
|
vertical?: boolean;
|
|
34
|
-
// keep the label at its design size, the Duo displays otherwise scale it up
|
|
35
|
-
fixedFontScale?: boolean;
|
|
36
34
|
hideLabel?: boolean;
|
|
37
35
|
};
|
|
38
36
|
|
|
@@ -48,7 +46,6 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
|
|
|
48
46
|
icon,
|
|
49
47
|
badge,
|
|
50
48
|
vertical,
|
|
51
|
-
fixedFontScale,
|
|
52
49
|
hideLabel,
|
|
53
50
|
}) => {
|
|
54
51
|
const progress = useSharedValue(focused ? 1 : 0);
|
|
@@ -97,8 +94,7 @@ const TabItemContent: React.FC<TabItemContentProps> = ({
|
|
|
97
94
|
renderIcon={icon}
|
|
98
95
|
/>
|
|
99
96
|
</View>
|
|
100
|
-
{!hideLabel &&
|
|
101
|
-
(fixedFontScale ? <FixedScaleText>{text}</FixedScaleText> : text)}
|
|
97
|
+
{!hideLabel && text}
|
|
102
98
|
</>
|
|
103
99
|
);
|
|
104
100
|
};
|
|
@@ -147,7 +143,7 @@ const styles = StyleSheet.create({
|
|
|
147
143
|
minWidth: 1,
|
|
148
144
|
alignItems: 'center',
|
|
149
145
|
justifyContent: 'center',
|
|
150
|
-
gap:
|
|
146
|
+
gap: TabBarMetrics.itemIconLabelGap,
|
|
151
147
|
},
|
|
152
148
|
slot: {
|
|
153
149
|
height: TabBarMetrics.indicatorHeight,
|
|
@@ -166,6 +162,11 @@ const styles = StyleSheet.create({
|
|
|
166
162
|
},
|
|
167
163
|
labelBox: {
|
|
168
164
|
alignSelf: 'stretch',
|
|
165
|
+
// fixed to the design line height so a capped-scale label overflows downward and the
|
|
166
|
+
// icon above it stays put
|
|
167
|
+
height: TabBarMetrics.labelHeight,
|
|
168
|
+
justifyContent: 'flex-start',
|
|
169
|
+
overflow: 'visible',
|
|
169
170
|
},
|
|
170
171
|
label: {
|
|
171
172
|
textAlign: 'center',
|
|
@@ -13,7 +13,6 @@ import { TabItemContent } from './CustomBottomTabItem';
|
|
|
13
13
|
import { CurvedContainer } from './CurvedContainer';
|
|
14
14
|
import { TabBarMetrics } from './metrics';
|
|
15
15
|
import { getTabItems, TabItem } from './tabItems';
|
|
16
|
-
import { FixedScaleText } from './TabBarIcon';
|
|
17
16
|
import { usePressLift } from './usePressLift';
|
|
18
17
|
import { ActiveIndicator } from './ActiveIndicator';
|
|
19
18
|
import {
|
|
@@ -76,7 +75,6 @@ const Rail: React.FC<{
|
|
|
76
75
|
>
|
|
77
76
|
<TabItemContent
|
|
78
77
|
vertical
|
|
79
|
-
fixedFontScale
|
|
80
78
|
label={item.label}
|
|
81
79
|
focused={item.focused}
|
|
82
80
|
icon={item.icon}
|
|
@@ -116,7 +114,6 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
|
|
|
116
114
|
<CtaIcon icon={floatingButton.icon} />
|
|
117
115
|
</Animated.View>
|
|
118
116
|
<View style={styles.railCtaLabel}>
|
|
119
|
-
<FixedScaleText>
|
|
120
117
|
<Text
|
|
121
118
|
numberOfLines={1}
|
|
122
119
|
typography={'label_xs_medium'}
|
|
@@ -124,7 +121,6 @@ const RailCenterAction: React.FC<{ floatingButton: FloatingButtonProps }> = ({
|
|
|
124
121
|
>
|
|
125
122
|
{floatingButton.label}
|
|
126
123
|
</Text>
|
|
127
|
-
</FixedScaleText>
|
|
128
124
|
{floatingButton.labelContent && (
|
|
129
125
|
<View pointerEvents={'none'} style={styles.railCtaLabelOverlay}>
|
|
130
126
|
{floatingButton.labelContent}
|
|
@@ -3,7 +3,11 @@ import React from 'react';
|
|
|
3
3
|
import { useWindowDimensions, View } from 'react-native';
|
|
4
4
|
import { useScreenLayout } from '../../Layout/utils';
|
|
5
5
|
import { FloatingButtonProps } from '../types';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
getSystemIndicatorHeight,
|
|
8
|
+
hasCenterAction,
|
|
9
|
+
HorizontalTabBar,
|
|
10
|
+
} from './BottomTabBar';
|
|
7
11
|
import { getHorizontalBarWidth, TabBarMetrics } from './metrics';
|
|
8
12
|
import { getTabItems } from './tabItems';
|
|
9
13
|
import { DuoRailTabBar } from './DuoRailTabBar';
|
|
@@ -34,6 +38,7 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
|
|
|
34
38
|
const { insets } = useScreenLayout();
|
|
35
39
|
const { width, height } = useWindowDimensions();
|
|
36
40
|
const { floatingButton, activeTintColor, inactiveTintColor } = props;
|
|
41
|
+
const tabItems = getTabItems(props);
|
|
37
42
|
|
|
38
43
|
return (
|
|
39
44
|
<View
|
|
@@ -44,9 +49,15 @@ const DuoFloatingTabBar: React.FC<DuoTabBarProps> = props => {
|
|
|
44
49
|
]}
|
|
45
50
|
>
|
|
46
51
|
<HorizontalTabBar
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
items={tabItems}
|
|
53
|
+
width={getHorizontalBarWidth(
|
|
54
|
+
width,
|
|
55
|
+
height,
|
|
56
|
+
insets.left,
|
|
57
|
+
insets.right,
|
|
58
|
+
tabItems.length,
|
|
59
|
+
hasCenterAction(floatingButton, tabItems.length),
|
|
60
|
+
)}
|
|
50
61
|
floatingButton={floatingButton}
|
|
51
62
|
activeTintColor={activeTintColor}
|
|
52
63
|
inactiveTintColor={inactiveTintColor}
|
|
@@ -9,23 +9,36 @@ import { Colors, Radius, Spacing } from '../../Consts';
|
|
|
9
9
|
* - vertical: iPhone Duo rail, center action under the container
|
|
10
10
|
*/
|
|
11
11
|
const TabBarMetrics = {
|
|
12
|
-
height:
|
|
12
|
+
height: 60,
|
|
13
13
|
margin: Spacing.S,
|
|
14
|
-
paddingVertical: Spacing.
|
|
14
|
+
paddingVertical: Spacing.XS,
|
|
15
15
|
paddingHorizontal: Spacing.M,
|
|
16
|
-
itemGap:
|
|
16
|
+
itemGap: 1,
|
|
17
|
+
// gap between the icon slot and the label inside a tab (Figma: none)
|
|
18
|
+
itemIconLabelGap: 0,
|
|
19
|
+
// .Nav/Background: no backdrop blur, so use the Figma Blur=Off spec (opacity 95%, no blur) plus the 1px
|
|
20
|
+
// border — nearly solid, matching the frosted sample; surface keeps dark-mode support
|
|
21
|
+
backgroundOpacity: 0.95,
|
|
22
|
+
// white 60% hairline around the center action button (Figma Icon Button border)
|
|
23
|
+
ctaBorderOpacity: 0.6,
|
|
17
24
|
iconSize: 24,
|
|
18
25
|
// active indicator behind the icon: 24 icon + XS·M padding
|
|
19
26
|
indicatorPaddingVertical: Spacing.XS,
|
|
20
27
|
indicatorPaddingHorizontal: Spacing.M,
|
|
21
28
|
indicatorRadius: Radius.XL,
|
|
22
29
|
indicatorHeight: 32,
|
|
30
|
+
// label_s_medium line height: the label box keeps this height so a scaled label grows
|
|
31
|
+
// downward instead of pushing the icon up
|
|
32
|
+
labelHeight: 18,
|
|
23
33
|
// home indicator / gesture bar area under the container
|
|
24
34
|
systemIndicatorHeight: 24,
|
|
25
35
|
// unfolded foldables (short side from 600, the smallest in the spec) use 65% of the short
|
|
26
36
|
// side, centered; phones and folded outer displays fill the window
|
|
27
37
|
foldableMinShortSide: 600,
|
|
28
38
|
foldableWidthRatio: 0.65,
|
|
39
|
+
// a 2-tab bar doesn't stretch to the full width: each item is this fixed width and the bar is
|
|
40
|
+
// sized to its content, centered (Figma Navigation · 2 Tab: item 100, bar ~225)
|
|
41
|
+
twoTabItemWidth: 100,
|
|
29
42
|
ctaSlotWidth: 92,
|
|
30
43
|
ctaSize: 48,
|
|
31
44
|
// the icon sits 10 inside the 48 button (prototype .cta: 28 icon), 12 inside the 52 rail one
|
|
@@ -136,7 +149,17 @@ const getHorizontalBarWidth = (
|
|
|
136
149
|
height: number,
|
|
137
150
|
insetLeft: number,
|
|
138
151
|
insetRight: number,
|
|
152
|
+
itemCount: number,
|
|
153
|
+
hasCta: boolean,
|
|
139
154
|
) => {
|
|
155
|
+
// 2 tabs (no center action) stay content-sized and centered instead of filling the window
|
|
156
|
+
if (itemCount === 2 && !hasCta) {
|
|
157
|
+
return (
|
|
158
|
+
TabBarMetrics.twoTabItemWidth * 2 +
|
|
159
|
+
TabBarMetrics.paddingHorizontal * 2 +
|
|
160
|
+
TabBarMetrics.itemGap
|
|
161
|
+
);
|
|
162
|
+
}
|
|
140
163
|
const shortSide = Math.min(width, height);
|
|
141
164
|
if (shortSide >= TabBarMetrics.foldableMinShortSide) {
|
|
142
165
|
return shortSide * TabBarMetrics.foldableWidthRatio;
|
|
@@ -24,7 +24,6 @@ import Navigation from './Navigation';
|
|
|
24
24
|
import { ModalParams } from './types';
|
|
25
25
|
import BottomSheet from './BottomSheet';
|
|
26
26
|
import { isIPhoneDuo } from './utils';
|
|
27
|
-
import { AdoptionProbe } from './KitAdoption';
|
|
28
27
|
import { useModalLayout } from './modalLayout';
|
|
29
28
|
import { scheduleOnRN } from 'react-native-worklets';
|
|
30
29
|
|
|
@@ -183,12 +182,7 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
183
182
|
modalStyle,
|
|
184
183
|
]}
|
|
185
184
|
>
|
|
186
|
-
<
|
|
187
|
-
screenName={Component?.name || Component?.type?.name || 'Invalid'}
|
|
188
|
-
navigation={props.navigation}
|
|
189
|
-
>
|
|
190
|
-
<Component {...params} onRequestClose={onDismiss} />
|
|
191
|
-
</AdoptionProbe>
|
|
185
|
+
<Component {...params} onRequestClose={onDismiss} />
|
|
192
186
|
</Animated.View>
|
|
193
187
|
</KeyboardAvoidingView>
|
|
194
188
|
</Container>
|
|
@@ -30,7 +30,6 @@ import { HeaderBackground } from './Components/HeaderBackground';
|
|
|
30
30
|
import { useScreenLayout } from '../Layout';
|
|
31
31
|
import { getHeaderRowOptions } from '../Layout/utils';
|
|
32
32
|
import { ReservedRegionsProvider } from '../Layout/ReservedRegions';
|
|
33
|
-
import { AdoptionBadge } from './KitAdoption';
|
|
34
33
|
|
|
35
34
|
const Stack = createStackNavigator();
|
|
36
35
|
|
|
@@ -87,7 +86,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
87
86
|
initialParams={initialParams}
|
|
88
87
|
localize={localize}
|
|
89
88
|
/>
|
|
90
|
-
<AdoptionBadge />
|
|
91
89
|
</MiniAppContext.Provider>
|
|
92
90
|
</ReservedRegionsProvider>
|
|
93
91
|
</SafeAreaProvider>
|
|
@@ -8,7 +8,6 @@ import { SwipeBackHaptic } from './Components/SwipeBackHaptic';
|
|
|
8
8
|
import { useAppState } from './hooks';
|
|
9
9
|
import { createScreenTracking } from './StackScreen.tracking';
|
|
10
10
|
import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal';
|
|
11
|
-
import { AdoptionProbe } from './KitAdoption';
|
|
12
11
|
|
|
13
12
|
const runAfterInteractions = InteractionManager.runAfterInteractions;
|
|
14
13
|
|
|
@@ -284,9 +283,7 @@ const StackScreen: React.FC<any> = props => {
|
|
|
284
283
|
}}
|
|
285
284
|
>
|
|
286
285
|
<TooltipPortalProvider>
|
|
287
|
-
<
|
|
288
|
-
<Component heightHeader={heightHeader} {...data} />
|
|
289
|
-
</AdoptionProbe>
|
|
286
|
+
<Component heightHeader={heightHeader} {...data} />
|
|
290
287
|
<TooltipPortalHost />
|
|
291
288
|
<SwipeBackHaptic />
|
|
292
289
|
</TooltipPortalProvider>
|
package/package.json
CHANGED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import React, { useState, useSyncExternalStore } from 'react';
|
|
2
|
-
import { Share, TouchableOpacity, View } from 'react-native';
|
|
3
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
|
-
|
|
5
|
-
import { Colors, Spacing } from '../../Consts';
|
|
6
|
-
import { Text } from '../../Text';
|
|
7
|
-
import { adoptionStore } from './AdoptionStore';
|
|
8
|
-
import { styles } from './styles';
|
|
9
|
-
import type { ScreenSnapshot } from './types';
|
|
10
|
-
|
|
11
|
-
/** Subscribes a React listener to the adoption store. Stable identity for useSyncExternalStore. */
|
|
12
|
-
const subscribeToStore = (listener: () => void): (() => void) =>
|
|
13
|
-
adoptionStore.subscribe(listener);
|
|
14
|
-
|
|
15
|
-
const getLatestSnapshot = (): ScreenSnapshot | undefined =>
|
|
16
|
-
adoptionStore.getLatest();
|
|
17
|
-
|
|
18
|
-
const getScreenCount = (): number => adoptionStore.getAll().length;
|
|
19
|
-
|
|
20
|
-
/** Share of classified nodes that are app-authored kit: K / (K + R + O). */
|
|
21
|
-
const getAdoptionRate = (snapshot: ScreenSnapshot): number => {
|
|
22
|
-
const { K, R, O } = snapshot.counts;
|
|
23
|
-
const total = K + R + O;
|
|
24
|
-
return total === 0 ? 0 : K / total;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const formatSummary = (snapshot: ScreenSnapshot | undefined): string => {
|
|
28
|
-
if (!snapshot) return 'Kit adoption: chưa có dữ liệu';
|
|
29
|
-
const { K, R, O } = snapshot.counts;
|
|
30
|
-
const percent = Math.round(getAdoptionRate(snapshot) * 100);
|
|
31
|
-
return `Kit ${percent}% · K${K} R${R} O${O}`;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const handleRemeasure = (): void => {
|
|
35
|
-
adoptionStore.remeasureActive();
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const handleShare = (): void => {
|
|
39
|
-
Share.share({ message: JSON.stringify(adoptionStore.getAll()) }).catch(
|
|
40
|
-
() => undefined,
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const handleClear = (): void => {
|
|
45
|
-
adoptionStore.clear();
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Dev-only floating badge showing the latest screen's kit adoption. Tap to
|
|
50
|
-
* expand actions (re-measure, share JSON, clear). Renders nothing in release builds.
|
|
51
|
-
*/
|
|
52
|
-
const AdoptionBadge: React.FC = () => {
|
|
53
|
-
const insets = useSafeAreaInsets();
|
|
54
|
-
const [isExpanded, setExpanded] = useState(false);
|
|
55
|
-
const latest = useSyncExternalStore(subscribeToStore, getLatestSnapshot);
|
|
56
|
-
const screenCount = useSyncExternalStore(subscribeToStore, getScreenCount);
|
|
57
|
-
|
|
58
|
-
if (!__DEV__) return null;
|
|
59
|
-
|
|
60
|
-
const handleToggle = (): void => setExpanded(prev => !prev);
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<View
|
|
64
|
-
pointerEvents="box-none"
|
|
65
|
-
style={[styles.wrapper, { top: insets.top + Spacing.S }]}
|
|
66
|
-
>
|
|
67
|
-
<TouchableOpacity
|
|
68
|
-
accessibilityRole="button"
|
|
69
|
-
onPress={handleToggle}
|
|
70
|
-
style={styles.badge}
|
|
71
|
-
>
|
|
72
|
-
<Text typography="label_xs_medium" color={Colors.black_01}>
|
|
73
|
-
{formatSummary(latest)}
|
|
74
|
-
</Text>
|
|
75
|
-
{latest ? (
|
|
76
|
-
<Text typography="description_xs_regular" color={Colors.black_01}>
|
|
77
|
-
{latest.screen_name}
|
|
78
|
-
</Text>
|
|
79
|
-
) : null}
|
|
80
|
-
</TouchableOpacity>
|
|
81
|
-
{isExpanded ? (
|
|
82
|
-
<View style={styles.panel}>
|
|
83
|
-
<Text typography="label_xs_medium" color={Colors.black_17}>
|
|
84
|
-
{`Đã đo ${screenCount} màn hình`}
|
|
85
|
-
</Text>
|
|
86
|
-
<TouchableOpacity
|
|
87
|
-
accessibilityRole="button"
|
|
88
|
-
onPress={handleRemeasure}
|
|
89
|
-
style={styles.action}
|
|
90
|
-
>
|
|
91
|
-
<Text typography="label_xs_medium" color={Colors.blue_03}>
|
|
92
|
-
Đo lại
|
|
93
|
-
</Text>
|
|
94
|
-
</TouchableOpacity>
|
|
95
|
-
<TouchableOpacity
|
|
96
|
-
accessibilityRole="button"
|
|
97
|
-
onPress={handleShare}
|
|
98
|
-
style={styles.action}
|
|
99
|
-
>
|
|
100
|
-
<Text typography="label_xs_medium" color={Colors.blue_03}>
|
|
101
|
-
Share JSON
|
|
102
|
-
</Text>
|
|
103
|
-
</TouchableOpacity>
|
|
104
|
-
<TouchableOpacity
|
|
105
|
-
accessibilityRole="button"
|
|
106
|
-
onPress={handleClear}
|
|
107
|
-
style={styles.actionOutline}
|
|
108
|
-
>
|
|
109
|
-
<Text typography="label_xs_medium" color={Colors.black_17}>
|
|
110
|
-
Xoá
|
|
111
|
-
</Text>
|
|
112
|
-
</TouchableOpacity>
|
|
113
|
-
</View>
|
|
114
|
-
) : null}
|
|
115
|
-
</View>
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export { AdoptionBadge };
|