@expo/ui 57.0.16 → 57.0.18

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 (105) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/DatePickerView.kt +31 -20
  4. package/android/src/main/java/expo/modules/ui/DateRangePickerView.kt +191 -0
  5. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +17 -0
  6. package/android/src/main/java/expo/modules/ui/RadioButtonView.kt +23 -1
  7. package/android/src/main/java/expo/modules/ui/menu/DropdownMenu.kt +2 -0
  8. package/android/src/main/java/expo/modules/ui/menu/DropdownMenuRecords.kt +2 -1
  9. package/build/community/bottom-sheet/BottomSheet.android.d.ts.map +1 -1
  10. package/build/community/bottom-sheet/BottomSheet.ios.d.ts.map +1 -1
  11. package/build/jetpack-compose/DatePicker/index.d.ts +88 -0
  12. package/build/jetpack-compose/DatePicker/index.d.ts.map +1 -1
  13. package/build/jetpack-compose/DropdownMenu/index.d.ts +7 -0
  14. package/build/jetpack-compose/DropdownMenu/index.d.ts.map +1 -1
  15. package/build/jetpack-compose/RadioButton/index.d.ts +19 -0
  16. package/build/jetpack-compose/RadioButton/index.d.ts.map +1 -1
  17. package/build/swift-ui/Button/index.d.ts +3 -1
  18. package/build/swift-ui/Button/index.d.ts.map +1 -1
  19. package/build/swift-ui/NavigationDestination/index.d.ts +30 -0
  20. package/build/swift-ui/NavigationDestination/index.d.ts.map +1 -0
  21. package/build/swift-ui/NavigationLink/index.d.ts +32 -0
  22. package/build/swift-ui/NavigationLink/index.d.ts.map +1 -0
  23. package/build/swift-ui/NavigationStack/index.d.ts +45 -0
  24. package/build/swift-ui/NavigationStack/index.d.ts.map +1 -0
  25. package/build/swift-ui/Toolbar/index.d.ts +40 -0
  26. package/build/swift-ui/Toolbar/index.d.ts.map +1 -0
  27. package/build/swift-ui/index.d.ts +4 -0
  28. package/build/swift-ui/index.d.ts.map +1 -1
  29. package/build/swift-ui/modifiers/containerBackground.d.ts +9 -4
  30. package/build/swift-ui/modifiers/containerBackground.d.ts.map +1 -1
  31. package/build/swift-ui/modifiers/index.d.ts +47 -10
  32. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  33. package/build/swift-ui/modifiers/presentationModifiers.d.ts +6 -3
  34. package/build/swift-ui/modifiers/presentationModifiers.d.ts.map +1 -1
  35. package/expo-module.config.json +1 -1
  36. package/ios/Button/Button.swift +13 -0
  37. package/ios/Button/ButtonProps.swift +6 -0
  38. package/ios/ExpoUIModule.swift +4 -0
  39. package/ios/Modifiers/ContainerBackgroundModifier.swift +4 -4
  40. package/ios/Modifiers/PresentationModifiers.swift +3 -3
  41. package/ios/Modifiers/ScrollClipDisabledModifier.swift +16 -0
  42. package/ios/Modifiers/ViewModifierRegistry.swift +35 -8
  43. package/ios/NavigationLinkView.swift +22 -0
  44. package/ios/NavigationStackView.swift +63 -0
  45. package/ios/SlotView.swift +5 -0
  46. package/ios/ToolbarView.swift +33 -0
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.16/expo.modules.ui-57.0.16-sources.jar → 57.0.18/expo.modules.ui-57.0.18-sources.jar} +0 -0
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18-sources.jar.md5 +1 -0
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18-sources.jar.sha1 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18-sources.jar.sha256 +1 -0
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18-sources.jar.sha512 +1 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.aar +0 -0
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.aar.md5 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.aar.sha1 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.aar.sha256 +1 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.aar.sha512 +1 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.16/expo.modules.ui-57.0.16.module → 57.0.18/expo.modules.ui-57.0.18.module} +22 -22
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.module.md5 +1 -0
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.module.sha1 +1 -0
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.module.sha256 +1 -0
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.module.sha512 +1 -0
  62. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{57.0.16/expo.modules.ui-57.0.16.pom → 57.0.18/expo.modules.ui-57.0.18.pom} +1 -1
  63. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.pom.md5 +1 -0
  64. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.pom.sha1 +1 -0
  65. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.pom.sha256 +1 -0
  66. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.18/expo.modules.ui-57.0.18.pom.sha512 +1 -0
  67. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  68. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  69. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  70. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  71. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  72. package/package.json +3 -3
  73. package/spm.config.json +1 -1
  74. package/src/community/bottom-sheet/BottomSheet.android.tsx +5 -3
  75. package/src/community/bottom-sheet/BottomSheet.ios.tsx +4 -3
  76. package/src/jetpack-compose/DatePicker/index.tsx +156 -0
  77. package/src/jetpack-compose/DropdownMenu/index.tsx +9 -0
  78. package/src/jetpack-compose/RadioButton/index.tsx +20 -0
  79. package/src/swift-ui/Button/index.tsx +3 -1
  80. package/src/swift-ui/NavigationDestination/index.tsx +39 -0
  81. package/src/swift-ui/NavigationLink/index.tsx +51 -0
  82. package/src/swift-ui/NavigationStack/index.tsx +75 -0
  83. package/src/swift-ui/Toolbar/index.tsx +64 -0
  84. package/src/swift-ui/index.tsx +4 -0
  85. package/src/swift-ui/modifiers/containerBackground.ts +10 -5
  86. package/src/swift-ui/modifiers/index.ts +67 -9
  87. package/src/swift-ui/modifiers/presentationModifiers.ts +7 -4
  88. package/src/universal/transformStyle.ios.ts +2 -2
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16-sources.jar.md5 +0 -1
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16-sources.jar.sha1 +0 -1
  91. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16-sources.jar.sha256 +0 -1
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16-sources.jar.sha512 +0 -1
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.aar +0 -0
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.aar.md5 +0 -1
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.aar.sha1 +0 -1
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.aar.sha256 +0 -1
  97. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.aar.sha512 +0 -1
  98. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.module.md5 +0 -1
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.module.sha1 +0 -1
  100. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.module.sha256 +0 -1
  101. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.module.sha512 +0 -1
  102. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.pom.md5 +0 -1
  103. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.pom.sha1 +0 -1
  104. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.pom.sha256 +0 -1
  105. package/local-maven-repo/expo/modules/ui/expo.modules.ui/57.0.16/expo.modules.ui-57.0.16.pom.sha512 +0 -1
