@momo-kits/foundation 0.168.1-beta.29 → 0.168.1-beta.30

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.
@@ -41,10 +41,10 @@ import Animated, {
41
41
  import { scheduleOnRN } from 'react-native-worklets';
42
42
  import layoutStyles from '../Layout/styles';
43
43
  import { DuoMetrics, InnerEdgeStyles, useScreenLayout } from '../Layout/utils';
44
- import { getDuoTopInset, isIPhoneDuo } from './device';
44
+ import { getDuoTopInset, isFoldableDevice } from './device';
45
45
  import { SideBarButton } from './Components/SideBarButton';
46
46
 
47
- // iPhone Duo inner display: center the sheet instead of spanning the width
47
+ // foldable inner display: center the sheet instead of spanning the width
48
48
  const DUO_REGULAR_MIN_WIDTH = 600;
49
49
 
50
50
  const INDICATOR_HEIGHT = 16;
@@ -59,7 +59,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
59
59
  const { theme, navigator } = useContext(ApplicationContext);
60
60
  const context = useContext<any>(MiniAppContext);
61
61
  const windowSize = useWindowDimensions();
62
- const isDuo = isIPhoneDuo();
62
+ const isDuo = isFoldableDevice();
63
63
  const isDuoCentered =
64
64
  isDuo &&
65
65
  Math.min(windowSize.width, windowSize.height) >= DUO_REGULAR_MIN_WIDTH;
@@ -91,6 +91,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
91
91
  const isIOS = Platform.OS === 'ios';
92
92
  const bottomInset = isIOS ? Math.min(insets.bottom, 21) : insets.bottom;
93
93
  const keyboardOffset = heightHeader - bottomInset;
94
+ // Android shrinks the root for the keyboard but keeps the window height and
95
+ // fires no keyboardWill* events, so only the root can bound the sheet
96
+ const fitsResizedRoot = isDuo && !isIOS;
94
97
 
95
98
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
96
99
 
@@ -209,8 +212,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
209
212
  }
210
213
 
211
214
  /**
212
- * emit dismiss event, once: folding or rotating iPhone Duo changes the
213
- * window height while the sheet stays open
215
+ * emit dismiss event, once: folding or rotating a foldable device changes
216
+ * the window height while the sheet stays open
214
217
  */
