@momo-kits/foundation 0.169.7-beta.8 → 0.169.7-beta.9

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.
Files changed (36) hide show
  1. package/Application/BottomTab/ActiveIndicator.tsx +1 -5
  2. package/Application/BottomTab/BottomTabBar.styles.ts +1 -18
  3. package/Application/BottomTab/BottomTabBar.tsx +10 -8
  4. package/Application/BottomTab/CurvedContainer.tsx +2 -8
  5. package/Application/BottomTab/CustomBottomTabItem.tsx +2 -2
  6. package/Application/BottomTab/DuoTabBar.tsx +3 -15
  7. package/Application/BottomTab/metrics.ts +3 -23
  8. package/Application/Components/HeaderAnimated.tsx +13 -37
  9. package/Application/Components/HeaderBackground.tsx +3 -16
  10. package/Application/Components/HeaderExtendHeader.tsx +28 -41
  11. package/Application/Components/HeaderLeft.tsx +18 -4
  12. package/Application/Components/HeaderRight.styles.ts +13 -15
  13. package/Application/Components/HeaderTitle.shared.ts +11 -10
  14. package/Application/Components/HeaderToolkitAction.tsx +37 -12
  15. package/Application/Components/NavigationButton.tsx +6 -9
  16. package/Application/Components/SearchHeader.tsx +7 -20
  17. package/Application/StackScreen.tsx +0 -9
  18. package/Application/index.ts +1 -11
  19. package/Application/navigationOptions.tsx +15 -6
  20. package/Application/types.header.ts +2 -7
  21. package/Application/types.tabs.ts +1 -0
  22. package/Application/types.ts +0 -7
  23. package/Input/InputSearch.tsx +2 -18
  24. package/Input/index.tsx +1 -7
  25. package/Layout/Card.tsx +10 -10
  26. package/Layout/Item.tsx +2 -2
  27. package/Layout/Screen.tsx +0 -3
  28. package/Layout/Section.tsx +8 -9
  29. package/Layout/constants.ts +19 -0
  30. package/Layout/screenHeader.tsx +52 -55
  31. package/Layout/screenTypes.ts +2 -10
  32. package/Layout/types.ts +37 -1
  33. package/Layout/useGridSystem.ts +49 -0
  34. package/Layout/utils.ts +0 -8
  35. package/package.json +1 -1
  36. package/Application/headerLayout.ts +0 -158
@@ -1,7 +1,6 @@
1
1
  import { useCallback, useEffect, useRef } from 'react';
2
2
  import { StatusBar, StatusBarStyle } from 'react-native';
3
3
  import { StackNavigationOptions } from '@react-navigation/stack';
4
- import type { SharedValue } from 'react-native-reanimated';
5
4
  import Navigation from '../Application/Navigation';
6
5
  import { AnimatedHeader, SearchHeaderProps } from '../Application/types';
7
6
  import { exportHeaderTitle } from '../Application/navigationOptions';
@@ -13,11 +12,6 @@ import {
13
12
  SearchHeader,
14
13
  } from '../Application';
15
14
  import { DuoGlassBackground } from '../Application/Components/DuoGlassBackground';
16
- import {
17
- type HeaderContentLayout,
18
- useHeaderFrameOptions,
19
- useHeaderLayout,
20
- } from '../Application/headerLayout';
21
15
  import { getHeaderMargin } from './utils';
22
16
  import { ANIMATED_BANNER_HEIGHT } from './screenTypes';
23
17
  import { HeaderType } from './types';
@@ -35,31 +29,6 @@ type ScreenHeaderParams = {
35
29
  gradientColor?: string;
36
30
  headerBackground?: string;
37
31
  layoutOffset: number;
38
- contentLayout?: HeaderContentLayout;
39
- };
40
-
41
- /**
42
- * Transparent header for an `inputSearchProps` Screen: its title fades out over the
43
- * first 50pt of scroll while the search collapses into the bar.
44
- */
45
- const getSearchTitleOptions = (
46
- sharedValue: SharedValue<number>,
47
- ): StackNavigationOptions => {
48
- return {
49
- headerShown: true,
50
- headerTransparent: true,
51
- headerBackground: () => null,
52
- headerTitle: (props: any) => (
53
- <HeaderTitle
54
- {...props}
55
- interpolate={{
56
- inputRange: [0, 50],
57
- outputRange: [1, 0],
58
- }}
59
- animatedValue={sharedValue}
60
- />
61
- ),
62
- };
63
32
  };
