react-native-platform-components 1.6.0 → 1.7.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.
Files changed (139) hide show
  1. package/README.md +19 -18
  2. package/android/src/main/java/com/platformcomponents/PCActionSheetView.kt +309 -0
  3. package/android/src/main/java/com/platformcomponents/PCActionSheetViewManager.kt +131 -0
  4. package/android/src/main/java/com/platformcomponents/PCButtonGroupView.kt +5 -1
  5. package/android/src/main/java/com/platformcomponents/PCButtonView.kt +5 -1
  6. package/android/src/main/java/com/platformcomponents/PCContextMenuView.kt +82 -27
  7. package/android/src/main/java/com/platformcomponents/PCContextMenuViewManager.kt +12 -13
  8. package/android/src/main/java/com/platformcomponents/PCMenuSupport.kt +66 -2
  9. package/android/src/main/java/com/platformcomponents/PlatformComponentsPackage.kt +1 -0
  10. package/android/src/main/res/values/ids.xml +2 -0
  11. package/ios/PCActionSheet.h +12 -0
  12. package/ios/PCActionSheet.mm +160 -0
  13. package/ios/PCActionSheetView.swift +280 -0
  14. package/ios/PCContextMenu.mm +22 -1
  15. package/ios/PCContextMenu.swift +218 -15
  16. package/ios/PCMenuItems.h +2 -1
  17. package/ios/PCMenuSupport.swift +22 -3
  18. package/lib/commonjs/ActionSheet.js +85 -0
  19. package/lib/commonjs/ActionSheet.js.map +1 -0
  20. package/lib/commonjs/ActionSheetNativeComponent.ts +94 -0
  21. package/lib/commonjs/ButtonGroupNativeComponent.ts +2 -0
  22. package/lib/commonjs/ButtonNativeComponent.ts +2 -0
  23. package/lib/commonjs/ContextMenu.js +50 -4
  24. package/lib/commonjs/ContextMenu.js.map +1 -1
  25. package/lib/commonjs/ContextMenuNativeComponent.ts +29 -8
  26. package/lib/commonjs/index.js +11 -0
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/index.web.js +7 -0
  29. package/lib/commonjs/index.web.js.map +1 -1
  30. package/lib/commonjs/jest.js +92 -5
  31. package/lib/commonjs/jest.js.map +1 -1
  32. package/lib/commonjs/menuItems.js +2 -1
  33. package/lib/commonjs/menuItems.js.map +1 -1
  34. package/lib/commonjs/selectionIdentifiers.js.map +1 -1
  35. package/lib/commonjs/web/ActionSheet.js +131 -0
  36. package/lib/commonjs/web/ActionSheet.js.map +1 -0
  37. package/lib/commonjs/web/components.js +3 -1
  38. package/lib/commonjs/web/components.js.map +1 -1
  39. package/lib/commonjs/web/containers.js +1 -0
  40. package/lib/commonjs/web/containers.js.map +1 -1
  41. package/lib/module/ActionSheet.js +78 -0
  42. package/lib/module/ActionSheet.js.map +1 -0
  43. package/lib/module/ActionSheetNativeComponent.ts +94 -0
  44. package/lib/module/ButtonGroupNativeComponent.ts +2 -0
  45. package/lib/module/ButtonNativeComponent.ts +2 -0
  46. package/lib/module/ContextMenu.js +51 -4
  47. package/lib/module/ContextMenu.js.map +1 -1
  48. package/lib/module/ContextMenuNativeComponent.ts +29 -8
  49. package/lib/module/index.js +1 -0
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/index.web.js +1 -1
  52. package/lib/module/index.web.js.map +1 -1
  53. package/lib/module/jest.js +90 -5
  54. package/lib/module/jest.js.map +1 -1
  55. package/lib/module/menuItems.js +2 -1
  56. package/lib/module/menuItems.js.map +1 -1
  57. package/lib/module/selectionIdentifiers.js.map +1 -1
  58. package/lib/module/web/ActionSheet.js +126 -0
  59. package/lib/module/web/ActionSheet.js.map +1 -0
  60. package/lib/module/web/components.js +2 -0
  61. package/lib/module/web/components.js.map +1 -1
  62. package/lib/module/web/containers.js +1 -0
  63. package/lib/module/web/containers.js.map +1 -1
  64. package/lib/typescript/commonjs/docs/sidebars.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/src/ActionSheet.d.ts +98 -0
  66. package/lib/typescript/commonjs/src/ActionSheet.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/src/ActionSheetNativeComponent.d.ts +78 -0
  68. package/lib/typescript/commonjs/src/ActionSheetNativeComponent.d.ts.map +1 -0
  69. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts +2 -0
  70. package/lib/typescript/commonjs/src/ButtonGroupNativeComponent.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts +2 -0
  72. package/lib/typescript/commonjs/src/ButtonNativeComponent.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/src/ContextMenu.d.ts +22 -6
  74. package/lib/typescript/commonjs/src/ContextMenu.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts +20 -7
  76. package/lib/typescript/commonjs/src/ContextMenuNativeComponent.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/src/index.d.ts +1 -0
  78. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/src/index.web.d.ts +2 -1
  80. package/lib/typescript/commonjs/src/index.web.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/src/jest.d.ts +17 -3
  82. package/lib/typescript/commonjs/src/jest.d.ts.map +1 -1
  83. package/lib/typescript/commonjs/src/menuItems.d.ts +12 -0
  84. package/lib/typescript/commonjs/src/menuItems.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts +1 -1
  86. package/lib/typescript/commonjs/src/selectionIdentifiers.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/src/web/ActionSheet.d.ts +11 -0
  88. package/lib/typescript/commonjs/src/web/ActionSheet.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/src/web/components.d.ts +1 -0
  90. package/lib/typescript/commonjs/src/web/components.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/src/web/containers.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/src/webComponents.d.ts +2 -0
  93. package/lib/typescript/commonjs/src/webComponents.d.ts.map +1 -1
  94. package/lib/typescript/module/docs/sidebars.d.ts.map +1 -1
  95. package/lib/typescript/module/src/ActionSheet.d.ts +98 -0
  96. package/lib/typescript/module/src/ActionSheet.d.ts.map +1 -0
  97. package/lib/typescript/module/src/ActionSheetNativeComponent.d.ts +78 -0
  98. package/lib/typescript/module/src/ActionSheetNativeComponent.d.ts.map +1 -0
  99. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts +2 -0
  100. package/lib/typescript/module/src/ButtonGroupNativeComponent.d.ts.map +1 -1
  101. package/lib/typescript/module/src/ButtonNativeComponent.d.ts +2 -0
  102. package/lib/typescript/module/src/ButtonNativeComponent.d.ts.map +1 -1
  103. package/lib/typescript/module/src/ContextMenu.d.ts +22 -6
  104. package/lib/typescript/module/src/ContextMenu.d.ts.map +1 -1
  105. package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts +20 -7
  106. package/lib/typescript/module/src/ContextMenuNativeComponent.d.ts.map +1 -1
  107. package/lib/typescript/module/src/index.d.ts +1 -0
  108. package/lib/typescript/module/src/index.d.ts.map +1 -1
  109. package/lib/typescript/module/src/index.web.d.ts +2 -1
  110. package/lib/typescript/module/src/index.web.d.ts.map +1 -1
  111. package/lib/typescript/module/src/jest.d.ts +17 -3
  112. package/lib/typescript/module/src/jest.d.ts.map +1 -1
  113. package/lib/typescript/module/src/menuItems.d.ts +12 -0
  114. package/lib/typescript/module/src/menuItems.d.ts.map +1 -1
  115. package/lib/typescript/module/src/selectionIdentifiers.d.ts +1 -1
  116. package/lib/typescript/module/src/selectionIdentifiers.d.ts.map +1 -1
  117. package/lib/typescript/module/src/web/ActionSheet.d.ts +11 -0
  118. package/lib/typescript/module/src/web/ActionSheet.d.ts.map +1 -0
  119. package/lib/typescript/module/src/web/components.d.ts +1 -0
  120. package/lib/typescript/module/src/web/components.d.ts.map +1 -1
  121. package/lib/typescript/module/src/web/containers.d.ts.map +1 -1
  122. package/lib/typescript/module/src/webComponents.d.ts +2 -0
  123. package/lib/typescript/module/src/webComponents.d.ts.map +1 -1
  124. package/package.json +9 -2
  125. package/src/ActionSheet.tsx +193 -0
  126. package/src/ActionSheetNativeComponent.ts +94 -0
  127. package/src/ButtonGroupNativeComponent.ts +2 -0
  128. package/src/ButtonNativeComponent.ts +2 -0
  129. package/src/ContextMenu.tsx +71 -12
  130. package/src/ContextMenuNativeComponent.ts +29 -8
  131. package/src/index.tsx +1 -0
  132. package/src/index.web.tsx +2 -0
  133. package/src/jest.tsx +108 -4
  134. package/src/menuItems.ts +13 -0
  135. package/src/selectionIdentifiers.ts +1 -1
  136. package/src/web/ActionSheet.tsx +130 -0
  137. package/src/web/components.tsx +5 -0
  138. package/src/web/containers.tsx +1 -0
  139. package/src/webComponents.ts +2 -0
