@momo-kits/foundation 0.169.4 → 0.169.5
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 +8 -8
- package/Application/BottomSheet.parts.tsx +44 -34
- package/Application/BottomSheet.tsx +35 -26
- package/Application/BottomTab/ActiveIndicator.tsx +15 -14
- package/Application/BottomTab/BottomTabBar.tsx +43 -34
- package/Application/BottomTab/BottomTabInset.tsx +8 -4
- package/Application/BottomTab/CurvedContainer.tsx +14 -12
- package/Application/BottomTab/CustomBottomTabItem.tsx +10 -6
- package/Application/BottomTab/DuoBarCompression.tsx +13 -19
- package/Application/BottomTab/DuoRailTabBar.tsx +13 -13
- package/Application/BottomTab/DuoTabBar.tsx +8 -9
- package/Application/BottomTab/TabBarIcon.tsx +13 -8
- package/Application/BottomTab/TabScreen.tsx +1 -5
- package/Application/BottomTab/index.tsx +38 -30
- package/Application/BottomTab/metrics.ts +21 -20
- package/Application/BottomTab/tabItems.ts +1 -1
- package/Application/BottomTab/usePressLift.ts +2 -2
- package/Application/Components/BackgroundImageView.tsx +4 -0
- package/Application/Components/DuoGlassBackground.tsx +5 -4
- package/Application/Components/HeaderAnimated.tsx +4 -0
- package/Application/Components/HeaderBackground.tsx +4 -0
- package/Application/Components/HeaderExtendHeader.tsx +11 -10
- package/Application/Components/HeaderLeft.tsx +2 -1
- package/Application/Components/HeaderRight.tsx +2 -1
- package/Application/Components/HeaderTitle.shared.ts +11 -9
- package/Application/Components/HeaderTitle.tsx +4 -3
- package/Application/Components/HeaderToolkitAction.tsx +17 -20
- package/Application/Components/NavigationButton.tsx +2 -1
- package/Application/Components/SearchHeader.tsx +17 -10
- package/Application/Components/TitleJourney.tsx +2 -2
- package/Application/Components/TitleLocation.tsx +2 -2
- package/Application/Components/TitleUser.tsx +2 -2
- package/Application/Components/useToolkitActions.tsx +17 -32
- package/Application/Localize.ts +7 -0
- package/Application/ModalScreen.tsx +9 -3
- package/Application/Navigation.ts +14 -16
- package/Application/NavigationContainer.tsx +19 -16
- package/Application/Navigator.ts +20 -48
- package/Application/StackScreen.tracking.ts +11 -26
- package/Application/StackScreen.tsx +8 -35
- package/Application/TooltipPortal.tsx +12 -5
- package/Application/WidgetContainer.tsx +10 -4
- package/Application/modalLayout.ts +11 -12
- package/Application/navigationOptions.tsx +21 -17
- package/Badge/Badge.tsx +5 -0
- package/Badge/BadgeDot.tsx +1 -0
- package/Badge/BadgeDotAnimation.tsx +15 -7
- package/Badge/BadgeRibbon.tsx +46 -38
- package/Badge/Shape.tsx +2 -0
- package/Badge/types.ts +2 -2
- package/Button/index.tsx +5 -16
- package/Button/utils.ts +9 -15
- package/CheckBox/index.tsx +4 -0
- package/Consts/theme.ts +10 -10
- package/Context/index.ts +1 -0
- package/Divider/DashDivider.tsx +1 -0
- package/Divider/index.tsx +4 -0
- package/FoundationList/index.tsx +4 -0
- package/Icon/index.tsx +6 -6
- package/Icon/types.ts +1 -1
- package/IconButton/index.tsx +4 -12
- package/Image/index.tsx +9 -7
- package/Input/Input.tsx +8 -9
- package/Input/InputDropDown.tsx +10 -12
- package/Input/InputMoney.tsx +12 -8
- package/Input/InputMoneyView.tsx +14 -7
- package/Input/InputOTP.tsx +35 -33
- package/Input/InputPhoneNumber.tsx +8 -9
- package/Input/InputSearch.tsx +4 -6
- package/Input/InputTextArea.tsx +4 -0
- package/Input/SearchTextTyping.tsx +4 -0
- package/Input/SystemTextInput.tsx +7 -2
- package/Input/TextTyping.tsx +4 -0
- package/Input/common.tsx +26 -12
- package/Input/index.tsx +17 -20
- package/Input/moneyUtils.ts +10 -4
- package/Input/styles.ts +0 -5
- package/Input/utils.ts +14 -12
- package/Layout/Card.tsx +7 -4
- package/Layout/FloatingButton.tsx +4 -0
- package/Layout/Item.tsx +6 -2
- package/Layout/ItemList.tsx +9 -7
- package/Layout/ItemSectionList.tsx +2 -1
- package/Layout/ReservedRegions.tsx +25 -25
- package/Layout/Screen.tsx +56 -58
- package/Layout/Section.tsx +8 -5
- package/Layout/TrackingScope.tsx +1 -0
- package/Layout/screenContent.tsx +4 -2
- package/Layout/screenFooter.tsx +28 -18
- package/Layout/screenHeader.tsx +34 -31
- package/Layout/screenQuickReturn.tsx +17 -20
- package/Layout/screenScroll.ts +4 -7
- package/Layout/screenTypes.ts +3 -2
- package/Layout/types.ts +2 -4
- package/Layout/utils.ts +31 -37
- package/Loader/DotLoader.tsx +4 -0
- package/Loader/Loader.tsx +4 -0
- package/Loader/ProgressBar.tsx +1 -0
- package/Loader/Spinner.tsx +4 -0
- package/Loader/utils.ts +11 -7
- package/Pagination/Dot.tsx +4 -0
- package/Pagination/PaginationDot.tsx +1 -0
- package/Pagination/PaginationNumber.tsx +5 -3
- package/Pagination/PaginationScroll.tsx +5 -0
- package/Pagination/PaginationWhiteDot.tsx +1 -0
- package/Pagination/index.tsx +4 -0
- package/Pagination/types.ts +2 -2
- package/Popup/PopupNotify.tsx +39 -39
- package/Popup/PopupPromotion.tsx +4 -7
- package/Popup/types.ts +4 -4
- package/Popup/utils.ts +4 -0
- package/Radio/index.tsx +4 -0
- package/Skeleton/index.tsx +8 -0
- package/Switch/index.tsx +1 -0
- package/Tag/index.tsx +5 -1
- package/Tag/types.ts +3 -3
- package/Text/index.tsx +14 -3
- package/Text/styles.ts +1 -1
- package/Text/types.ts +1 -6
- package/Text/utils.ts +9 -0
- package/Title/index.tsx +41 -32
- package/ViewBoundary/index.tsx +4 -2
- package/index.ts +0 -6
- package/package.json +1 -1
- package/Application/__tests__/modalLayout.test.ts +0 -128
package/Layout/utils.ts
CHANGED
|
@@ -15,9 +15,8 @@ import {
|
|
|
15
15
|
import { ReservedRegion, ReservedRegionsContext } from './ReservedRegions';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* @param childrenTypes
|
|
18
|
+
* Warns when a child is not one of `childrenTypes` (Fragments pass) and returns the
|
|
19
|
+
* children, flattened to the valid elements when given an array.
|
|
21
20
|
*/
|
|
22
21
|
const validateChildren = (
|
|
23
22
|
children: React.ReactNode | React.ReactNode[],
|
|
@@ -54,20 +53,20 @@ const validateChildren = (
|
|
|
54
53
|
const NO_INSETS: EdgeInsets = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
56
|
+
* How far system regions (e.g. the camera, at either end depending on the rotation) reach
|
|
57
|
+
* into a side strip from its top and bottom ends, plus the center y of the region reaching
|
|
58
|
+
* furthest from the top.
|
|
60
59
|
*/
|
|
61
60
|
const getStripOcclusion = (
|
|
62
61
|
regions: ReservedRegion[],
|
|
63
62
|
stripLeft: number,
|
|
64
63
|
stripRight: number,
|
|
65
64
|
windowHeight: number,
|
|
66
|
-
) =>
|
|
67
|
-
regions.reduce(
|
|
65
|
+
) => {
|
|
66
|
+
return regions.reduce(
|
|
68
67
|
(occlusion, region) => {
|
|
69
|
-
const
|
|
70
|
-
|
|
68
|
+
const regionRight = region.x + region.width;
|
|
69
|
+
const overlapsStrip = region.x < stripRight && regionRight > stripLeft;
|
|
71
70
|
if (!overlapsStrip) {
|
|
72
71
|
return occlusion;
|
|
73
72
|
}
|
|
@@ -84,10 +83,11 @@ const getStripOcclusion = (
|
|
|
84
83
|
},
|
|
85
84
|
{ top: 0, topCenter: 0, bottom: 0 },
|
|
86
85
|
);
|
|
86
|
+
};
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
90
|
-
* each side. `undefined` without reserved regions there
|
|
89
|
+
* How far the clock and battery clusters of a status bar on the top edge reach in from
|
|
90
|
+
* each side. `undefined` without reserved regions there.
|
|
91
91
|
*/
|
|
92
92
|
const getStatusBarClusters = (
|
|
93
93
|
regions: ReservedRegion[],
|
|
@@ -95,12 +95,11 @@ const getStatusBarClusters = (
|
|
|
95
95
|
topInset: number,
|
|
96
96
|
) => {
|
|
97
97
|
const midX = windowWidth / 2;
|
|
98
|
-
const clusters = regions.filter(
|
|
99
|
-
region
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
98
|
+
const clusters = regions.filter(region => {
|
|
99
|
+
const isInTopInset = region.y + region.height <= topInset;
|
|
100
|
+
const isOnOneSide = region.x + region.width <= midX || region.x >= midX;
|
|
101
|
+
return region.y < topInset && isInTopInset && isOnOneSide;
|
|
102
|
+
});
|
|
104
103
|
if (!clusters.length) {
|
|
105
104
|
return undefined;
|
|
106
105
|
}
|
|
@@ -118,8 +117,10 @@ const getStatusBarClusters = (
|
|
|
118
117
|
);
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
/** iOS phone idiom: iPad keeps the plain header and is never treated as iPhone Duo. */
|
|
121
|
+
const isIPhone = () => {
|
|
122
|
+
return Platform.OS === 'ios' && Platform.constants.interfaceIdiom === 'phone';
|
|
123
|
+
};
|
|
123
124
|
|
|
124
125
|
/**
|
|
125
126
|
* Header height and the room it keeps above the bar row for the status bar. On iPhone the
|
|
@@ -141,7 +142,7 @@ const getHeaderLayout = (topInset: number) => {
|
|
|
141
142
|
|
|
142
143
|
/**
|
|
143
144
|
* Header whose bar row is centered on a window y, at least iPhoneMinHeight tall and
|
|
144
|
-
* reaching 8pt below a side bar control centered there
|
|
145
|
+
* reaching 8pt below a side bar control centered there.
|
|
145
146
|
*/
|
|
146
147
|
const getCenteredHeaderLayout = (centerY: number) => {
|
|
147
148
|
const headerHeight = Math.max(
|
|
@@ -221,8 +222,7 @@ const useScreenLayout = (): ScreenLayout => {
|
|
|
221
222
|
if (sideInset >= DuoMetrics.controlSize + Spacing.M) {
|
|
222
223
|
const edge = insets.right >= insets.left ? 'right' : 'left';
|
|
223
224
|
const stripLeft = edge === 'right' ? width - sideInset : 0;
|
|
224
|
-
//
|
|
225
|
-
// calls and Live Activities. Region frames already include the system's margin
|
|
225
|
+
// clear the camera and status bar cluster; region frames include the system margin
|
|
226
226
|
const occlusion = getStripOcclusion(
|
|
227
227
|
reservedRegions,
|
|
228
228
|
stripLeft,
|
|
@@ -242,8 +242,7 @@ const useScreenLayout = (): ScreenLayout => {
|
|
|
242
242
|
} else if (height >= DuoMetrics.innerMinHeight) {
|
|
243
243
|
top = DuoMetrics.firstItemTopLandscape;
|
|
244
244
|
} else {
|
|
245
|
-
// outer
|
|
246
|
-
// on the camera when it sits at the top end, otherwise on the back button
|
|
245
|
+
// outer landscape: title row centers on a camera at the top end, else on back
|
|
247
246
|
top = DuoMetrics.edgeInset;
|
|
248
247
|
const cameraAtTop = occlusion.top > top;
|
|
249
248
|
const backCenterY = top + DuoMetrics.controlSize / 2;
|
|
@@ -285,7 +284,7 @@ const useScreenLayout = (): ScreenLayout => {
|
|
|
285
284
|
|
|
286
285
|
/**
|
|
287
286
|
* Header row options for react-navigation: its height, the room for the status bar, and
|
|
288
|
-
* on iPhone Duo inner portrait the buttons kept clear of the clock and battery clusters
|
|
287
|
+
* on iPhone Duo inner portrait the buttons kept clear of the clock and battery clusters.
|
|
289
288
|
*/
|
|
290
289
|
const getHeaderRowOptions = ({
|
|
291
290
|
headerHeight,
|
|
@@ -305,9 +304,7 @@ const getHeaderRowOptions = ({
|
|
|
305
304
|
};
|
|
306
305
|
};
|
|
307
306
|
|
|
308
|
-
/**
|
|
309
|
-
* Side margin @react-navigation/elements Header adds to its content row
|
|
310
|
-
*/
|
|
307
|
+
/** Side margin @react-navigation/elements Header adds to its content row. */
|
|
311
308
|
const getHeaderMargin = (width: number) => {
|
|
312
309
|
if (Platform.OS === 'ios' && width >= HeaderMetrics.largeWidth) {
|
|
313
310
|
return HeaderMetrics.largeMargin;
|
|
@@ -321,11 +318,9 @@ const InnerEdgeStyles: Record<SideBarEdge, ViewStyle> = {
|
|
|
321
318
|
};
|
|
322
319
|
|
|
323
320
|
/**
|
|
324
|
-
* Absolute style
|
|
325
|
-
*
|
|
321
|
+
* Absolute style floating a header item into the side bar, 12pt from the strip's inner
|
|
322
|
+
* edge, relative to the `container` rendering it; `slot` 0 is back, 1 the toolkit below.
|
|
326
323
|
* `undefined` while there is no side bar.
|
|
327
|
-
* @param container header container rendering the item
|
|
328
|
-
* @param slot position in the side bar, 0 for back, 1 for the toolkit below it
|
|
329
324
|
*/
|
|
330
325
|
const useSideBarItemStyle = (
|
|
331
326
|
container: 'left' | 'right',
|
|
@@ -338,10 +333,8 @@ const useSideBarItemStyle = (
|
|
|
338
333
|
|
|
339
334
|
const margin = getHeaderMargin(width);
|
|
340
335
|
const columnLeft = sideBar.edge === 'left' ? 0 : width - sideBar.width;
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
headerStatusBarHeight +
|
|
344
|
-
slot * (DuoMetrics.controlSize + Spacing.M);
|
|
336
|
+
const slotOffset = slot * (DuoMetrics.controlSize + Spacing.M);
|
|
337
|
+
const top = sideBar.top - headerStatusBarHeight + slotOffset;
|
|
345
338
|
const innerEdgeStyle = InnerEdgeStyles[sideBar.edge];
|
|
346
339
|
|
|
347
340
|
if (container === 'left') {
|
|
@@ -364,6 +357,7 @@ const useSideBarItemStyle = (
|
|
|
364
357
|
};
|
|
365
358
|
};
|
|
366
359
|
|
|
360
|
+
/** Column count of the content grid: 24 on content wider than 600pt, otherwise 12. */
|
|
367
361
|
const useGridSystem = () => {
|
|
368
362
|
const { contentWidth } = useScreenLayout();
|
|
369
363
|
const numberOfColumns = contentWidth > 600 ? 24 : 12;
|
package/Loader/DotLoader.tsx
CHANGED
|
@@ -6,6 +6,10 @@ import { ApplicationContext } from '../Context';
|
|
|
6
6
|
import { hexToRGBA } from './utils';
|
|
7
7
|
import { LoaderProps } from './types';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Three-dot Lottie loader recolored to `color` (theme primary by default).
|
|
11
|
+
* Recoloring mutates the shared animation JSON, so `Loader` remounts on change.
|
|
12
|
+
*/
|
|
9
13
|
const DotLoader: FC<LoaderProps> = ({ color, style }) => {
|
|
10
14
|
const { theme } = useContext(ApplicationContext);
|
|
11
15
|
|
package/Loader/Loader.tsx
CHANGED
|
@@ -3,6 +3,10 @@ import {LoaderProps} from './types';
|
|
|
3
3
|
import DotLoader from './DotLoader';
|
|
4
4
|
import Spinner from './Spinner';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Dot (default) or spinner loader. Keyed by `color` so a color change remounts
|
|
8
|
+
* the Lottie view with the recolored source.
|
|
9
|
+
*/
|
|
6
10
|
const Loader: FC<LoaderProps> = ({type = 'dot', ...props}) => {
|
|
7
11
|
if (type === 'spinner') {
|
|
8
12
|
return <Spinner {...props} key={props.color} />;
|
package/Loader/ProgressBar.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import styles from './styles';
|
|
|
9
9
|
import { ProgressBarProps } from './types';
|
|
10
10
|
import { ApplicationContext } from '../Context';
|
|
11
11
|
|
|
12
|
+
/** Full-width bar that animates its fill to `percent`, clamped to 0–100. */
|
|
12
13
|
const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
13
14
|
const { theme } = useContext(ApplicationContext);
|
|
14
15
|
const animation = useSharedValue(0);
|
package/Loader/Spinner.tsx
CHANGED
|
@@ -6,6 +6,10 @@ import { hexToRGBA } from './utils';
|
|
|
6
6
|
import { ApplicationContext } from '../Context';
|
|
7
7
|
import { LoaderProps } from './types';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Lottie spinner recolored to `color` (theme primary by default).
|
|
11
|
+
* Recoloring mutates the shared animation JSON, so `Loader` remounts on change.
|
|
12
|
+
*/
|
|
9
13
|
const Spinner: FC<LoaderProps> = ({ color, style }) => {
|
|
10
14
|
const { theme } = useContext(ApplicationContext);
|
|
11
15
|
|
package/Loader/utils.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts `#rgb`/`#rrggbb` (the `#` is optional) to Lottie's `[r, g, b]` in
|
|
3
|
+
* 0–1, rounded to 3 decimals. Throws on an invalid hex.
|
|
4
|
+
*/
|
|
1
5
|
export const hexToRGBA = (hex: string) => {
|
|
2
6
|
const normalizedHex = hex.startsWith('#') ? hex.slice(1) : hex;
|
|
3
7
|
|
|
@@ -5,13 +9,13 @@ export const hexToRGBA = (hex: string) => {
|
|
|
5
9
|
throw new Error('Invalid HEX color.');
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
let expandedHex = normalizedHex;
|
|
13
|
+
if (normalizedHex.length === 3) {
|
|
14
|
+
expandedHex = normalizedHex
|
|
15
|
+
.split('')
|
|
16
|
+
.map(val => val + val)
|
|
17
|
+
.join('');
|
|
18
|
+
}
|
|
15
19
|
|
|
16
20
|
const hexMatch = expandedHex.match(/.{2}/g);
|
|
17
21
|
|
package/Pagination/Dot.tsx
CHANGED
|
@@ -4,6 +4,10 @@ import styles from './styles';
|
|
|
4
4
|
import { DotProps } from './types';
|
|
5
5
|
import { ApplicationContext } from '../Context';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* One pagination dot: a wide bar in the caller's color when active, otherwise
|
|
9
|
+
* a small dot in the theme pressed color.
|
|
10
|
+
*/
|
|
7
11
|
const Dot: FC<DotProps> = ({ active, style }) => {
|
|
8
12
|
const { theme } = useContext(ApplicationContext);
|
|
9
13
|
const pressedColor = theme.colors.background.pressed;
|
|
@@ -6,6 +6,7 @@ import styles from './styles';
|
|
|
6
6
|
import { Spacing } from '../Consts';
|
|
7
7
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
8
8
|
|
|
9
|
+
/** Row of primary-color dots with the `activeIndex` dot widened. */
|
|
9
10
|
const PaginationDot: FC<ChildPaginationProps> = ({
|
|
10
11
|
dataLength = 2,
|
|
11
12
|
activeIndex = 0,
|
|
@@ -6,6 +6,7 @@ import { Text } from '../Text';
|
|
|
6
6
|
import { Colors } from '../Consts';
|
|
7
7
|
import { MiniAppContext } from '../Context';
|
|
8
8
|
|
|
9
|
+
/** `n/total` counter on a translucent dark pill. */
|
|
9
10
|
const PaginationNumber: FC<ChildPaginationProps> = ({
|
|
10
11
|
activeIndex = 0,
|
|
11
12
|
dataLength = 2,
|
|
@@ -14,15 +15,16 @@ const PaginationNumber: FC<ChildPaginationProps> = ({
|
|
|
14
15
|
const context = useContext<any>(MiniAppContext);
|
|
15
16
|
|
|
16
17
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
18
|
+
const pageLabel = `${activeIndex + 1}/${dataLength}`;
|
|
17
19
|
|
|
18
20
|
return (
|
|
19
21
|
<View
|
|
20
22
|
style={[style, styles.row, showBaseLineDebug && styles.debugBaseLine]}
|
|
21
23
|
>
|
|
22
24
|
<View style={styles.paginationNumberContainer}>
|
|
23
|
-
<Text color={Colors.black_01} typography={'label_default_medium'}>
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
<Text color={Colors.black_01} typography={'label_default_medium'}>
|
|
26
|
+
{pageLabel}
|
|
27
|
+
</Text>
|
|
26
28
|
</View>
|
|
27
29
|
</View>
|
|
28
30
|
);
|
|
@@ -13,6 +13,11 @@ import { ApplicationContext, MiniAppContext } from '../Context';
|
|
|
13
13
|
|
|
14
14
|
const INDICATOR_WIDTH = 24;
|
|
15
15
|
const INDICATOR_CONTAINER_WIDTH = 72;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Horizontal scroll view with a small indicator that tracks the scroll offset.
|
|
19
|
+
* The indicator shows only when the content is wider than the view.
|
|
20
|
+
*/
|
|
16
21
|
const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
17
22
|
style,
|
|
18
23
|
children,
|
|
@@ -6,6 +6,7 @@ import Dot from './Dot';
|
|
|
6
6
|
import { Colors, Spacing } from '../Consts';
|
|
7
7
|
import { MiniAppContext } from '../Context';
|
|
8
8
|
|
|
9
|
+
/** Pagination dots on a translucent dark pill, for use over images. */
|
|
9
10
|
const PaginationWhiteDot: FC<ChildPaginationProps> = ({
|
|
10
11
|
dataLength = 2,
|
|
11
12
|
activeIndex = 0,
|
package/Pagination/index.tsx
CHANGED
|
@@ -5,6 +5,10 @@ import PaginationDot from './PaginationDot';
|
|
|
5
5
|
import PaginationNumber from './PaginationNumber';
|
|
6
6
|
import PaginationScroll from './PaginationScroll';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Page indicator: `type` picks plain dots, dots on a dark pill or an `n/total`
|
|
10
|
+
* counter. `PaginationScroll` is the separate scroll-indicator variant.
|
|
11
|
+
*/
|
|
8
12
|
const Pagination: FC<PaginationProps> = props => {
|
|
9
13
|
const {type} = props;
|
|
10
14
|
|
package/Pagination/types.ts
CHANGED
|
@@ -5,9 +5,9 @@ export type PaginationProps = {
|
|
|
5
5
|
/**
|
|
6
6
|
* Optional. Defines the visual style of the Pagination component.
|
|
7
7
|
* - 'default': Represents the default style.
|
|
8
|
-
* - 'black_white': Represents
|
|
8
|
+
* - 'black_white': Represents dots on a translucent dark background.
|
|
9
9
|
* - 'number': Represents a style where pages are represented by numbers.
|
|
10
|
-
* - 'scroll':
|
|
10
|
+
* - 'scroll': Rendered as 'default'; use `PaginationScroll` instead.
|
|
11
11
|
*/
|
|
12
12
|
type?: 'default' | 'black_white' | 'number' | 'scroll';
|
|
13
13
|
|
package/Popup/PopupNotify.tsx
CHANGED
|
@@ -11,6 +11,13 @@ import { useModalLayout } from '../Application/modalLayout';
|
|
|
11
11
|
import { ModalIconButton } from '../Application/ModalIconButton';
|
|
12
12
|
import { splitHighlightText } from './utils';
|
|
13
13
|
|
|
14
|
+
const MAX_ROW_TITLE_LENGTH = 12;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Notify popup body: optional image, title, description, error code and one or
|
|
18
|
+
* two actions. Actions close via `onRequestClose` if given, else pop the modal
|
|
19
|
+
* and then run the callback.
|
|
20
|
+
*/
|
|
14
21
|
const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
15
22
|
image,
|
|
16
23
|
title = 'Title',
|
|
@@ -43,10 +50,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
43
50
|
descriptionStyle = { maxHeight: scaledMaxHeight };
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
/**
|
|
47
|
-
* on action popup
|
|
48
|
-
* @param callback
|
|
49
|
-
*/
|
|
50
53
|
const onAction = (callback?: () => void) => {
|
|
51
54
|
if (typeof onRequestClose === 'function') {
|
|
52
55
|
onRequestClose?.(callback);
|
|
@@ -56,9 +59,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
56
59
|
}
|
|
57
60
|
};
|
|
58
61
|
|
|
59
|
-
/**
|
|
60
|
-
* build main action of popup
|
|
61
|
-
*/
|
|
62
62
|
const buildAction = () => {
|
|
63
63
|
const renderRow = () => {
|
|
64
64
|
return (
|
|
@@ -126,10 +126,13 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
if (buttonDirection === 'auto') {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
(
|
|
132
|
-
|
|
129
|
+
const secondaryTitle = secondary?.title;
|
|
130
|
+
if (!secondaryTitle) {
|
|
131
|
+
return renderRow();
|
|
132
|
+
}
|
|
133
|
+
const isLongSecondary = secondaryTitle.length > MAX_ROW_TITLE_LENGTH;
|
|
134
|
+
const isLongPrimary = primary.title.length > MAX_ROW_TITLE_LENGTH;
|
|
135
|
+
if (isLongSecondary || isLongPrimary) {
|
|
133
136
|
return renderColumn();
|
|
134
137
|
}
|
|
135
138
|
return renderRow();
|
|
@@ -140,9 +143,7 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
140
143
|
return renderColumn();
|
|
141
144
|
};
|
|
142
145
|
|
|
143
|
-
/**
|
|
144
|
-
* build description text, highlighted keywords keep size/lineHeight and use weight 600
|
|
145
|
-
*/
|
|
146
|
+
/** Highlighted keywords use weight 600 but keep the body size and line height. */
|
|
146
147
|
const renderDescription = (text: string) => {
|
|
147
148
|
const segments = splitHighlightText(text, highlightText);
|
|
148
149
|
if (!segments) {
|
|
@@ -160,9 +161,6 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
160
161
|
});
|
|
161
162
|
};
|
|
162
163
|
|
|
163
|
-
/**
|
|
164
|
-
* build close action
|
|
165
|
-
*/
|
|
166
164
|
const buildCloseIcon = () => {
|
|
167
165
|
if (typeof onIconClose !== 'function') {
|
|
168
166
|
return null;
|
|
@@ -180,6 +178,28 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
180
178
|
);
|
|
181
179
|
};
|
|
182
180
|
|
|
181
|
+
const hasCloseIcon = typeof onIconClose === 'function';
|
|
182
|
+
// keeps the title clear of the close button on a text only popup
|
|
183
|
+
const titleStyle = !image && hasCloseIcon && styles.titleClearClose;
|
|
184
|
+
|
|
185
|
+
let descriptionContent: React.ReactNode = description;
|
|
186
|
+
if (typeof description === 'string') {
|
|
187
|
+
descriptionContent = (
|
|
188
|
+
<Text
|
|
189
|
+
accessibilityLabel={'description_popup_notify'}
|
|
190
|
+
typography={'body_default_regular'}
|
|
191
|
+
onTextLayout={({ nativeEvent: { lines } }) => {
|
|
192
|
+
const enableScroll = lines.length > 8.5;
|
|
193
|
+
if (enableScroll !== scrollContent) {
|
|
194
|
+
setScrollContent(enableScroll);
|
|
195
|
+
}
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
{renderDescription(description)}
|
|
199
|
+
</Text>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
183
203
|
const body = (
|
|
184
204
|
<>
|
|
185
205
|
{!!image && (
|
|
@@ -190,32 +210,12 @@ const PopupNotify: React.FC<PopupNotifyProps> = ({
|
|
|
190
210
|
typography={'header_default_bold'}
|
|
191
211
|
numberOfLines={2}
|
|
192
212
|
accessibilityLabel={'title_popup_notify'}
|
|
193
|
-
|
|
194
|
-
style={
|
|
195
|
-
!image &&
|
|
196
|
-
typeof onIconClose === 'function' &&
|
|
197
|
-
styles.titleClearClose
|
|
198
|
-
}
|
|
213
|
+
style={titleStyle}
|
|
199
214
|
>
|
|
200
215
|
{title}
|
|
201
216
|
</Text>
|
|
202
217
|
<Description style={[styles.description, descriptionStyle]}>
|
|
203
|
-
{
|
|
204
|
-
<Text
|
|
205
|
-
accessibilityLabel={'description_popup_notify'}
|
|
206
|
-
typography={'body_default_regular'}
|
|
207
|
-
onTextLayout={({ nativeEvent: { lines } }) => {
|
|
208
|
-
const enableScroll = lines.length > 8.5;
|
|
209
|
-
if (enableScroll !== scrollContent) {
|
|
210
|
-
setScrollContent(enableScroll);
|
|
211
|
-
}
|
|
212
|
-
}}
|
|
213
|
-
>
|
|
214
|
-
{renderDescription(description)}
|
|
215
|
-
</Text>
|
|
216
|
-
) : (
|
|
217
|
-
description
|
|
218
|
-
)}
|
|
218
|
+
{descriptionContent}
|
|
219
219
|
</Description>
|
|
220
220
|
{!!error && (
|
|
221
221
|
<View style={styles.information}>
|
package/Popup/PopupPromotion.tsx
CHANGED
|
@@ -12,6 +12,10 @@ const ICON_CLOSE_SIZE = 20;
|
|
|
12
12
|
// close button row under the image: marginTop + vertical padding + icon
|
|
13
13
|
const CLOSE_ROW_HEIGHT = Spacing.S * 3 + ICON_CLOSE_SIZE;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Promotion popup body: a tappable portrait image with a close button under it.
|
|
17
|
+
* Both close the popup first, then call `onActionClick` / `onIconClose`.
|
|
18
|
+
*/
|
|
15
19
|
const PopupPromotion: React.FC<PopupPromotionProps> = ({
|
|
16
20
|
image,
|
|
17
21
|
onIconClose,
|
|
@@ -33,10 +37,6 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
|
|
|
33
37
|
|
|
34
38
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
35
39
|
|
|
36
|
-
/**
|
|
37
|
-
* on action popup
|
|
38
|
-
* @param callback
|
|
39
|
-
*/
|
|
40
40
|
const onAction = (callback?: () => void) => {
|
|
41
41
|
if (typeof onRequestClose === 'function') {
|
|
42
42
|
onRequestClose?.(callback);
|
|
@@ -46,9 +46,6 @@ const PopupPromotion: React.FC<PopupPromotionProps> = ({
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
/**
|
|
50
|
-
* build close action
|
|
51
|
-
*/
|
|
52
49
|
const buildCloseIcon = () => {
|
|
53
50
|
return (
|
|
54
51
|
<View style={styles.iconCloseContainer}>
|
package/Popup/types.ts
CHANGED
|
@@ -56,7 +56,7 @@ export type PopupNotifyProps = {
|
|
|
56
56
|
onIconClose?: () => void;
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* onRequestClose
|
|
59
|
+
* `onRequestClose`: Optional. Closes the modal, then runs `callback`. ModalScreen injects it; without it the popup uses `navigator.pop()`.
|
|
60
60
|
*/
|
|
61
61
|
onRequestClose?: (callback?: () => void) => void;
|
|
62
62
|
};
|
|
@@ -73,17 +73,17 @@ export type PopupPromotionProps = {
|
|
|
73
73
|
image: string;
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* `
|
|
76
|
+
* `onIconClose`: Optional. A callback function that is called after the popup closes from the close icon.
|
|
77
77
|
*/
|
|
78
78
|
onIconClose?: () => void;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* `onActionClick`: Optional. A callback function that is called
|
|
81
|
+
* `onActionClick`: Optional. A callback function that is called after the popup closes from an image tap.
|
|
82
82
|
*/
|
|
83
83
|
onActionClick?: () => void;
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* onRequestClose
|
|
86
|
+
* `onRequestClose`: Optional. Closes the modal, then runs `callback`. ModalScreen injects it; without it the popup uses `navigator.pop()`.
|
|
87
87
|
*/
|
|
88
88
|
onRequestClose?: (callback?: () => void) => void;
|
|
89
89
|
};
|
package/Popup/utils.ts
CHANGED
package/Radio/index.tsx
CHANGED
|
@@ -11,6 +11,10 @@ import {
|
|
|
11
11
|
MiniAppContext,
|
|
12
12
|
} from '../Context';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Radio button, selected when `value === groupValue`. `onChange` is called on
|
|
16
|
+
* press without arguments; the parent updates `groupValue`.
|
|
17
|
+
*/
|
|
14
18
|
const Radio: FC<RadioProps> = ({
|
|
15
19
|
value,
|
|
16
20
|
groupValue,
|
package/Skeleton/index.tsx
CHANGED
|
@@ -21,6 +21,10 @@ import { Colors, Styles } from '../Consts';
|
|
|
21
21
|
import styles from './styles';
|
|
22
22
|
import { ScreenContext, SkeletonContext } from '../Context';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Shimmering placeholder that fills its parent. Reports loading to the screen
|
|
26
|
+
* context while mounted.
|
|
27
|
+
*/
|
|
24
28
|
const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
25
29
|
const screen = useContext<any>(ScreenContext);
|
|
26
30
|
const [width, setWidth] = useState(0);
|
|
@@ -84,6 +88,10 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
|
84
88
|
);
|
|
85
89
|
};
|
|
86
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Shares `loading` with descendants (e.g. `Image` shows a skeleton) and animates
|
|
93
|
+
* the layout change when it flips.
|
|
94
|
+
*/
|
|
87
95
|
const SkeletonProvider: React.FC<{ loading: boolean; children: any }> = ({
|
|
88
96
|
loading = true,
|
|
89
97
|
children,
|
package/Switch/index.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { Colors } from '../Consts';
|
|
|
6
6
|
import { useComponentId } from '../Application';
|
|
7
7
|
import { ComponentContext, MiniAppContext } from '../Context';
|
|
8
8
|
|
|
9
|
+
/** On/off toggle; `onChange` receives the negated `value`. */
|
|
9
10
|
const Switch: FC<SwitchProps> = ({
|
|
10
11
|
value = false,
|
|
11
12
|
onChange,
|
package/Tag/index.tsx
CHANGED
|
@@ -25,6 +25,10 @@ const textColor = {
|
|
|
25
25
|
grey: Colors.black_12,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Small status label with an optional leading icon. `customColor` replaces the
|
|
30
|
+
* palette color and turns the text white.
|
|
31
|
+
*/
|
|
28
32
|
const Tag: FC<TagProps> = ({
|
|
29
33
|
label = 'Label',
|
|
30
34
|
icon,
|
|
@@ -72,7 +76,7 @@ const Tag: FC<TagProps> = ({
|
|
|
72
76
|
`${componentName}/${label}`,
|
|
73
77
|
accessibilityLabel,
|
|
74
78
|
);
|
|
75
|
-
//
|
|
79
|
+
// Dev builds only accept `Colors.*_03` as customColor; release accepts any.
|
|
76
80
|
const validateCustomColor = (hex: string) => {
|
|
77
81
|
if (__DEV__) {
|
|
78
82
|
const colorCore: { [key: string]: string } = Colors;
|
package/Tag/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type TagProps = {
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* `size`: Optional. Defines the size of the Tag component.
|
|
11
|
-
* Defaults to '
|
|
11
|
+
* Defaults to 'large' if not provided.
|
|
12
12
|
*/
|
|
13
13
|
size?: 'medium' | 'large';
|
|
14
14
|
|
|
@@ -29,8 +29,8 @@ export type TagProps = {
|
|
|
29
29
|
style?: ViewStyle;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* `customColor`: Optional.
|
|
33
|
-
*
|
|
32
|
+
* `customColor`: Optional. Custom background color; the label turns white.
|
|
33
|
+
* Dev builds ignore values other than the `Colors.*_03` tokens.
|
|
34
34
|
*/
|
|
35
35
|
customColor?: string;
|
|
36
36
|
|