64
33
 
65
34
  /**
@@ -79,12 +48,9 @@ export const useScreenHeader = ({
79
48
  gradientColor,
80
49
  headerBackground,
81
50
  layoutOffset,
82
- contentLayout,
83
51
  }: ScreenHeaderParams) => {
84
52
  const currentTint = useRef<string | undefined>(undefined);
85
53
  const appliedHeaderMode = useRef<string | undefined>(undefined);
86
- const headerLayout = useHeaderLayout(contentLayout);
87
- const frameOptions = useHeaderFrameOptions(contentLayout);
88
54
 
89
55
  // keepTitle leaves a title the screen set itself when the same type is re-applied
90
56
  const setHeaderType = useCallback(
@@ -113,7 +79,21 @@ export const useScreenHeader = ({
113
79
  headerTitle: (props: any) => <HeaderTitle {...props} />,
114
80
  };
115
81
  if (inputSearchProps) {
116
- options = getSearchTitleOptions(sharedValue);
82
+ options = {
83
+ headerShown: true,
84
+ headerTransparent: true,
85
+ headerBackground: () => null,
86
+ headerTitle: (props: any) => (
87
+ <HeaderTitle
88
+ {...props}
89
+ interpolate={{
90
+ inputRange: [0, 50],
91
+ outputRange: [1, 0],
92
+ }}
93
+ animatedValue={sharedValue}
94
+ />
95
+ ),
96
+ };
117
97
  }
118
98
  break;
119
99
 
@@ -147,7 +127,21 @@ export const useScreenHeader = ({
147
127
  headerTitle: (props: any) => <HeaderTitle {...props} />,
148
128
  };
149
129
  if (inputSearchProps) {
150
- options = getSearchTitleOptions(sharedValue);
130
+ options = {
131
+ headerShown: true,
132
+ headerTransparent: true,
133
+ headerBackground: () => null,
134
+ headerTitle: (props: any) => (
135
+ <HeaderTitle
136
+ {...props}
137
+ interpolate={{
138
+ inputRange: [0, 50],
139
+ outputRange: [1, 0],
140
+ }}
141
+ animatedValue={sharedValue}
142
+ />
143
+ ),
144
+ };
151
145
  }
152
146
  }
153
147
 
@@ -155,11 +149,7 @@ export const useScreenHeader = ({
155
149
  delete options.headerTitle;
156
150
  }
157
151
 
158
- navigation?.instance?.setOptions({
159
- header: undefined,
160
- ...frameOptions,
161
- ...options,
162
- });
152
+ navigation?.instance?.setOptions({ header: undefined, ...options });
163
153
  },
164
154
  [
165
155
  gradientColor,
@@ -170,7 +160,6 @@ export const useScreenHeader = ({
170
160
  useShadowHeader,
171
161
  hasSideBar,
172
162
  quickReturnOptions,
173
- frameOptions,
174
163
  ],
175
164
  );
176
165
 
@@ -218,7 +207,7 @@ export const useScreenHeader = ({
218
207
  };
219
208
  }
220
209
 
221
- navigation?.instance?.setOptions({ ...frameOptions, ...options });
210
+ navigation?.instance?.setOptions(options);
222
211
  },
223
212
  [
224
213
  gradientColor,
@@ -229,23 +218,32 @@ export const useScreenHeader = ({
229
218
  hasSideBar,
230
219
  layoutOffset,
231
220
  quickReturnOptions,
232
- frameOptions,
233
221
  ],
234
222
  );
235
223
 
236
224
  const setAnimatedSearch = useCallback(() => {
237
- const options = getSearchTitleOptions(sharedValue);
238
- navigation?.instance?.setOptions({
239
- header: undefined,
240
- ...frameOptions,
241
- ...options,
242
- });
243
- }, [navigation?.instance, sharedValue, frameOptions]);
225
+ const options: StackNavigationOptions = {
226
+ headerShown: true,
227
+ headerTransparent: true,
228
+ headerBackground: () => null,
229
+ headerTitle: (props: any) => (
230
+ <HeaderTitle
231
+ {...props}
232
+ interpolate={{
233
+ inputRange: [0, 50],
234
+ outputRange: [1, 0],
235
+ }}
236
+ animatedValue={sharedValue}
237
+ />
238
+ ),
239
+ };
240
+
241
+ navigation?.instance?.setOptions({ header: undefined, ...options });
242
+ }, [navigation?.instance, sharedValue]);
244
243
 
245
- // the search field spans the content (or the stretch frame), clear of the side bar
246
- // and status bar clusters
244
+ // the search field spans the content, clear of the side bar and status bar clusters
247
245
  const setSearchHeader = (params: SearchHeaderProps) => {
248
- let searchWidth = headerLayout.frameWidth;
246
+ let searchWidth = layout.width - 24;
249
247
  let searchLeft = Spacing.M;
250
248
  if (layout.sideBar) {
251
249
  searchWidth = layout.contentWidth - Spacing.M * 2;
@@ -256,7 +254,6 @@ export const useScreenHeader = ({
256
254
  searchWidth -= clearance.left + clearance.right;
257
255
  }
258
256
  const options: StackNavigationOptions = {
259
- ...frameOptions,
260
257
  headerRight: undefined,
261
258
  headerTitleContainerStyle: {
262
259
  width: searchWidth,
@@ -9,7 +9,6 @@ import {
9
9
  import { FloatingButtonProps } from './FloatingButton';
10
10
  import { HeaderType } from './types';
11
11
  import { InputRef } from '../Input';
12
- import type { HeaderContentLayout } from '../Application/headerLayout';
13
12
 
14
13
  export const ANIMATED_BANNER_HEIGHT = 210;
15
14
 
@@ -78,17 +77,10 @@ export interface ScreenProps extends ViewProps {
78
77
  layoutOffset?: -8 | -24 | -56 | number;
79
78
 
80
79
  /**
81
- * Optional. Width of the right header. Defaults to the toolkit width (84), or 0 when
80
+ * Optional. Width of the right header. Defaults to the toolkit width (73), or 0 when
82
81
  * iPhone Duo floats the toolkit into its side bar.
83
82
  */