@@ -1,6 +1,7 @@
1
1
  // ContextMenuNativeComponent.ts
2
+ import type * as React from 'react';
2
3
  import type { HostComponent, ViewProps } from 'react-native';
3
- import { codegenNativeComponent } from 'react-native';
4
+ import { codegenNativeCommands, codegenNativeComponent } from 'react-native';
4
5
  import type { BubblingEventHandler, Double, Int32 } from './codegenTypes';
5
6
 
6
7
  /**
@@ -43,6 +44,8 @@ export type ContextMenuItem = Readonly<{
43
44
  state: string;
44
45
  /** The action's own haptics; '' = the component's */
45
46
  haptics: string;
47
+ /** Test identifier of the menu row; '' = none */
48
+ testID: string;
46
49
  }>;
47
50
 
48
51
  /**
@@ -59,7 +62,7 @@ export type ContextMenuPressActionEvent = Readonly<{
59
62
  export type ContextMenuInteractivity = 'enabled' | 'disabled';
60
63
 
61
64
  /** Trigger mode for opening the menu. */
62
- export type ContextMenuTrigger = 'longPress' | 'tap';
65
+ export type ContextMenuTrigger = 'longPress' | 'tap' | 'none';
63
66
 
64
67
  /**
65
68
  * iOS-specific configuration.
@@ -75,11 +78,6 @@ export type IOSProps = Readonly<{
75
78
  export type AndroidProps = Readonly<{
76
79
  /** Anchor position for the popup menu */