215
218
  useEffect(() => {
216
219
  openAnimation();
@@ -473,6 +476,8 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
473
476
  maxHeight: windowSize.height - topGap - keyboardLift,
474
477
  marginBottom: keyboardLift,
475
478
  },
479
+ fitsResizedRoot && styles.shrink,
480
+ fitsResizedRoot && { marginTop: topGap },
476
481
  isDuoFloating && { marginHorizontal: Spacing.M },
477
482
  sideColumn && {
478
483
  minHeight:
@@ -18,7 +18,7 @@ import { Styles } from '../Consts';
18
18
  import Navigation from './Navigation';
19
19
  import { ModalParams } from './types';
20
20
  import BottomSheet from './BottomSheet';
21
- import { getDuoModalBounds, isIPhoneDuo } from './device';
21
+ import { getDuoModalBounds, isFoldableDevice } from './device';
22
22
  import { scheduleOnRN } from 'react-native-worklets';
23
23
 
24
24
  const ModalScreen: React.FC<any> = props => {
@@ -42,7 +42,7 @@ const Modal: React.FC<ModalParams> = props => {
42
42
  const Component = useRef(screen).current;
43
43
  const insets = useSafeAreaInsets();
44
44
  const windowSize = useWindowDimensions();
45
- const isDuo = isIPhoneDuo();
45
+ const isDuo = isFoldableDevice();
46
46
 
47
47
  /**
48
48
  * Fire auto_popup_displayed tracking event on mount
@@ -1,4 +1,4 @@
1
- import { Dimensions, Platform, ScaledSize } from 'react-native';
1
+ import { Dimensions, NativeModules, Platform, ScaledSize } from 'react-native';
2
2
  import type { EdgeInsets } from 'react-native-safe-area-context';
3
3
  import { Spacing } from '../Consts';
4
4
 
@@ -23,6 +23,13 @@ const isIPhoneDuo = () => {
23
23
  );
24
24
  };
25
25
 
26
+ // reported by the host; hosts without the constant count as not foldable
27
+ const isAndroidFoldable = () =>
28
+ Platform.OS === 'android' &&
29
+ NativeModules?.FoldStateModule?.isFoldableDevice === true;
30
+
31
+ const isFoldableDevice = () => isIPhoneDuo() || isAndroidFoldable();
32
+
26
33
  // portrait side status bar is about twice as tall as its inset is wide
27
34
  const getDuoTopInset = (insets: EdgeInsets, width: number, height: number) =>
28
35
  Math.max(
@@ -31,8 +38,9 @@ const getDuoTopInset = (insets: EdgeInsets, width: number, height: number) =>
31
38
  );
32
39
 
33
40
  /**
34
- * Max modal size on iPhone Duo: inside the safe area, so clear of the side
35
- * toolbar strip, and capped so it stays a dialog on the inner display.
41
+ * Max modal size on foldable devices: inside the safe area, so clear of the
42
+ * side toolbar strip on iPhone Duo, and capped so it stays a dialog on the
43
+ * inner display.
36
44
  */
37
45
  const getDuoModalBounds = (
38
46
  insets: EdgeInsets,
@@ -49,4 +57,10 @@ const getDuoModalBounds = (
49
57
  ),
50
58
  });
51
59
 
52
- export { isIPhoneDuo, getDuoTopInset, getDuoModalBounds };
60
+ export {
61
+ isIPhoneDuo,
62
+ isAndroidFoldable,
63
+ isFoldableDevice,
64
+ getDuoTopInset,
65
+ getDuoModalBounds,
66
+ };
@@ -8,18 +8,12 @@ import { Image } from '../Image';
8
8
  import { Text, useScaleSize } from '../Text';
9
9
  import { PopupNotifyProps } from './types';
10
10
  import { scheduleOnRN } from 'react-native-worklets';
11
- import {
12
- getPopupRadius,
13
- splitHighlightText,
14
- usePopupMaxHeight,
15
- usePopupWidth,
16
- } from './utils';
11
+ import { getPopupRadius, usePopupMaxHeight, usePopupWidth } from './utils';
17
12
 
18
13
  const PopupNotify: React.FC<PopupNotifyProps> = ({
19
14
  image,
20
15
  title = 'Title',
21
16
  description = 'Description',
22
- highlightText,
23
17
  error,
24
18
  secondary,
25
19
  primary,
@@ -141,25 +135,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
141
135
  return renderColumn();
142
136
  };
143
137
 
144
- /**
145
- * build description text, highlighted keywords keep size/lineHeight and use weight 600
146
- */
147
- const renderDescription = (text: string) => {
148
- const segments = splitHighlightText(text, highlightText);
149
- if (!segments) {
150
- return text;
151
- }
152
- return segments.map((segment, index) =>
153
- segment.highlight ? (
154
- <Text key={index} typography={'header_s_semibold'}>
155
- {segment.text}
156
- </Text>
157
- ) : (
158
- segment.text
159
- ),
160
- );
161
- };
162
-
163
138
  /**
164
139
  * build close action
165
140
  */
@@ -225,7 +200,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
225
200
  }
226
201
  }}
227
202
  >
228
- {renderDescription(description)}
203
+ {description}
229
204
  </Text>
230
205
  ) : (
231
206
  description
package/Popup/types.ts CHANGED
@@ -20,13 +20,6 @@ export type PopupNotifyProps = {
20
20
  */
21
21
  description?: string | React.ReactNode;
22
22
 
23
- /**
24
- * `highlightText`: Optional. List of words/phrases within `description` to be rendered in semibold.
25
- * Only applies when `description` is a string. Matching is whole-word and case-sensitive;
26
- * every occurrence of each string is highlighted.
27
- */
28
- highlightText?: string[];
29
-
30
23
  /**
31
24
  * `error`: Optional. Represents additional error text to be displayed in the PopupNotify component.
32
25
  */
package/Popup/utils.ts CHANGED
@@ -1,14 +1,12 @@
1
1
  import { Dimensions, useWindowDimensions } from 'react-native';
2
2
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
3
3
  import { Radius, Spacing } from '../Consts';
4
- import { getDuoModalBounds, isIPhoneDuo } from '../Application/device';
5
-
6
- type HighlightSegment = { text: string; highlight: boolean };
4
+ import { getDuoModalBounds, isFoldableDevice } from '../Application/device';
7
5
 
8
6
  const usePopupWidth = () => {
9
7
  const insets = useSafeAreaInsets();
10
8
  const { width, height } = useWindowDimensions();
11
- if (!isIPhoneDuo()) {
9
+ if (!isFoldableDevice()) {
12
10
  return Dimensions.get('window').width - Spacing.XL * 2;
13
11
  }
14
12
  return getDuoModalBounds(insets, width, height).maxWidth;
@@ -17,55 +15,12 @@ const usePopupWidth = () => {
17
15
  const usePopupMaxHeight = () => {
18
16
  const insets = useSafeAreaInsets();
19
17
  const { width, height } = useWindowDimensions();
20
- if (!isIPhoneDuo()) {
18
+ if (!isFoldableDevice()) {
21
19
  return undefined;
22
20
  }
23
21
  return getDuoModalBounds(insets, width, height).maxHeight;
24
22
  };
25
23
 
26
- const getPopupRadius = () => (isIPhoneDuo() ? Radius.XL : Radius.L);
27
-
28
- const isWordChar = (char?: string) =>
29
- !!char && (char.toLowerCase() !== char.toUpperCase() || /[0-9_]/.test(char));
30
-
31
- /**
32
- * Split `text` into plain/highlighted segments (same matching as Information `highlightText`):
33
- * whole-word, case-sensitive, every occurrence, longest keyword first.
34
- * Returns `undefined` when there is nothing to highlight.
35
- */
36
- const splitHighlightText = (
37
- text: string,
38
- highlightText?: string[],
39
- ): HighlightSegment[] | undefined => {
40
- const keywords = highlightText?.filter(
41
- keyword => typeof keyword === 'string' && keyword,
42
- );
43
- if (!keywords?.length) {
44
- return undefined;
45
- }
46
- const pattern = [...keywords]
47
- .sort((a, b) => b.length - a.length)
48
- .map(keyword => keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
49
- .join('|');
50
- const regex = new RegExp(pattern, 'g');
51
- const segments: HighlightSegment[] = [];
52
- let lastIndex = 0;
53
- let match;
54
- while ((match = regex.exec(text))) {
55
- const start = match.index;
56
- const end = start + match[0].length;
57
- if (isWordChar(text[start - 1]) || isWordChar(text[end])) {
58
- regex.lastIndex = start + 1;
59
- continue;
60
- }
61
- if (start > lastIndex) {
62
- segments.push({ text: text.slice(lastIndex, start), highlight: false });
63
- }
64
- segments.push({ text: match[0], highlight: true });
65
- lastIndex = end;
66
- }
67
- segments.push({ text: text.slice(lastIndex), highlight: false });
68
- return segments;
69
- };
24
+ const getPopupRadius = () => (isFoldableDevice() ? Radius.XL : Radius.L);
70
25
 
71
- export { usePopupWidth, usePopupMaxHeight, getPopupRadius, splitHighlightText };
26
+ export { usePopupWidth, usePopupMaxHeight, getPopupRadius };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.168.1-beta.29",
3
+ "version": "0.168.1-beta.30",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},