84
- headerRightWidth?: 0 | 84 | 124 | number;
85
-
86
- /**
87
- * Optional. `stretch` centers back, title or search and the right cluster of the
88
- * header in a 600pt frame on wide windows (foldable inner display, tablet); the
89
- * background and status bar stay full width. Defaults to `grid`.
90
- */
91
- contentLayout?: HeaderContentLayout;
83
+ headerRightWidth?: 0 | 73 | 110 | number;
92
84
 
93
85
  /**
94
86
  * Optional. Props for the floating action button.
package/Layout/types.ts CHANGED
@@ -13,7 +13,43 @@ export type ScreenRef = {
13
13
  */
14
14
  setSearchHeader: (params: SearchHeaderProps) => void;
15
15
  };
16
- export type SpanNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
16
+ export type SpanNumber =
17
+ | 1
18
+ | 2
19
+ | 3
20
+ | 4
21
+ | 5
22
+ | 6
23
+ | 7
24
+ | 8
25
+ | 9
26
+ | 10
27
+ | 11
28
+ | 12
29
+ | 13
30
+ | 14
31
+ | 15
32
+ | 16
33
+ | 17
34
+ | 18
35
+ | 19
36
+ | 20
37
+ | 21
38
+ | 22
39
+ | 23
40
+ | 24
41
+ | 25
42
+ | 26
43
+ | 27
44
+ | 28
45
+ | 29
46
+ | 30
47
+ | 31
48
+ | 32
49
+ | 33
50
+ | 34
51
+ | 35
52
+ | 36;
17
53
 