77
80
  anchorPosition?: string; // 'left' | 'right'
78
- /**
79
- * Programmatic visibility control (Android only).
80
- * 'open' to show the menu, 'closed' to hide it.
81
- */
82
- visible?: string; // 'open' | 'closed'
83
81
  }>;
84
82
 
85
83
  export interface ContextMenuProps extends ViewProps {
@@ -102,9 +100,16 @@ export interface ContextMenuProps extends ViewProps {
102
100
  * How the menu is triggered:
103
101
  * - 'longPress' (default): Long-press opens the menu
104
102
  * - 'tap': Single tap opens the menu
103
+ * - 'none': only `visible` opens it; the content keeps its touches
105
104
  */
106
105
  trigger?: string; // ContextMenuTrigger
107
106
 
107
+ /**
108
+ * Programmatic visibility: 'open' presents the menu anchored to the view,
109
+ * 'closed' dismisses it. '' (unset) leaves the menu to the user.
110
+ */
111
+ visible?: string; // '' | 'open' | 'closed'
112
+
108
113
  /**
109
114
  * Haptic played when an action is pressed: '' (none added) | 'none' | 'selection' |
110
115
  * 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error'
@@ -135,6 +140,22 @@ export interface ContextMenuProps extends ViewProps {
135
140
  android?: AndroidProps;
136
141
  }
137
142
 
143
+ type NativeContextMenuComponent = HostComponent<ContextMenuProps>;
144
+
145
+ // `React.ElementRef` rather than `React.ComponentRef`: the codegen shipped with
146
+ // React Native 0.81 (and the 0.76 Android floor) only accepts the former.
147
+ interface NativeCommands {
148
+ /**
149
+ * trigger 'tap': the user tapped the content and no inner pressable took
150
+ * the tap. Opens the menu (iOS 17.4+; earlier iOS opens it natively).
151
+ */
152
+ handleTap: (viewRef: React.ElementRef<NativeContextMenuComponent>) => void;
153
+ }
154
+
155
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
156
+ supportedCommands: ['handleTap'],
157
+ });
158
+
138
159
  export default codegenNativeComponent<ContextMenuProps>(
139
160
  'PCContextMenu'
140
- ) as HostComponent<ContextMenuProps>;
161
+ ) as NativeContextMenuComponent;
package/src/index.tsx CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './DatePicker';
2
2
  export * from './SelectionMenu';
