@react-navigation/native-stack 7.3.28 → 7.4.0
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/lib/module/views/NativeStackView.native.js +8 -1
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/module/views/useHeaderConfigProps.js +121 -4
- package/lib/module/views/useHeaderConfigProps.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +312 -7
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts +2 -31
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts.map +1 -1
- package/package.json +6 -4
- package/src/index.tsx +9 -0
- package/src/types.tsx +335 -5
- package/src/views/NativeStackView.native.tsx +8 -1
- package/src/views/useHeaderConfigProps.tsx +178 -5
package/src/types.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
Theme,
|
|
13
13
|
} from '@react-navigation/native';
|
|
14
14
|
import type {
|
|
15
|
+
ColorValue,
|
|
15
16
|
ImageSourcePropType,
|
|
16
17
|
StyleProp,
|
|
17
18
|
TextStyle,
|
|
@@ -20,6 +21,7 @@ import type {
|
|
|
20
21
|
import type {
|
|
21
22
|
ScreenProps,
|
|
22
23
|
ScreenStackHeaderConfigProps,
|
|
24
|
+
ScrollEdgeEffect,
|
|
23
25
|
SearchBarProps,
|
|
24
26
|
} from 'react-native-screens';
|
|
25
27
|
|
|
@@ -114,7 +116,7 @@ export type NativeStackHeaderProps = {
|
|
|
114
116
|
navigation: NativeStackNavigationProp<ParamListBase>;
|
|
115
117
|
};
|
|
116
118
|
|
|
117
|
-
export type
|
|
119
|
+
export type NativeStackHeaderItemProps = {
|
|
118
120
|
/**
|
|
119
121
|
* Tint color for the header.
|
|
120
122
|
*/
|
|
@@ -125,10 +127,10 @@ export type NativeStackHeaderRightProps = {
|
|
|
125
127
|
canGoBack?: boolean;
|
|
126
128
|
};
|
|
127
129
|
|
|
128
|
-
export type
|
|
130
|
+
export type NativeStackHeaderBackProps = NativeStackHeaderItemProps & {
|
|
129
131
|
/**
|
|
130
132
|
* Label text for the button. Usually the title of the previous screen.
|
|
131
|
-
* By default, this is only shown on iOS.
|
|
133
|
+
* By default, this is only shown on iOS 18.
|
|
132
134
|
*/
|
|
133
135
|
label?: string;
|
|
134
136
|
/**
|
|
@@ -137,6 +139,16 @@ export type NativeStackHeaderLeftProps = NativeStackHeaderRightProps & {
|
|
|
137
139
|
href?: string;
|
|
138
140
|
};
|
|
139
141
|
|
|
142
|
+
/**
|
|
143
|
+
* @deprecated Use `NativeStackHeaderBackProps` instead.
|
|
144
|
+
*/
|
|
145
|
+
export type NativeStackHeaderLeftProps = NativeStackHeaderBackProps;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated Use `NativeStackHeaderItemProps` instead.
|
|
149
|
+
*/
|
|
150
|
+
export type NativeStackHeaderRightProps = NativeStackHeaderItemProps;
|
|
151
|
+
|
|
140
152
|
export type NativeStackNavigationOptions = {
|
|
141
153
|
/**
|
|
142
154
|
* String that can be displayed in the header as a fallback for `headerTitle`.
|
|
@@ -281,12 +293,36 @@ export type NativeStackNavigationOptions = {
|
|
|
281
293
|
/**
|
|
282
294
|
* Function which returns a React Element to display on the left side of the header.
|
|
283
295
|
* This replaces the back button. See `headerBackVisible` to show the back button along side left element.
|
|
296
|
+
* Will be overriden by `headerLeftItems` on iOS.
|
|
284
297
|
*/
|
|
285
|
-
headerLeft?: (props:
|
|
298
|
+
headerLeft?: (props: NativeStackHeaderBackProps) => React.ReactNode;
|
|
286
299
|
/**
|
|
287
300
|
* Function which returns a React Element to display on the right side of the header.
|
|
301
|
+
* Will be overriden by `headerRightItems` on iOS.
|
|
288
302
|
*/
|
|
289
|
-
headerRight?: (props:
|
|
303
|
+
headerRight?: (props: NativeStackHeaderItemProps) => React.ReactNode;
|
|
304
|
+
/**
|
|
305
|
+
* Function which returns an array of items to display as on the left side of the header.
|
|
306
|
+
* Overrides `headerLeft`.
|
|
307
|
+
*
|
|
308
|
+
* This is an unstable API and might change in the future.
|
|
309
|
+
*
|
|
310
|
+
* @platform ios
|
|
311
|
+
*/
|
|
312
|
+
unstable_headerLeftItems?: (
|
|
313
|
+
props: NativeStackHeaderItemProps
|
|
314
|
+
) => NativeStackHeaderItem[];
|
|
315
|
+
/**
|
|
316
|
+
* Function which returns an array of items to display as on the right side of the header.
|
|
317
|
+
* Overrides `headerRight`.
|
|
318
|
+
*
|
|
319
|
+
* This is an unstable API and might change in the future.
|
|
320
|
+
*
|
|
321
|
+
* @platform ios
|
|
322
|
+
*/
|
|
323
|
+
unstable_headerRightItems?: (
|
|
324
|
+
props: NativeStackHeaderItemProps
|
|
325
|
+
) => NativeStackHeaderItem[];
|
|
290
326
|
/**
|
|
291
327
|
* String or a function that returns a React Element to be used by the header.
|
|
292
328
|
* Defaults to screen `title` or route name.
|
|
@@ -668,6 +704,24 @@ export type NativeStackNavigationOptions = {
|
|
|
668
704
|
* Only supported on iOS and Android.
|
|
669
705
|
*/
|
|
670
706
|
freezeOnBlur?: boolean;
|
|
707
|
+
/**
|
|
708
|
+
* Configures the scroll edge effect for the _content ScrollView_ (the ScrollView that is present in first descendants chain of the Screen).
|
|
709
|
+
* Depending on values set, it will blur the scrolling content below certain UI elements (Header Items, SearchBar)
|
|
710
|
+
* for the specifed edge of the ScrollView.
|
|
711
|
+
*
|
|
712
|
+
* When set in nested containers, i.e. ScreenStack inside BottomTabs, or the other way around,
|
|
713
|
+
* the ScrollView will use only the innermost one's config.
|
|
714
|
+
*
|
|
715
|
+
* @platform ios
|
|
716
|
+
*
|
|
717
|
+
* @supported iOS 26 or higher
|
|
718
|
+
*/
|
|
719
|
+
scrollEdgeEffects?: {
|
|
720
|
+
bottom?: ScrollEdgeEffect;
|
|
721
|
+
left?: ScrollEdgeEffect;
|
|
722
|
+
right?: ScrollEdgeEffect;
|
|
723
|
+
top?: ScrollEdgeEffect;
|
|
724
|
+
};
|
|
671
725
|
/**
|
|
672
726
|
* Footer component that can be used alongside formSheet stack presentation style.
|
|
673
727
|
*
|
|
@@ -683,6 +737,282 @@ export type NativeStackNavigationOptions = {
|
|
|
683
737
|
unstable_sheetFooter?: () => React.ReactNode;
|
|
684
738
|
};
|
|
685
739
|
|
|
740
|
+
type PlatformIconShared = {
|
|
741
|
+
type: 'imageSource';
|
|
742
|
+
imageSource: ImageSourcePropType;
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
type PlatformIconIOSSfSymbol = {
|
|
746
|
+
type: 'sfSymbol';
|
|
747
|
+
name: string;
|
|
748
|
+
};
|
|
749
|
+
|
|
750
|
+
type PlatformIconIOS = PlatformIconIOSSfSymbol | PlatformIconShared;
|
|
751
|
+
|
|
752
|
+
type SharedHeaderItem = {
|
|
753
|
+
/**
|
|
754
|
+
* Label of the item.
|
|
755
|
+
*/
|
|
756
|
+
label: string;
|
|
757
|
+
/**
|
|
758
|
+
* Style for the item label.
|
|
759
|
+
*/
|
|
760
|
+
labelStyle?: {
|
|
761
|
+
fontFamily?: string;
|
|
762
|
+
fontSize?: number;
|
|
763
|
+
fontWeight?: string;
|
|
764
|
+
color?: ColorValue;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* Icon for the item
|
|
768
|
+
*/
|
|
769
|
+
icon?: PlatformIconIOS;
|
|
770
|
+
/**
|
|
771
|
+
* The variant of the item.
|
|
772
|
+
* "prominent" only available from iOS 26.0 and later.
|
|
773
|
+
*
|
|
774
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/style-swift.property
|
|
775
|
+
*/
|
|
776
|
+
variant?: 'plain' | 'done' | 'prominent';
|
|
777
|
+
/**
|
|
778
|
+
* The tint color to apply to the item.
|
|
779
|
+
*
|
|
780
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/tintcolor
|
|
781
|
+
*/
|
|
782
|
+
tintColor?: ColorValue;
|
|
783
|
+
/**
|
|
784
|
+
* Whether the item is in a disabled state.
|
|
785
|
+
*/
|
|
786
|
+
disabled?: boolean;
|
|
787
|
+
/**
|
|
788
|
+
* The width of the item.
|
|
789
|
+
*
|
|
790
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/width
|
|
791
|
+
*/
|
|
792
|
+
width?: number;
|
|
793
|
+
/**
|
|
794
|
+
* Whether the background this item may share with other items in the bar should be hidden.
|
|
795
|
+
* Only available from iOS 26.0 and later.
|
|
796
|
+
*
|
|
797
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/hidessharedbackground
|
|
798
|
+
*/
|
|
799
|
+
hidesSharedBackground?: boolean;
|
|
800
|
+
/**
|
|
801
|
+
* Whether this item can share a background with other items.
|
|
802
|
+
* Only available from iOS 26.0 and later.
|
|
803
|
+
*
|
|
804
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/sharesbackground
|
|
805
|
+
*/
|
|
806
|
+
sharesBackground?: boolean;
|
|
807
|
+
/**
|
|
808
|
+
* An identifier used to match items across transitions.
|
|
809
|
+
* Only available from iOS 26.0 and later.
|
|
810
|
+
*
|
|
811
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/identifier
|
|
812
|
+
*/
|
|
813
|
+
identifier?: string;
|
|
814
|
+
/**
|
|
815
|
+
* A badge to display on a item.
|
|
816
|
+
* Only available from iOS 26.0 and later.
|
|
817
|
+
*
|
|
818
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitembadge
|
|
819
|
+
*/
|
|
820
|
+
badge?: {
|
|
821
|
+
/**
|
|
822
|
+
* The text to display in the badge.
|
|
823
|
+
*/
|
|
824
|
+
value: number | string;
|
|
825
|
+
/**
|
|
826
|
+
* Style of the badge.
|
|
827
|
+
*/
|
|
828
|
+
style?: {
|
|
829
|
+
color?: ColorValue;
|
|
830
|
+
backgroundColor?: ColorValue;
|
|
831
|
+
fontFamily?: string;
|
|
832
|
+
fontSize?: number;
|
|
833
|
+
fontWeight?: string;
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
/**
|
|
837
|
+
* Accessibility label for the item.
|
|
838
|
+
*/
|
|
839
|
+
accessibilityLabel?: string;
|
|
840
|
+
/**
|
|
841
|
+
* Accessibility hint for the item.
|
|
842
|
+
*/
|
|
843
|
+
accessibilityHint?: string;
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* A button item in the header.
|
|
848
|
+
*/
|
|
849
|
+
export type NativeStackHeaderItemButton = SharedHeaderItem & {
|
|
850
|
+
/**
|
|
851
|
+
* Type of the item.
|
|
852
|
+
*/
|
|
853
|
+
type: 'button';
|
|
854
|
+
/**
|
|
855
|
+
* Function to call when the item is pressed.
|
|
856
|
+
*/
|
|
857
|
+
onPress: () => void;
|
|
858
|
+
/**
|
|
859
|
+
* Whether the item is in a selected state.
|
|
860
|
+
*
|
|
861
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/isselected
|
|
862
|
+
*/
|
|
863
|
+
selected?: boolean;
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* An action item in a menu.
|
|
868
|
+
*/
|
|
869
|
+
export type NativeStackHeaderItemMenuAction = {
|
|
870
|
+
type: 'action';
|
|
871
|
+
/**
|
|
872
|
+
* Label for the menu item.
|
|
873
|
+
*/
|
|
874
|
+
label: string;
|
|
875
|
+
/**
|
|
876
|
+
* Icon for the menu item.
|
|
877
|
+
*/
|
|
878
|
+
icon?: PlatformIconIOSSfSymbol;
|
|
879
|
+
/**
|
|
880
|
+
* Function to call when the menu item is pressed.
|
|
881
|
+
*/
|
|
882
|
+
onPress: () => void;
|
|
883
|
+
/**
|
|
884
|
+
* The state of an action- or command-based menu item.
|
|
885
|
+
*
|
|
886
|
+
* Read more: https://developer.apple.com/documentation/uikit/uimenuelement/state
|
|
887
|
+
*/
|
|
888
|
+
state?: 'on' | 'off' | 'mixed';
|
|
889
|
+
/**
|
|
890
|
+
* Whether to apply disabled style to the item.
|
|
891
|
+
*
|
|
892
|
+
* Read more: https://developer.apple.com/documentation/uikit/uimenuelement/attributes/disabled
|
|
893
|
+
*/
|
|
894
|
+
disabled?: boolean;
|
|
895
|
+
/**
|
|
896
|
+
* Whether to apply destructive style to the item.
|
|
897
|
+
*
|
|
898
|
+
* Read more: https://developer.apple.com/documentation/uikit/uimenuelement/attributes/destructive
|
|
899
|
+
*/
|
|
900
|
+
destructive?: boolean;
|
|
901
|
+
/**
|
|
902
|
+
* Whether to apply hidden style to the item.
|
|
903
|
+
*
|
|
904
|
+
* Read more: https://developer.apple.com/documentation/uikit/uimenuelement/attributes/hidden
|
|
905
|
+
*/
|
|
906
|
+
hidden?: boolean;
|
|
907
|
+
/**
|
|
908
|
+
* Whether to keep the menu presented after firing the element’s action.
|
|
909
|
+
*
|
|
910
|
+
* Read more: https://developer.apple.com/documentation/uikit/uimenuelement/attributes/keepsmenupresented
|
|
911
|
+
*/
|
|
912
|
+
keepsMenuPresented?: boolean;
|
|
913
|
+
/**
|
|
914
|
+
* An elaborated title that explains the purpose of the action.
|
|
915
|
+
*
|
|
916
|
+
* On iOS, the system displays this title in the discoverability heads-up display (HUD).
|
|
917
|
+
* If this is not set, the HUD displays the title property.
|
|
918
|
+
*
|
|
919
|
+
* Read more: https://developer.apple.com/documentation/uikit/uiaction/discoverabilitytitle
|
|
920
|
+
*/
|
|
921
|
+
discoverabilityLabel?: string;
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* A submenu item that contains other menu items.
|
|
926
|
+
*/
|
|
927
|
+
export type NativeStackHeaderItemMenuSubmenu = {
|
|
928
|
+
type: 'submenu';
|
|
929
|
+
/**
|
|
930
|
+
* Label for the submenu item.
|
|
931
|
+
*/
|
|
932
|
+
label: string;
|
|
933
|
+
/**
|
|
934
|
+
* Icon for the submenu item.
|
|
935
|
+
*/
|
|
936
|
+
icon?: PlatformIconIOSSfSymbol;
|
|
937
|
+
/**
|
|
938
|
+
* Array of menu items (actions or submenus).
|
|
939
|
+
*/
|
|
940
|
+
items: NativeStackHeaderItemMenu['menu']['items'];
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* An item that shows a menu when pressed.
|
|
945
|
+
*/
|
|
946
|
+
export type NativeStackHeaderItemMenu = SharedHeaderItem & {
|
|
947
|
+
type: 'menu';
|
|
948
|
+
/**
|
|
949
|
+
* Whether the menu is a selection menu.
|
|
950
|
+
* Tapping an item in a selection menu will add a checkmark to the selected item.
|
|
951
|
+
*
|
|
952
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/changesselectionasprimaryaction
|
|
953
|
+
*/
|
|
954
|
+
changesSelectionAsPrimaryAction?: boolean;
|
|
955
|
+
/**
|
|
956
|
+
* Menu for the item.
|
|
957
|
+
*/
|
|
958
|
+
menu: {
|
|
959
|
+
/**
|
|
960
|
+
* Optional title to show on top of the menu.
|
|
961
|
+
*/
|
|
962
|
+
title?: string;
|
|
963
|
+
/**
|
|
964
|
+
* Array of menu items (actions or submenus).
|
|
965
|
+
*/
|
|
966
|
+
items: (
|
|
967
|
+
| NativeStackHeaderItemMenuAction
|
|
968
|
+
| NativeStackHeaderItemMenuSubmenu
|
|
969
|
+
)[];
|
|
970
|
+
};
|
|
971
|
+
};
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* An item to add spacing between other items in the header.
|
|
975
|
+
*/
|
|
976
|
+
export type NativeStackHeaderItemSpacing = {
|
|
977
|
+
type: 'spacing';
|
|
978
|
+
/**
|
|
979
|
+
* The amount of spacing to add.
|
|
980
|
+
*/
|
|
981
|
+
spacing: number;
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* A custom item to display any React Element in the header.
|
|
986
|
+
*/
|
|
987
|
+
export type NativeStackHeaderItemCustom = {
|
|
988
|
+
type: 'custom';
|
|
989
|
+
/**
|
|
990
|
+
* A React Element to display as the item.
|
|
991
|
+
*/
|
|
992
|
+
element: React.ReactElement;
|
|
993
|
+
/**
|
|
994
|
+
* Whether the background this item may share with other items in the bar should be hidden.
|
|
995
|
+
* Only available from iOS 26.0 and later.
|
|
996
|
+
*
|
|
997
|
+
* Read more: https://developer.apple.com/documentation/uikit/uibarbuttonitem/hidessharedbackground
|
|
998
|
+
*/
|
|
999
|
+
hidesSharedBackground?: boolean;
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* An item that can be displayed in the header.
|
|
1004
|
+
* It can be a button, a menu, spacing, or a custom element.
|
|
1005
|
+
*
|
|
1006
|
+
* On iOS 26, when showing items on the right side of the header,
|
|
1007
|
+
* if the items don't fit the available space, they will be collapsed into a menu automatically.
|
|
1008
|
+
* Items with `type: 'custom'` will not be included in this automatic collapsing behavior.
|
|
1009
|
+
*/
|
|
1010
|
+
export type NativeStackHeaderItem =
|
|
1011
|
+
| NativeStackHeaderItemButton
|
|
1012
|
+
| NativeStackHeaderItemMenu
|
|
1013
|
+
| NativeStackHeaderItemSpacing
|
|
1014
|
+
| NativeStackHeaderItemCustom;
|
|
1015
|
+
|
|
686
1016
|
export type NativeStackNavigatorProps = DefaultNavigatorOptions<
|
|
687
1017
|
ParamListBase,
|
|
688
1018
|
string | undefined,
|
|
@@ -112,7 +112,7 @@ const SceneView = ({
|
|
|
112
112
|
headerBackButtonMenuEnabled,
|
|
113
113
|
headerShown,
|
|
114
114
|
headerBackground,
|
|
115
|
-
headerTransparent
|
|
115
|
+
headerTransparent,
|
|
116
116
|
autoHideHomeIndicator,
|
|
117
117
|
keyboardHandlingEnabled,
|
|
118
118
|
navigationBarColor,
|
|
@@ -132,6 +132,7 @@ const SceneView = ({
|
|
|
132
132
|
statusBarTranslucent,
|
|
133
133
|
statusBarBackgroundColor,
|
|
134
134
|
unstable_sheetFooter,
|
|
135
|
+
scrollEdgeEffects,
|
|
135
136
|
freezeOnBlur,
|
|
136
137
|
contentStyle,
|
|
137
138
|
} = options;
|
|
@@ -335,6 +336,12 @@ const SceneView = ({
|
|
|
335
336
|
nativeBackButtonDismissalEnabled={false} // on Android
|
|
336
337
|
onHeaderBackButtonClicked={onHeaderBackButtonClicked}
|
|
337
338
|
preventNativeDismiss={isRemovePrevented} // on iOS
|
|
339
|
+
scrollEdgeEffects={{
|
|
340
|
+
bottom: scrollEdgeEffects?.bottom ?? 'automatic',
|
|
341
|
+
top: scrollEdgeEffects?.top ?? 'automatic',
|
|
342
|
+
left: scrollEdgeEffects?.left ?? 'automatic',
|
|
343
|
+
right: scrollEdgeEffects?.right ?? 'automatic',
|
|
344
|
+
}}
|
|
338
345
|
onNativeDismissCancelled={onNativeDismissCancelled}
|
|
339
346
|
// Unfortunately, because of the bug that exists on Fabric, where native event drivers
|
|
340
347
|
// for Animated objects are being created after the first notifications about the header height
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import { getHeaderTitle, HeaderTitle } from '@react-navigation/elements';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type Route,
|
|
4
|
+
type Theme,
|
|
5
|
+
useLocale,
|
|
6
|
+
useTheme,
|
|
7
|
+
} from '@react-navigation/native';
|
|
8
|
+
import color from 'color';
|
|
3
9
|
import { Platform, StyleSheet, type TextStyle, View } from 'react-native';
|
|
4
10
|
import {
|
|
11
|
+
type HeaderBarButtonItem,
|
|
12
|
+
type HeaderBarButtonItemMenuAction,
|
|
13
|
+
type HeaderBarButtonItemSubmenu,
|
|
5
14
|
isSearchBarAvailableForCurrentPlatform,
|
|
6
15
|
ScreenStackHeaderBackButtonImage,
|
|
7
16
|
ScreenStackHeaderCenterView,
|
|
17
|
+
type ScreenStackHeaderConfigProps,
|
|
8
18
|
ScreenStackHeaderLeftView,
|
|
9
19
|
ScreenStackHeaderRightView,
|
|
10
20
|
ScreenStackHeaderSearchBarView,
|
|
11
21
|
SearchBar,
|
|
12
22
|
} from 'react-native-screens';
|
|
13
23
|
|
|
14
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
NativeStackHeaderItem,
|
|
26
|
+
NativeStackHeaderItemMenuAction,
|
|
27
|
+
NativeStackHeaderItemMenuSubmenu,
|
|
28
|
+
NativeStackNavigationOptions,
|
|
29
|
+
} from '../types';
|
|
15
30
|
import { processFonts } from './FontProcessor';
|
|
16
31
|
|
|
17
32
|
type Props = NativeStackNavigationOptions & {
|
|
@@ -21,6 +36,112 @@ type Props = NativeStackNavigationOptions & {
|
|
|
21
36
|
route: Route<string>;
|
|
22
37
|
};
|
|
23
38
|
|
|
39
|
+
const processBarButtonItems = (
|
|
40
|
+
items: NativeStackHeaderItem[] | undefined,
|
|
41
|
+
colors: Theme['colors'],
|
|
42
|
+
fonts: Theme['fonts']
|
|
43
|
+
) => {
|
|
44
|
+
return items
|
|
45
|
+
?.map((item, index) => {
|
|
46
|
+
if (item.type === 'custom') {
|
|
47
|
+
// Handled with `ScreenStackHeaderLeftView` or `ScreenStackHeaderRightView`
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (item.type === 'spacing') {
|
|
52
|
+
if (item.spacing == null) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Spacing item must have a 'spacing' property defined: ${JSON.stringify(
|
|
55
|
+
item
|
|
56
|
+
)}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return item;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (item.type === 'button' || item.type === 'menu') {
|
|
64
|
+
if (item.type === 'menu' && item.menu == null) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
`Menu item must have a 'menu' property defined: ${JSON.stringify(
|
|
67
|
+
item
|
|
68
|
+
)}`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { badge, label, labelStyle, ...rest } = item;
|
|
73
|
+
|
|
74
|
+
let processedItem: HeaderBarButtonItem = {
|
|
75
|
+
...rest,
|
|
76
|
+
index,
|
|
77
|
+
title: label,
|
|
78
|
+
titleStyle: {
|
|
79
|
+
...fonts.regular,
|
|
80
|
+
...labelStyle,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (processedItem.type === 'menu' && item.type === 'menu') {
|
|
85
|
+
processedItem = {
|
|
86
|
+
...processedItem,
|
|
87
|
+
menu: {
|
|
88
|
+
...processedItem.menu,
|
|
89
|
+
items: item.menu.items.map(getMenuItem),
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (badge) {
|
|
95
|
+
const badgeBackgroundColor =
|
|
96
|
+
badge.style?.backgroundColor ?? colors.notification;
|
|
97
|
+
const badgeTextColor = color(badgeBackgroundColor).isLight()
|
|
98
|
+
? 'black'
|
|
99
|
+
: 'white';
|
|
100
|
+
|
|
101
|
+
processedItem = {
|
|
102
|
+
...processedItem,
|
|
103
|
+
badge: {
|
|
104
|
+
...badge,
|
|
105
|
+
value: String(badge.value),
|
|
106
|
+
style: {
|
|
107
|
+
backgroundColor: badgeBackgroundColor,
|
|
108
|
+
color: badgeTextColor,
|
|
109
|
+
...fonts.regular,
|
|
110
|
+
...badge.style,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return processedItem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Invalid item type: ${JSON.stringify(item)}. Valid types are 'button', 'menu', 'custom' and 'spacing'.`
|
|
121
|
+
);
|
|
122
|
+
})
|
|
123
|
+
.filter((item) => item != null);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const getMenuItem = (
|
|
127
|
+
item: NativeStackHeaderItemMenuAction | NativeStackHeaderItemMenuSubmenu
|
|
128
|
+
): HeaderBarButtonItemMenuAction | HeaderBarButtonItemSubmenu => {
|
|
129
|
+
const { label, ...rest } = item;
|
|
130
|
+
|
|
131
|
+
if (rest.type === 'submenu') {
|
|
132
|
+
return {
|
|
133
|
+
...rest,
|
|
134
|
+
title: label,
|
|
135
|
+
items: rest.items.map(getMenuItem),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...rest,
|
|
141
|
+
title: label,
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
24
145
|
export function useHeaderConfigProps({
|
|
25
146
|
headerBackImageSource,
|
|
26
147
|
headerBackButtonDisplayMode,
|
|
@@ -49,7 +170,9 @@ export function useHeaderConfigProps({
|
|
|
49
170
|
headerBack,
|
|
50
171
|
route,
|
|
51
172
|
title,
|
|
52
|
-
|
|
173
|
+
unstable_headerLeftItems: headerLeftItems,
|
|
174
|
+
unstable_headerRightItems: headerRightItems,
|
|
175
|
+
}: Props): ScreenStackHeaderConfigProps {
|
|
53
176
|
const { direction } = useLocale();
|
|
54
177
|
const { colors, fonts } = useTheme();
|
|
55
178
|
const tintColor =
|
|
@@ -187,11 +310,43 @@ export function useHeaderConfigProps({
|
|
|
187
310
|
|
|
188
311
|
const isCenterViewRenderedAndroid = headerTitleAlign === 'center';
|
|
189
312
|
|
|
313
|
+
const leftItems = headerLeftItems?.({
|
|
314
|
+
tintColor,
|
|
315
|
+
canGoBack,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
let rightItems = headerRightItems?.({
|
|
319
|
+
tintColor,
|
|
320
|
+
canGoBack,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (rightItems) {
|
|
324
|
+
// iOS renders right items in reverse order
|
|
325
|
+
// So we need to reverse them here to match the order
|
|
326
|
+
rightItems = [...rightItems].reverse();
|
|
327
|
+
}
|
|
328
|
+
|
|
190
329
|
const children = (
|
|
191
330
|
<>
|
|
192
331
|
{Platform.OS === 'ios' ? (
|
|
193
332
|
<>
|
|
194
|
-
{
|
|
333
|
+
{leftItems ? (
|
|
334
|
+
leftItems.map((item, index) => {
|
|
335
|
+
if (item.type === 'custom') {
|
|
336
|
+
return (
|
|
337
|
+
<ScreenStackHeaderLeftView
|
|
338
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
339
|
+
key={index}
|
|
340
|
+
hidesSharedBackground={item.hidesSharedBackground}
|
|
341
|
+
>
|
|
342
|
+
{item.element}
|
|
343
|
+
</ScreenStackHeaderLeftView>
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return null;
|
|
348
|
+
})
|
|
349
|
+
) : headerLeftElement != null ? (
|
|
195
350
|
<ScreenStackHeaderLeftView>
|
|
196
351
|
{headerLeftElement}
|
|
197
352
|
</ScreenStackHeaderLeftView>
|
|
@@ -247,7 +402,23 @@ export function useHeaderConfigProps({
|
|
|
247
402
|
{headerBackImageSource !== undefined ? (
|
|
248
403
|
<ScreenStackHeaderBackButtonImage source={headerBackImageSource} />
|
|
249
404
|
) : null}
|
|
250
|
-
{
|
|
405
|
+
{rightItems ? (
|
|
406
|
+
rightItems.map((item, index) => {
|
|
407
|
+
if (item.type === 'custom') {
|
|
408
|
+
return (
|
|
409
|
+
<ScreenStackHeaderRightView
|
|
410
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
411
|
+
key={index}
|
|
412
|
+
hidesSharedBackground={item.hidesSharedBackground}
|
|
413
|
+
>
|
|
414
|
+
{item.element}
|
|
415
|
+
</ScreenStackHeaderRightView>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return null;
|
|
420
|
+
})
|
|
421
|
+
) : headerRightElement != null ? (
|
|
251
422
|
<ScreenStackHeaderRightView>
|
|
252
423
|
{headerRightElement}
|
|
253
424
|
</ScreenStackHeaderRightView>
|
|
@@ -297,5 +468,7 @@ export function useHeaderConfigProps({
|
|
|
297
468
|
topInsetEnabled: headerTopInsetEnabled,
|
|
298
469
|
translucent: translucent === true,
|
|
299
470
|
children,
|
|
471
|
+
headerLeftBarButtonItems: processBarButtonItems(leftItems, colors, fonts),
|
|
472
|
+
headerRightBarButtonItems: processBarButtonItems(rightItems, colors, fonts),
|
|
300
473
|
} as const;
|
|
301
474
|
}
|