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