3
3
  export * from './ContextMenu';
4
+ export * from './ActionSheet';
4
5
  export * from './SegmentedControl';
5
6
  export * from './TabBar';
6
7
  export * from './FloatingActionButton';
package/src/index.web.tsx CHANGED
@@ -8,6 +8,7 @@
8
8
  export type * from './DatePicker';
9
9
  export type * from './SelectionMenu';
10
10
  export type * from './ContextMenu';
11
+ export type * from './ActionSheet';
11
12
  export type * from './SegmentedControl';
12
13
  export type * from './TabBar';
13
14
  export type * from './FloatingActionButton';
@@ -33,6 +34,7 @@ export type {
33
34
  } from './webComponents';
34
35
  export { PlatformComponentsProvider } from './web/PlatformComponentsProvider';
35
36
  export {
37
+ ActionSheet,
36
38
  Button,
37
39
  ButtonGroup,
38
40
  SplitButton,
package/src/jest.tsx CHANGED
@@ -27,9 +27,10 @@ import {
27
27
  type ViewProps,
28
28
  } from 'react-native';
29
29
 
30
+ import type { ActionSheetProps } from './ActionSheet';
30
31
  import type { ButtonProps } from './Button';
31
32
  import type { ButtonGroupProps } from './ButtonGroup';
32
- import type { ContextMenuProps } from './ContextMenu';
33
+ import type { ContextMenuAction, ContextMenuProps } from './ContextMenu';
33
34
  import type { DatePickerProps, DateRangePickerProps } from './DatePicker';
34
35
  import type { FloatingActionButtonProps } from './FloatingActionButton';
35
36
  import type { FloatingToolbarProps } from './FloatingToolbar';
@@ -54,6 +55,7 @@ import { nextSelection } from './web/ButtonGroup';
54
55
  export type * from './DatePicker';
55
56
  export type * from './SelectionMenu';
56
57
  export type * from './ContextMenu';
58
+ export type * from './ActionSheet';
57
59
  export type * from './Button';
58
60
  export type * from './ButtonGroup';
59
61
  export type * from './SplitButton';
@@ -845,9 +847,12 @@ export function SelectionMenu(props: SelectionMenuProps): React.ReactElement {
845
847
  }
846
848
 
847
849
  /**
848
- * Its children in a view carrying `testID`. The menu itself isn't rendered;
849
- * `fireEvent(menu, 'pressAction', id, title)`, `'menuOpen'`, `'menuClose'`
850
- * and `'previewPress'` reach the callbacks.
850
+ * Its children in a view carrying `testID`. While `visible` (or the
851
+ * deprecated `android.visible`) is true, each action follows them as a
852
+ * `Pressable` with the `menuitem` role and the action's `testID` (else
853
+ * `${testID}-${id}`) that calls `onPressAction(id, title)`; submenus and
854
+ * sections list their actions. `fireEvent(menu, 'pressAction', id, title)`,
855
+ * `'menuOpen'`, `'menuClose'` and `'previewPress'` reach the callbacks.
851
856
  */
852
857
  export function ContextMenu(props: ContextMenuProps): React.ReactElement {
853
858
  const {
@@ -860,18 +865,117 @@ export function ContextMenu(props: ContextMenuProps): React.ReactElement {
860
865
  actions,
861
866
  disabled,
862
867
  trigger,
868
+ visible,
863
869
  haptics,
864
870
  ios,
865
871
  android,
872
+ testID,
866
873
  ...viewProps
867
874
  } = props;
868
875
 
876
+ const open = !disabled && !!(visible ?? android?.visible);
877
+ const leaves = (list: ContextMenuProps['actions']): ContextMenuAction[] =>
878
+ list.flatMap((action) =>
879
+ action.attributes?.hidden
880
+ ? []
881
+ : action.subactions?.length
882
+ ? leaves(action.subactions)
883
+ : [action]
884
+ );
885
+
869
886
  return (
870
887
  <View
871
888
  {...viewProps}
889
+ testID={testID}
872
890
  {...handlers({ onPressAction, onMenuOpen, onMenuClose, onPreviewPress })}
873
891
  >
874
892
  {children}
893
+ {open
894
+ ? leaves(actions).map((action) => (
895
+ <Pressable
896
+ key={action.id}
897
+ testID={
898
+ action.testID ?? (testID ? `${testID}-${action.id}` : undefined)
899
+ }
900
+ accessibilityRole="menuitem"
901
+ accessibilityState={{ disabled: !!action.attributes?.disabled }}
902
+ disabled={action.attributes?.disabled}
903
+ onPress={() => onPressAction?.(action.id, action.title)}
904
+ >
905
+ <Text>{action.title}</Text>
906
+ </Pressable>
907
+ ))
908
+ : null}
909
+ </View>
910
+ );
911
+ }
912
+
913
+ /**
914
+ * Its children in a view carrying `testID`. While `visible`, the title and
915
+ * message show as `Text`, and each shown action is a `Pressable` with its
916
+ * `testID` (else `${testID}-${id}`) that calls `onSelect(id)`; a disabled
917
+ * one ignores presses. A cancel `Pressable` with `cancelTestID` (else
918
+ * `${testID}-cancel`) calls `onCancel`. `fireEvent(sheet, 'select', id)` and
919
+ * `fireEvent(sheet, 'cancel')` also work.
920
+ */
921
+ export function ActionSheet(props: ActionSheetProps): React.ReactElement {
922
+ const {
923
+ visible,
924
+ title,
925
+ message,
926
+ actions,
927
+ cancelTitle,
928
+ cancelTestID,
929
+ haptics,
930
+ maxFontSizeMultiplier,
931
+ onSelect,
932
+ onCancel,
933
+ testID,
934
+ children,
935
+ ...viewProps
936
+ } = props;
937
+
938
+ return (
939
+ <View
940
+ {...viewProps}
941
+ testID={testID}
942
+ accessibilityState={{ expanded: visible }}
943
+ {...handlers({ onSelect, onCancel })}
944
+ >
945
+ {children}
946
+ {visible ? (
947
+ <View accessibilityRole="menu">
948
+ {title ? <Text>{title}</Text> : null}
949
+ {message ? <Text>{message}</Text> : null}
950
+ {actions
951
+ .filter((action) => !action.attributes?.hidden)
952
+ .map((action) => {
953
+ const disabled = !!action.attributes?.disabled;
954
+ return (
955
+ <Pressable
956
+ key={action.id}
957
+ testID={
958
+ action.testID ??
959
+ (testID ? `${testID}-${action.id}` : undefined)
960
+ }
961
+ accessibilityRole="menuitem"
962
+ accessibilityState={{ disabled }}
963
+ disabled={disabled}
964
+ onPress={() => onSelect?.(action.id)}
965
+ >
966
+ <Text>{action.title}</Text>
967
+ </Pressable>
968
+ );
969
+ })}
970
+ <Pressable
971
+ testID={cancelTestID ?? (testID ? `${testID}-cancel` : undefined)}
972
+ accessibilityRole="button"
973
+ onPress={() => onCancel?.()}
974
+ >
975
+ <Text>{cancelTitle ?? 'Cancel'}</Text>
976
+ </Pressable>
977
+ </View>
978
+ ) : null}
875
979
  </View>
876
980
  );
877
981
  }
package/src/menuItems.ts CHANGED
@@ -69,6 +69,16 @@ export interface ContextMenuAction {
69
69
  * - Android: a menu group with group dividers; the title is not shown.
70
70
  */
71
71
  displayInline?: boolean;
72
+ /**
73
+ * Test identifier of the action's menu row, so UI tests (Detox `by.id`)
74
+ * find it without its title. A submenu's row takes one too; an inline
75
+ * section has no row of its own.
76
+ *
77
+ * - iOS: the `accessibilityIdentifier` of the `UIAction` / `UIMenu`.
78
+ * - Android 10+: the tag of the popup row view, which Detox and Espresso
79
+ * match `by.id` against. Not a resource-id.
80
+ */
81
+ testID?: string;
72
82
  }
73
83
 
74
84
  /**
@@ -99,6 +109,8 @@ export type NativeMenuItem = {
99
109
  state: string;
100
110
  /** The action's own haptics; '' = the component's */
101
111
  haptics: string;
112
+ /** Test identifier of the menu row; '' = none */
113
+ testID: string;
102
114
  };
103
115
 
104
116
  const flag = (value: boolean | undefined) => (value ? 'true' : 'false');
@@ -141,6 +153,7 @@ export function flattenMenuActions(
141
153
  keepsMenuPresented: flag(attributes?.keepsMenuPresented),
142
154
  state: action.state ?? '',
143
155
  haptics: action.haptics ?? '',
156
+ testID: action.testID ?? '',
144
157
  });