18
54
  export type GridContextProps = {
19
55
  /**
@@ -0,0 +1,49 @@
1
+ import {
2
+ GRID_DESKTOP_COLUMNS,
3
+ GRID_DESKTOP_MIN_WIDTH,
4
+ GRID_PHONE_COLUMNS,
5
+ GRID_PHONE_MAX_WIDTH,
6
+ GRID_TABLET_COLUMNS,
7
+ GRID_TABLET_MIN_WIDTH,
8
+ } from './constants';
9
+ import { useScreenLayout } from './utils';
10
+
11
+ /**
12
+ * Column count for a content width: 12 below 800pt, 24 below 1100pt, 36 from there
13
+ */
14
+ const getGridColumns = (width: number) => {
15
+ if (width >= GRID_DESKTOP_MIN_WIDTH) {
16
+ return GRID_DESKTOP_COLUMNS;
17
+ }
18
+ if (width >= GRID_TABLET_MIN_WIDTH) {
19
+ return GRID_TABLET_COLUMNS;
20
+ }
21
+ return GRID_PHONE_COLUMNS;
22
+ };
23
+
24
+ /**
25
+ * Grid width for a content width: the 12-column grid stops at 600pt and is centered,
26
+ * the wider grids fill the content
27
+ */
28
+ const getGridWidth = (width: number) => {
29
+ if (width < GRID_TABLET_MIN_WIDTH) {
30
+ return Math.min(width, GRID_PHONE_MAX_WIDTH);
31
+ }
32
+ return width;
33
+ };
34
+
35
+ /**
36
+ * Grid for the current content width: column count, grid width and the horizontal
37
+ * offset that centers a grid narrower than the content
38
+ */
39
+ const useGridSystem = () => {
40
+ const { contentWidth } = useScreenLayout();
41
+ const gridWidth = getGridWidth(contentWidth);
42
+ return {
43
+ numberOfColumns: getGridColumns(contentWidth),
44
+ gridWidth,
45
+ gridOffset: (contentWidth - gridWidth) / 2,
46
+ };
47
+ };
48
+
49
+ export { useGridSystem };
package/Layout/utils.ts CHANGED
@@ -357,13 +357,6 @@ const useSideBarItemStyle = (
357
357
  };
358
358
  };
359
359
 
360
- /** Column count of the content grid: 24 on content wider than 600pt, otherwise 12. */
361
- const useGridSystem = () => {
362
- const { contentWidth } = useScreenLayout();
363
- const numberOfColumns = contentWidth > 600 ? 24 : 12;
364
- return { numberOfColumns };
365
- };
366
-
367
360
  export type { ScreenLayout, SideBarEdge, SideBarLayout, StatusBarClearance };
368
361
 