@@ -8,6 +8,20 @@ export type AndroidVariant = 'picker' | 'input';
8
8
 
9
9
  export type DisplayedComponents = 'date' | 'hourAndMinute' | 'dateAndTime';
10
10
 
11
+ /**
12
+ * The date range reported by `DateRangePicker` and `DateRangePickerDialog`.
13
+ */
14
+ export type DateRangeSelection = {
15
+ /**
16
+ * The selected start date, or `null` when no start date is selected.
17
+ */
18
+ start: Date | null;
19
+ /**
20
+ * The selected end date, or `null` until the user selects the second date of the range.
21
+ */
22
+ end: Date | null;
23
+ };
24
+
11
25
  /**
12
26
  * Color overrides for the Material 3 DatePicker component.
13
27
  * All properties are optional — unset values use Material 3 theme defaults.
@@ -177,6 +191,10 @@ function convertSelectableDates(selectableDates?: {
177
191
  };
178
192
  }
179
193
 
194
+ function convertTimestampToDate(timestamp: number | null): Date | null {
195
+ return timestamp == null ? null : new Date(timestamp);
196
+ }
197
+
180
198
  function transformDateTimePickerProps(props: DateTimePickerProps): NativeDatePickerProps {
181
199
  const { modifiers, variant, initialDate, selectableDates, elementColors, color, ...rest } = props;
182
200
 
@@ -210,9 +228,107 @@ export function DateTimePicker(props: DateTimePickerProps) {
210
228
  return <DatePickerNativeView {...transformDateTimePickerProps(props)} />;
211
229
  }
212
230
 
231
+ export interface DateRangePickerProps {
232
+ /**
233
+ * The initially selected start date, as an ISO 8601 string.
234
+ */
235
+ initialStartDate?: string | null;
236
+ /**
237
+ * The initially selected end date, as an ISO 8601 string. It must be on or after `initialStartDate`.
238
+ */
239
+ initialEndDate?: string | null;
240
+ /**
241
+ * Called once when the component mounts with the initial range, and again whenever the selected date range changes.
242
+ * The end date is `null` while the user is selecting a range.
243
+ */
244
+ onDateRangeSelected?: (range: DateRangeSelection) => void;
245
+ /**
246
+ * The variant of the picker, which determines its appearance and behavior.
247
+ * @default 'picker'
248
+ */
249
+ variant?: AndroidVariant;
250
+ /**
251
+ * Show a button to toggle between variants on Android.
252
+ * @default true
253
+ */
254
+ showVariantToggle?: boolean;
255
+ /**
256
+ * The tint color to use on the picker elements.
257
+ */
258
+ color?: ColorValue;
259
+ /**
260
+ * Fine-grained color overrides for individual picker elements.
261
+ */
262
+ elementColors?: DatePickerElementColors;
263
+ /**
264
+ * Constrains which dates can be selected. `start` is the earliest selectable date and `end` is
265
+ * the latest.
266
+ */
267
+ selectableDates?: { start?: Date; end?: Date };
268
+ /**
269
+ * Modifiers for the component.
270
+ */
271
+ modifiers?: ModifierConfig[];
272
+ }
273
+
274
+ type NativeDateRangePickerProps = Omit<
275
+ DateRangePickerProps,
276
+ 'onDateRangeSelected' | 'initialStartDate' | 'initialEndDate' | 'selectableDates'
277
+ > & {
278
+ initialStartDate?: number | null;
279
+ initialEndDate?: number | null;
280
+ selectableDates?: { start?: number | null; end?: number | null } | null;
281
+ } & ViewEvent<'onDateRangeSelected', { start: number | null; end: number | null }>;
282
+
283
+ function transformDateRangePickerProps(
284
+ props: Omit<DateRangePickerProps, 'modifiers'>
285
+ ): Omit<NativeDateRangePickerProps, 'modifiers'> {
286
+ const {
287
+ initialStartDate,
288
+ initialEndDate,
289
+ selectableDates,
290
+ elementColors,
291
+ onDateRangeSelected,
292
+ ...rest
293
+ } = props;
294
+ return {
295
+ ...rest,
296
+ initialStartDate: initialStartDate ? new Date(initialStartDate).getTime() : null,
297
+ initialEndDate: initialEndDate ? new Date(initialEndDate).getTime() : null,
298
+ selectableDates: convertSelectableDates(selectableDates),
299
+ onDateRangeSelected: ({ nativeEvent: { start, end } }) => {
300
+ onDateRangeSelected?.({
301
+ start: convertTimestampToDate(start),
302
+ end: convertTimestampToDate(end),
303
+ });
304
+ },
305
+ ...(elementColors != null ? { elementColors } : undefined),
306
+ };
307
+ }
308
+
309
+ const DateRangePickerNativeView: React.ComponentType<NativeDateRangePickerProps> =
310
+ requireNativeView('ExpoUI', 'DateRangePickerView');
311
+
312
+ /**
313
+ * Renders an inline Material 3 date range picker.
314
+ */
315
+ export function DateRangePicker(props: DateRangePickerProps) {
316
+ const { modifiers, ...rest } = props;
317
+ const nativeProps: NativeDateRangePickerProps = {
318
+ modifiers,
319
+ ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
320
+ ...transformDateRangePickerProps(rest),
321
+ };
322
+ return <DateRangePickerNativeView {...nativeProps} />;
323
+ }
324
+
213
325
  // -- Dialog views (used internally by the compat layer) ---------------------