145
158
 
146
159
  if (kind !== 'action') visit(children, index);
@@ -1,6 +1,6 @@
1
1
  /** Diagnose identifiers reserved by the native selection protocol. */
2
2
  export function warnSelectionIdentifiers(
3
- component: 'SelectionMenu' | 'SegmentedControl',
3
+ component: 'SelectionMenu' | 'SegmentedControl' | 'ActionSheet',
4
4
  values: readonly string[]
5
5
  ): void {
6
6
  if (!__DEV__) return;
@@ -0,0 +1,130 @@
1
+ // web/ActionSheet.tsx
2
+ import React from 'react';
3
+ import { View } from 'react-native';
4
+
5
+ import type { ActionSheetProps } from '../ActionSheet';
6
+ import { Dialog } from './Dialog';
7
+ import { Icon } from './Icon';
8
+
9
+ // Material 3 baseline error color, as on Android
10
+ const DESTRUCTIVE_COLOR = '#B3261E';
11
+
12
+ /**
13
+ * Renders its children; while `visible`, the title, message and actions open
14
+ * in a `<dialog>` with a cancel button below them. Picking an action calls
15
+ * `onSelect`; the cancel button, Escape and a click outside call `onCancel`.
16
+ * Icons draw only for image sources (`{ type: 'image' }`); each button
17
+ * carries its action's `testID` as `data-testid`.
18
+ */
19
+ export function ActionSheet(props: ActionSheetProps): React.ReactElement {
20
+ const {
21
+ visible,
22
+ title,
23
+ message,
24
+ actions,
25
+ cancelTitle,
26
+ cancelTestID,
27
+ haptics,
28
+ maxFontSizeMultiplier,
29
+ onSelect,
30
+ onCancel,
31
+ accessibilityLabel,
32
+ children,
33
+ ...viewProps
34
+ } = props;
35
+
36
+ const label = accessibilityLabel ?? title ?? 'Actions';
37
+ const shown = actions.filter((action) => !action.attributes?.hidden);
38
+ const cancel = () => onCancel?.();
39
+
40
+ return (
41
+ <View {...viewProps}>
42
+ {children}
43
+ {visible ? (
44
+ <Dialog label={label} onDismiss={cancel}>
45
+ <div
46
+ style={{ display: 'flex', flexDirection: 'column', minWidth: 240 }}
47
+ >
48
+ {title || message ? (
49
+ <div style={{ padding: '4px 12px 12px', textAlign: 'center' }}>
50
+ {title ? (
51
+ <div style={{ fontSize: 15, fontWeight: 600 }}>{title}</div>
52
+ ) : null}
53
+ {message ? (
54
+ <div style={{ fontSize: 13, opacity: 0.7, marginTop: 4 }}>
55
+ {message}
56
+ </div>
57
+ ) : null}
58
+ </div>
59
+ ) : null}
60
+ {shown.map((action, index) => {
61
+ const destructive = !!action.attributes?.destructive;
62
+ const color = destructive ? DESTRUCTIVE_COLOR : 'inherit';
63
+ return (
64
+ <button
65
+ key={action.id}
66
+ type="button"
67
+ data-testid={action.testID}
68
+ disabled={action.attributes?.disabled}
69
+ autoFocus={
70
+ index ===
71
+ shown.findIndex((item) => !item.attributes?.disabled)
72
+ }
73
+ onClick={() => {
74
+ if (!action.attributes?.disabled) onSelect?.(action.id);
75
+ }}
76
+ style={{
77
+ display: 'flex',
78
+ alignItems: 'center',
79
+ gap: 12,
80
+ minHeight: 44,
81
+ padding: '0 12px',
82
+ border: 'none',
83
+ borderRadius: 8,
84
+ fontFamily: 'inherit',
85
+ fontSize: 16,
86
+ textAlign: 'start',
87
+ color,
88
+ opacity: action.attributes?.disabled ? 0.4 : 1,
89
+ backgroundColor: 'transparent',
90
+ cursor: action.attributes?.disabled ? 'default' : 'pointer',
91
+ }}
92
+ >
93
+ <Icon
94
+ icon={action.image}
95
+ color={
96
+ action.imageColor ??
97
+ (destructive ? DESTRUCTIVE_COLOR : 'CanvasText')
98
+ }
99
+ />
100
+ <span>{action.title}</span>
101
+ </button>
102
+ );
103
+ })}
104
+ <button
105
+ type="button"
106
+ data-testid={cancelTestID}
107
+ onClick={cancel}
108
+ style={{
109
+ minHeight: 44,
110
+ marginTop: 8,
111
+ padding: '0 12px',
112
+ border: 'none',
113
+ borderRadius: 8,
114
+ fontFamily: 'inherit',
115
+ fontSize: 16,
116
+ fontWeight: 600,
117
+ color: 'inherit',
118
+ backgroundColor:
119
+ 'color-mix(in srgb, CanvasText 8%, transparent)',
120
+ cursor: 'pointer',
121
+ }}
122
+ >
123
+ {cancelTitle ?? 'Cancel'}
124
+ </button>
125
+ </div>
126
+ </Dialog>
127
+ ) : null}
128
+ </View>
129
+ );
130
+ }
@@ -1,5 +1,6 @@
1
1
  import { withWebComponent } from './PlatformComponentsProvider';
2
2
  import type { WebComponents } from '../webComponents';
3
+ import { ActionSheet as DefaultActionSheet } from './ActionSheet';
3
4
  import { Button as DefaultButton } from './Button';
4
5
  import { ButtonGroup as DefaultButtonGroup } from './ButtonGroup';
5
6
  import { SplitButton as DefaultSplitButton } from './SplitButton';
@@ -19,6 +20,10 @@ import {
19
20
  } from './containers';
20
21
 
21
22
  // Explicit types keep published declarations tied to a resolvable type import.
23
+ export const ActionSheet: WebComponents['ActionSheet'] = withWebComponent(
24
+ 'ActionSheet',
25
+ DefaultActionSheet
26
+ );
22
27
  export const Button: WebComponents['Button'] = withWebComponent(
23
28
  'Button',
24
29
  DefaultButton
@@ -22,6 +22,7 @@ export function ContextMenu(props: ContextMenuProps): React.ReactElement {
22
22
  actions,
23
23
  disabled,
24
24
  trigger,
25
+ visible,
25
26
  onPressAction,
26
27
  onMenuOpen,
27
28
  onMenuClose,
@@ -1,4 +1,5 @@
1
1
  import type { ComponentType, ReactNode, RefAttributes } from 'react';
2
+ import type { ActionSheetProps } from './ActionSheet';
2
3
  import type { ButtonProps } from './Button';
3
4
  import type { ButtonGroupProps } from './ButtonGroup';
4
5
  import type { SplitButtonProps } from './SplitButton';
@@ -16,6 +17,7 @@ import type { TextFieldProps, TextFieldRef } from './TextField';
16
17
 
17
18
  /** Public contracts accepted by consumer-supplied web components. */
18
19
  export interface WebComponentProps {
20
+ ActionSheet: ActionSheetProps;
19
21
  Button: ButtonProps;
20
22
  ButtonGroup: ButtonGroupProps;
21
23
  SplitButton: SplitButtonProps;