369
362
  export {
@@ -372,7 +365,6 @@ export {
372
365
  InnerEdgeStyles,
373
366
  getHeaderMargin,
374
367
  getHeaderRowOptions,
375
- useGridSystem,
376
368
  useScreenLayout,
377
369
  useSideBarItemStyle,
378
370
  validateChildren,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.169.7-beta.8",
3
+ "version": "0.169.7-beta.9",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},
@@ -1,158 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { useWindowDimensions, type ViewStyle } from 'react-native';
3
- import type { StackNavigationOptions } from '@react-navigation/stack';
4
- import { Spacing } from '../Consts';
5
- import { getHeaderMargin } from '../Layout/utils';
6
- import { FOLDABLE_MIN_WIDTH } from './modalLayout';
7
- import { isIPhoneDuo } from './utils';
8
-
9
- // Top navigation spec, shared with the Compose / SwiftUI kits
10
- const HEADER_FRAME_MAX_WIDTH = 600;
11
-
12
- /**
13
- * Top navigation geometry on every device
14
- * - buttonSize / iconSize: round back and shortcut buttons, icons inside them and the pill
15
- * - pillWidth / pillHeight: help center | home capsule
16
- * - clusterWidth: shortcut, gap and pill on the right of the bar
17
- * - searchHeight: search field inside the header
18
- * - titleGap: space between back and the title, and the title and the right cluster
19
- * - sidePadding: bar edge to back and to the right cluster
20
- * - gradientHeight: header gradient, kept at the phone height on wide windows
21
- * - bannerWidth: animated banner image, kept at the phone width on wide windows
22
- */
23
- const HeaderBarMetrics = {
24
- buttonSize: 32,
25
- iconSize: 20,
26
- pillWidth: 72,
27
- pillHeight: 32,
28
- clusterWidth: 112,
29
- searchHeight: 32,
30
- titleGap: Spacing.S,
31
- sidePadding: Spacing.M,
32
- gradientHeight: 154,
33
- bannerWidth: 375,
34
- };
35
-
36
- /**
37
- * `grid` keeps the header row at the bar edges, `stretch` centers it in a 600pt
38
- * frame on wide windows. Background and status bar always span the window.
39
- */
40
- type HeaderContentLayout = 'grid' | 'stretch';
41
-
42
- type HeaderLayout = typeof HeaderBarMetrics & {
43
- /**
44
- * Window whose short side is at least 600 (Galaxy Z Fold / Pixel Fold inner
45
- * display, tablets), iPhone Duo excluded. Phones, landscape included, are not.
46
- */
47
- isWide: boolean;
48
- /**
49
- * Window edge to back and to the right cluster: `sidePadding`, or centering the
50
- * row in the 600pt frame with `stretch` on a wide window.
51
- */
52
- frameInset: number;
53
- /**
54
- * Header row width between the two `frameInset`s.
55
- */
56
- frameWidth: number;
57
- };
58
-
59
- type HeaderLayoutInput = {
60
- width: number;
61
- height: number;
62
- isDuo: boolean;
63
- contentLayout?: HeaderContentLayout;
64
- };
65
-
66
- /**
67
- * Header geometry for a window, pure so it can be tested without a device.
68
- */
69
- const getHeaderLayout = ({
70
- width,
71
- height,
72
- isDuo,
73
- contentLayout = 'grid',
74
- }: HeaderLayoutInput): HeaderLayout => {
75
- const { sidePadding } = HeaderBarMetrics;
76
- const isWide = !isDuo && Math.min(width, height) >= FOLDABLE_MIN_WIDTH;
77
- let frameInset = sidePadding;
78
- if (isWide && contentLayout === 'stretch') {
79
- const centered = (width - HEADER_FRAME_MAX_WIDTH) / 2;
80
- frameInset = Math.max(sidePadding, centered);
81
- }
82
- return {
83
- ...HeaderBarMetrics,
84
- isWide,
85
- frameInset,
86
- frameWidth: width - frameInset * 2,
87
- };
88
- };
89
-
90
- /**
91
- * Widest a title can be between back and the right cluster of the header row.
92
- */
93
- const getTitleBudget = (layout: HeaderLayout) => {
94
- const { buttonSize, titleGap, clusterWidth } = layout;
95
- return layout.frameWidth - buttonSize - titleGap * 2 - clusterWidth;
96
- };
97
-
98
- /**
99
- * Header geometry for the current window; follows fold, unfold and rotation.
100
- */
101
- const useHeaderLayout = (contentLayout?: HeaderContentLayout): HeaderLayout => {
102
- const { width, height } = useWindowDimensions();
103
- const isDuo = isIPhoneDuo();
104
- return useMemo(() => {
105
- return getHeaderLayout({ width, height, isDuo, contentLayout });
106
- }, [width, height, isDuo, contentLayout]);
107
- };
108
-
109
- const TITLE_CONTAINER_RESET: ViewStyle = {
110
- marginLeft: 0,
111
- marginEnd: 0,
112
- marginStart: 0,
113
- marginHorizontal: 0,
114
- marginVertical: 0,
115
- margin: 0,
116
- };
117
-
118
- /**
119
- * React Navigation container styles that keep back, title and the right cluster
120
- * in the `stretch` frame. `undefined` for `grid`, and on iPhone Duo, which keeps
121
- * its own header row.
122
- */
123
- const useHeaderFrameOptions = (
124
- contentLayout?: HeaderContentLayout,
125
- ): StackNavigationOptions | undefined => {
126
- const layout = useHeaderLayout(contentLayout);
127
- const { width } = useWindowDimensions();
128
- const isDuo = isIPhoneDuo();
129
- return useMemo(() => {
130
- if (contentLayout !== 'stretch' || isDuo) {
131
- return undefined;
132
- }
133
- // the header content row already sits this far in on large iOS windows
134
- const shift = layout.frameInset - layout.sidePadding;
135
- const containerShift = Math.max(0, shift - getHeaderMargin(width));
136
- return {
137
- headerLeftContainerStyle: { paddingLeft: containerShift },
138
- headerRightContainerStyle: { paddingRight: containerShift },
139
- headerTitleContainerStyle: {
140
- ...TITLE_CONTAINER_RESET,
141
- left: layout.titleGap,
142
- maxWidth: getTitleBudget(layout),
143
- },
144
- };
145
- }, [contentLayout, isDuo, layout, width]);
146
- };
147
-
148
- export type { HeaderContentLayout, HeaderLayout };
149
-
150
- export {
151
- HeaderBarMetrics,
152
- HEADER_FRAME_MAX_WIDTH,
153
- TITLE_CONTAINER_RESET,
154
- getHeaderLayout,
155
- getTitleBudget,
156
- useHeaderLayout,
157
- useHeaderFrameOptions,
158
- };