214
326
 
215
327
  export interface DatePickerDialogProps {
328
+ /**
329
+ * The initially selected date. When omitted, the dialog opens on the current month with no
330
+ * selection and the confirm button stays disabled until a date is picked.
331
+ */
216
332
  initialDate?: string | null;
217
333
  variant?: AndroidVariant;
218
334
  showVariantToggle?: boolean;
@@ -260,6 +376,46 @@ export function DatePickerDialog(props: DatePickerDialogProps) {
260
376
  return <DatePickerDialogNativeView {...nativeProps} />;
261
377
  }
262
378
 
379
+ export interface DateRangePickerDialogProps extends Omit<DateRangePickerProps, 'modifiers'> {
380
+ /**
381
+ * The label for the button that confirms the selected range.
382
+ * Defaults to the system "Ok" string.
383
+ */
384
+ confirmButtonLabel?: string;
385
+ /**
386
+ * The label for the button that dismisses the dialog.
387
+ * Defaults to the system "Cancel" string.
388
+ */
389
+ dismissButtonLabel?: string;
390
+ /** The tint color to use on the picker elements and dialog buttons. */
391
+ color?: ColorValue;
392
+ /** Callback function that is called when the user confirms a complete date range. */
393
+ onDateRangeSelected?: (range: DateRangeSelection) => void;
394
+ /** Callback function that is called when the dialog is dismissed. */
395
+ onDismissRequest: () => void;
396
+ }
397
+
398
+ type NativeDateRangePickerDialogProps = Omit<NativeDateRangePickerProps, 'modifiers'> &
399
+ Pick<DateRangePickerDialogProps, 'confirmButtonLabel' | 'dismissButtonLabel'> &
400
+ ViewEvent<'onDismissRequest', void>;
401
+
402
+ const DateRangePickerDialogNativeView: React.ComponentType<NativeDateRangePickerDialogProps> =
403
+ requireNativeView('ExpoUI', 'DateRangePickerDialogView');
404
+
405
+ /**
406
+ * Renders a modal Material 3 date range picker.
407
+ */
408
+ export function DateRangePickerDialog(props: DateRangePickerDialogProps) {
409
+ const { confirmButtonLabel, dismissButtonLabel, onDismissRequest, ...rest } = props;
410
+ const nativeProps: NativeDateRangePickerDialogProps = {
411
+ ...transformDateRangePickerProps(rest),
412
+ confirmButtonLabel,
413
+ dismissButtonLabel,
414
+ onDismissRequest,
415
+ };
416
+ return <DateRangePickerDialogNativeView {...nativeProps} />;
417
+ }
418
+
263
419
  export interface TimePickerDialogProps {
264
420
  initialDate?: string | null;
265
421
  is24Hour?: boolean;
@@ -43,6 +43,14 @@ export interface DropdownMenuProps {
43
43
  */
44
44
  color?: ColorValue;
45
45
 
46
+ /**
47
+ * Shadow elevation of the dropdown menu popup in dp. Set to `0` to remove the shadow.
48
+ * Defaults to Material3's `MenuDefaults.ShadowElevation`.
49
+ *
50
+ * @platform android
51
+ */
52
+ shadowElevation?: number;
53
+
46
54
  /**
47
55
  * Optional styles to apply to the `DropdownMenu`.
48
56
  */
@@ -58,6 +66,7 @@ type NativeMenuProps = {
58
66
  expanded?: boolean;
59
67
  onDismissRequest?: () => void;
60
68
  color?: ColorValue;
69
+ shadowElevation?: number;
61
70
  style?: StyleProp<ViewStyle>;
62
71
  modifiers?: ModifierConfig[];
63
72
  children?: ReactNode;
@@ -1,17 +1,37 @@
1
1
  import { requireNativeView } from 'expo';
2
+ import type { ColorValue } from 'react-native';
2
3
 
3
4
  import { type ModifierConfig, type ViewEvent } from '../../types';
4
5
  import { createViewModifierEventListener } from '../modifiers/utils';
5
6
 
7
+ /**
8
+ * Colors for the radio button in different states.
9
+ */
10
+ export type RadioButtonColors = {
11
+ selectedColor?: ColorValue;
12
+ unselectedColor?: ColorValue;
13
+ disabledSelectedColor?: ColorValue;
14
+ disabledUnselectedColor?: ColorValue;
15
+ };
16
+
6
17
  export interface RadioButtonProps {
7
18
  /**
8
19
  * Whether the radio button is selected.
9
20
  */
10
21
  selected: boolean;
22
+ /**
23
+ * Whether the radio button is enabled.
24
+ * @default true
25
+ */
26
+ enabled?: boolean;
11
27
  /**
12
28
  * Callback that is called when the radio button is clicked.
13
29
  */
14
30
  onClick?: () => void;
31
+ /**
32
+ * Colors for the radio button in different states.
33
+ */
34
+ colors?: RadioButtonColors;
15
35
  /**
16
36
  * Modifiers for the component.
17
37
  */
@@ -10,8 +10,10 @@ import { type CommonViewModifierProps } from '../types';
10
10
  * - `default` - The default button role.
11
11
  * - `cancel` - A button that cancels the current operation.
12
12
  * - `destructive` - A button that deletes data or performs a destructive action.
13
+ * - `close` - A button that closes the view it is presented in. Given no `label` and no children,
14
+ * the system draws it as an xmark.
13
15
  */
14
- export type ButtonRole = 'default' | 'cancel' | 'destructive';
16
+ export type ButtonRole = 'default' | 'cancel' | 'destructive' | 'close';
15
17
 
16
18
  export interface ButtonProps extends CommonViewModifierProps {
17
19
  /**
@@ -0,0 +1,39 @@
1
+ import { type ReactNode } from 'react';
2
+
3
+ import { Slot } from '../SlotView';
4
+
5
+ export interface NavigationDestinationProps {
6
+ /**
7
+ * The `NavigationLink` value this destination is presented for.
8
+ */
9
+ value: string;
10
+ /**
11
+ * The view pushed onto the stack for `value`.
12
+ */
13
+ children: ReactNode;
14
+ }
15
+
16
+ /**
17
+ * Associates a destination view with a `NavigationLink` value, matching SwiftUI's
18
+ * `navigationDestination(for:destination:)`. It must be a direct child of the `NavigationStack`,
19
+ * however deeply the link that pushes it is nested inside that stack.
20
+ *
21
+ * Keep it mounted so the pushed screen already carries its `navigationTitle` when the slide
22
+ * starts, or render one for each value on the stack's `path` to build screens on demand.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * <NavigationDestination value="settings">
27
+ * <SettingsScreen />
28
+ * </NavigationDestination>
29
+ * ```
30
+ *
31
+ * @platform ios
32
+ */
33
+ export function NavigationDestination(props: NavigationDestinationProps) {
34
+ return (
35
+ <Slot name="destination" extraProps={{ value: props.value }}>
36
+ {props.children}
37
+ </Slot>
38
+ );
39
+ }
@@ -0,0 +1,51 @@
1
+ import { requireNativeView } from 'expo';
2
+ import { type ComponentType, type ReactNode } from 'react';
3
+
4
+ import { createViewModifierEventListener } from '../modifiers/utils';
5
+ import { type CommonViewModifierProps } from '../types';
6
+
7
+ export interface NavigationLinkProps extends CommonViewModifierProps {
8
+ /**
9
+ * The value to append to the enclosing `NavigationStack`'s path when the link is tapped. The
10
+ * stack presents the `NavigationDestination` whose `value` matches.
11
+ */
12
+ value: string;
13
+ /**
14
+ * A view that describes the link.
15
+ */
16
+ children: ReactNode;
17
+ }
18
+
19
+ const NavigationLinkNativeView: ComponentType<NavigationLinkProps> = requireNativeView(
20
+ 'ExpoUI',
21
+ 'NavigationLinkView'
22
+ );
23
+
24
+ /**
25
+ * A view that controls a navigation presentation. In a `List` it renders as a row with a
26
+ * disclosure chevron, and tapping it pushes the matching `NavigationDestination`.
27
+ *
28
+ * The link has to be inside the `NavigationStack` itself. Content presented from the stack, such
29
+ * as a `BottomSheet` or a `Popover`, is its own presentation context and does not reach the
30
+ * stack's destinations, so give that content its own `NavigationStack`.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * <NavigationLink value="settings">
35
+ * <Text>Settings</Text>
36
+ * </NavigationLink>
37
+ * ```
38
+ *
39
+ * @platform ios
40
+ */
41
+ export function NavigationLink(props: NavigationLinkProps) {
42
+ const { modifiers, children, ...restProps } = props;
43
+ return (
44
+ <NavigationLinkNativeView
45
+ modifiers={modifiers}
46
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
47
+ {...restProps}>
48
+ {children}
49
+ </NavigationLinkNativeView>
50
+ );
51
+ }
@@ -0,0 +1,75 @@
1
+ import { requireNativeView } from 'expo';
2
+ import { type ComponentType, type ReactNode } from 'react';
3
+ import { type NativeSyntheticEvent } from 'react-native';
4
+
5
+ import { createViewModifierEventListener } from '../modifiers/utils';
6
+ import { type CommonViewModifierProps } from '../types';
7
+
8
+ export interface NavigationStackProps extends CommonViewModifierProps {
9
+ /**
10
+ * The root view of the stack, and the `NavigationDestination` views the stack can push. It is
11
+ * displayed inside a navigation bar.
12
+ */
13
+ children: ReactNode;
14
+ /**
15
+ * The values of the views pushed on top of the root view, in the order they were pushed.
16
+ * Leave it undefined to let the stack manage its own path.
17
+ */
18
+ path?: string[];
19
+ /**
20
+ * Callback function that is called when the stack pushes or pops a view, for example when a
21
+ * `NavigationLink` is tapped or the user swipes back. Gets called with the new path.
22
+ *
23
+ * When `path` is set, the stack is controlled: a push takes effect once the new path comes back
24
+ * through this prop.
25
+ */
26
+ onPathChange?: (path: string[]) => void;
27
+ }
28
+
29
+ type NativeNavigationStackProps = Omit<NavigationStackProps, 'onPathChange'> & {
30
+ onPathChange?: (event: NativeSyntheticEvent<{ path: string[] }>) => void;
31
+ };
32
+
33
+ const NavigationStackNativeView: ComponentType<NativeNavigationStackProps> = requireNativeView(
34
+ 'ExpoUI',
35
+ 'NavigationStackView'
36
+ );
37
+
38
+ /**
39
+ * A view that displays a root view and enables you to present additional views over the root view.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * const [path, setPath] = useState<string[]>([]);
44
+ *
45
+ * <NavigationStack path={path} onPathChange={setPath}>
46
+ * <List>
47
+ * <NavigationLink value="settings">
48
+ * <Text>Settings</Text>
49
+ * </NavigationLink>
50
+ * </List>
51
+ * <NavigationDestination value="settings">
52
+ * <SettingsScreen />
53
+ * </NavigationDestination>
54
+ * </NavigationStack>
55
+ * ```
56
+ *
57
+ * @platform ios
58
+ */
59
+ export function NavigationStack(props: NavigationStackProps) {
60
+ const { modifiers, onPathChange, ...restProps } = props;
61
+ return (
62
+ <NavigationStackNativeView
63
+ modifiers={modifiers}
64
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
65
+ {...restProps}
66
+ onPathChange={
67
+ onPathChange
68
+ ? ({ nativeEvent: { path } }) => {
69
+ onPathChange(path);
70
+ }
71
+ : undefined
72
+ }
73
+ />
74
+ );
75
+ }
@@ -0,0 +1,64 @@
1
+ import { requireNativeView } from 'expo';
2
+
3
+ import { Slot } from '../SlotView';
4
+ import { createViewModifierEventListener } from '../modifiers/utils';
5
+ import { type CommonViewModifierProps } from '../types';
6
+
7
+ export interface ToolbarProps extends CommonViewModifierProps {
8
+ /**
9
+ * The view the toolbar belongs to, together with the `Toolbar.Content` that fills it.
10
+ */
11
+ children: React.ReactNode;
12
+ }
13
+
14
+ export interface ToolbarContentProps {
15
+ /**
16
+ * The items to place in the toolbar.
17
+ */
18
+ children: React.ReactNode;
19
+ }
20
+
21
+ const ToolbarNativeView: React.ComponentType<ToolbarProps> = requireNativeView(
22
+ 'ExpoUI',
23
+ 'ToolbarView'
24
+ );
25
+
26
+ /**
27
+ * The items placed in the toolbar of the view `Toolbar` wraps.
28
+ */
29
+ function ToolbarContent(props: ToolbarContentProps) {
30
+ return <Slot name="content">{props.children}</Slot>;
31
+ }
32
+
33
+ /**
34
+ * Adds a toolbar to the view it wraps, matching SwiftUI's `toolbar(content:)`.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <NavigationStack>
39
+ * <Toolbar>
40
+ * <Text>Bird description</Text>
41
+ * <Toolbar.Content>
42
+ * <Button role="close" onPress={() => setIsPresented(false)} />
43
+ * </Toolbar.Content>
44
+ * </Toolbar>
45
+ * </NavigationStack>
46
+ * ```
47
+ *
48
+ * @platform ios
49
+ */
50
+ function ToolbarComponent(props: ToolbarProps) {
51
+ const { modifiers, children, ...restProps } = props;
52
+ return (
53
+ <ToolbarNativeView
54
+ modifiers={modifiers}
55
+ {...(modifiers ? createViewModifierEventListener(modifiers) : undefined)}
56
+ {...restProps}>
57
+ {children}
58
+ </ToolbarNativeView>
59
+ );
60
+ }
61
+
62
+ const Toolbar = Object.assign(ToolbarComponent, { Content: ToolbarContent });
63
+
64
+ export { Toolbar };
@@ -25,6 +25,9 @@ export * from './ZStack';
25
25
  export * from './Group';
26
26
  export * from './List';
27
27
  export * from './Menu';
28
+ export * from './NavigationDestination';
29
+ export * from './NavigationLink';
30
+ export * from './NavigationStack';
28
31
  export * from './Picker';
29
32
  export * from './ProgressView';
30
33
  export * from './Section';
@@ -38,6 +41,7 @@ export { useNativeState } from '../State';
38
41
  export { withAnimation, type WithAnimationCompletionCriteria } from './withAnimation';
39
42
  export * from './SyncToggle';
40
43
  export * from './TabView';
44
+ export * from './Toolbar';
41
45
  export * from './Toggle';
42
46
  export {
43
47
  TextField,
@@ -1,13 +1,18 @@
1
1
  import { createModifier } from './createModifier';
2
- import type { Color } from './types';
2
+ import { resolveShapeStyle, type ShapeStyle } from './shapeStyle';
3
3
 
4
4
  export type ContainerBackgroundPlacement = 'widget' | 'navigation' | 'navigationSplitView';
5
5
 
6
6
  /**
7
- * Sets the container background of the enclosing container using a view.
8
- * @param color - The color to set as the background of the container.
7
+ * Sets the container background of the enclosing container.
8
+ *
9
+ * > **Note:** `navigation` and `navigationSplitView` require iOS 18. On iOS 17 they fall back to
10
+ * > the `widget` placement.
11
+ *
12
+ * @param style - Any [`ShapeStyle`](#shapestyle): a color, a hierarchical style, a material, or a gradient.
9
13
  * @param container - The type of container to apply the background to.
14
+ * @platform ios 17.0+
10
15
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/containerbackground(_:for:)).
11
16
  */
12
- export const containerBackground = (color: Color, container: ContainerBackgroundPlacement) =>
13
- createModifier('containerBackground', { color, container });
17
+ export const containerBackground = (style: ShapeStyle, container: ContainerBackgroundPlacement) =>
18
+ createModifier('containerBackground', { style: resolveShapeStyle(style), container });
@@ -287,13 +287,38 @@ export const clipShape = (
287
287
  cornerRadius?: number
288
288
  ) => createModifier('clipShape', { shape, cornerRadius });
289
289
 
290
+ /**
291
+ * The parameters of the `border` modifier.
292
+ */
293
+ export type BorderParams =
294
+ | {
295
+ /**
296
+ * The style painted along the border. No border is drawn when the style is not available on
297
+ * the running platform, unlike `strokeBorder`, which falls back to the foreground style.
298
+ */
299
+ content: ShapeStyle;
300
+ /** The border width. @default 1 */
301
+ width?: number;
302
+ }
303
+ | {
304
+ /**
305
+ * @deprecated Use `content`, which takes any `ShapeStyle` and not only a color.
306
+ */
307
+ color: Color;
308
+ /** The border width. @default 1 */
309
+ width?: number;
310
+ };
311
+
290
312
  /**
291
313
  * Adds a border to a view.
292
- * @param params - The border parameters. Color and width.
314
+ * @param params - The border parameters. The style painted along the border, and its width.
293
315
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/border(_:width:)).
294
316
  */
295
- export const border = (params: { color: Color; width?: number }) =>
296
- createModifier('border', params);
317
+ export const border = (params: BorderParams) =>
318
+ createModifier('border', {
319
+ content: resolveShapeStyle('content' in params ? params.content : params.color),
320
+ width: params.width,
321
+ });
297
322
 
298
323
  /**
299
324
  * The characteristics of a stroke that traces a path.
@@ -316,10 +341,15 @@ export type StrokeStyle = {
316
341
 
317
342
  /**
318
343
  * Strokes an inset border along the view's shape.
319
- * @param params - The stroke parameters. Color (omit for the foreground style), style, antialiased, shape and cornerRadius.
344
+ * @param params - The stroke parameters. The style painted along the stroke (omit for the foreground style), the stroke style, antialiased, shape and cornerRadius.
320
345
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/insettableshape/strokeborder(_:style:antialiased:)).
321
346
  */
322
347
  export const strokeBorder = (params: {
348
+ /** The style painted along the stroke. Omit to use the foreground style. */
349
+ content?: ShapeStyle;
350
+ /**
351
+ * @deprecated Use `content`, which takes any `ShapeStyle` and not only a color.
352
+ */
323
353
  color?: Color;
324
354
  style?: StrokeStyle;
325
355
  antialiased?: boolean;
@@ -331,7 +361,14 @@ export const strokeBorder = (params: {
331
361
  | 'roundedRectangle'
332
362
  | 'containerRelativeShape';
333
363
  cornerRadius?: number;
334
- }) => createModifier('strokeBorder', params);
364
+ }) => {
365
+ const { content, color, ...rest } = params;
366
+ const shapeStyle = content ?? color;
367
+ return createModifier('strokeBorder', {
368
+ ...rest,
369
+ content: shapeStyle === undefined ? undefined : resolveShapeStyle(shapeStyle),
370
+ });
371
+ };
335
372
 
336
373
  /**
337
374
  * Applies scaling transformation.
@@ -495,11 +532,11 @@ export const italic = () => createModifier('italic', {});
495
532
  export const monospacedDigit = () => createModifier('monospacedDigit', {});
496
533
 
497
534
  /**
498
- * Sets the tint color of a view.
499
- * @param color - The tint color (hex string). For example, `#FF0000`.
535
+ * Sets the tint of a view.
536
+ * @param tint - Any [`ShapeStyle`](#shapestyle): a color, a hierarchical style, a material, or a gradient.
500
537
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/tint(_:)).
501
538
  */
502
- export const tint = (color: Color) => createModifier('tint', { color });
539
+ export const tint = (tint: ShapeStyle) => createModifier('tint', { tint: resolveShapeStyle(tint) });
503
540
 
504
541
  /**
505
542
  * Hides or shows a view.
@@ -749,6 +786,18 @@ export const scrollDismissesKeyboard = (
749
786
  export const scrollDisabled = (disabled: boolean = true) =>
750
787
  createModifier('scrollDisabled', { disabled });
751
788
 
789
+ /**
790
+ * Disables or enables clipping of a scrollable view's content to its bounds.
791
+ * Content drawn outside those bounds, such as a shadow or a view scaled up past the edge, is
792
+ * cut off by default and stays visible once clipping is disabled.
793
+ * @param disabled - Whether clipping should be disabled (default: true).
794
+ * @platform ios 17.0+
795
+ * @platform tvos 17.0+
796
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/scrollclipdisabled(_:)).
797
+ */
798
+ export const scrollClipDisabled = (disabled: boolean = true) =>
799
+ createModifier('scrollClipDisabled', { disabled });
800
+
752
801
  /**
753
802
  * Controls the visibility of scroll indicators for scrollable views.
754
803
  * Mirrors SwiftUI's `scrollIndicators(_:axes:)` modifier.
@@ -1638,6 +1687,13 @@ export const resizable = (
1638
1687
  resizingMode?: 'stretch' | 'tile'
1639
1688
  ) => createModifier('resizable', { ...capInsets, resizingMode });
1640
1689
 
1690
+ /**
1691
+ * Configures the view's title for purposes of navigation, using a string.
1692
+ * @param title - The title to display.
1693
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/navigationtitle(_:)).
1694
+ */
1695
+ export const navigationTitle = (title: string) => createModifier('navigationTitle', { title });
1696
+
1641
1697
  // =============================================================================
1642
1698
  // Type Definitions
1643
1699
  // =============================================================================
@@ -1725,6 +1781,7 @@ export type BuiltInModifier =
1725
1781
  | ReturnType<typeof containerRelativeFrame>
1726
1782
  | ReturnType<typeof scrollContentBackground>
1727
1783
  | ReturnType<typeof scrollDisabled>
1784
+ | ReturnType<typeof scrollClipDisabled>
1728
1785
  | ReturnType<typeof scrollIndicators>
1729
1786
  | ReturnType<typeof defaultScrollAnchor>
1730
1787
  | ReturnType<typeof defaultScrollAnchorForRole>
@@ -1780,7 +1837,8 @@ export type BuiltInModifier =
1780
1837
  | ReturnType<typeof widgetAccentedRenderingMode>
1781
1838
  | ReturnType<typeof widgetURL>
1782
1839
  | ReturnType<typeof activityBackgroundTint>
1783
- | ReturnType<typeof containerBackground>;
1840
+ | ReturnType<typeof containerBackground>
1841
+ | ReturnType<typeof navigationTitle>;
1784
1842
 
1785
1843
  /**
1786
1844
  * Main ViewModifier type that supports both built-in and 3rd party modifiers.