react-native-unified-action-sheet 0.1.1 → 0.3.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 (61) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/README.md +38 -105
  3. package/android/build.gradle +28 -1
  4. package/android/src/main/java/com/unifiedactionsheet/ActionSheetOptions.kt +6 -2
  5. package/android/src/main/java/com/unifiedactionsheet/PromptOptions.kt +132 -0
  6. package/android/src/main/java/com/unifiedactionsheet/SheetContentBuilder.kt +31 -0
  7. package/android/src/main/java/com/unifiedactionsheet/SheetPresenters.kt +54 -0
  8. package/android/src/main/java/com/unifiedactionsheet/UnifiedActionSheetModule.kt +61 -0
  9. package/android/src/material/java/com/unifiedactionsheet/BottomSheetPresenterFactory.kt +10 -0
  10. package/android/src/material/java/com/unifiedactionsheet/MaterialBottomSheetPresenter.kt +90 -0
  11. package/android/src/material/res/values/unified_action_sheet_bottom_sheet.xml +22 -0
  12. package/android/src/noMaterial/java/com/unifiedactionsheet/BottomSheetPresenterFactory.kt +10 -0
  13. package/app.plugin.js +1 -0
  14. package/ios/BottomSheetViewController.swift +208 -0
  15. package/ios/UnifiedActionSheet.mm +73 -1
  16. package/ios/UnifiedActionSheetImpl.swift +248 -11
  17. package/jest/index.d.ts +15 -1
  18. package/jest/index.js +28 -0
  19. package/lib/commonjs/NativeUnifiedActionSheet.js.map +1 -1
  20. package/lib/commonjs/action-sheet-options.interface.js +4 -0
  21. package/lib/commonjs/common-options.interface.js +2 -0
  22. package/lib/commonjs/common-options.interface.js.map +1 -0
  23. package/lib/commonjs/index.js +57 -5
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/prompt-options.interface.js +6 -0
  26. package/lib/commonjs/prompt-options.interface.js.map +1 -0
  27. package/lib/module/NativeUnifiedActionSheet.js.map +1 -1
  28. package/lib/module/action-sheet-options.interface.js +2 -0
  29. package/lib/module/common-options.interface.js +2 -0
  30. package/lib/module/common-options.interface.js.map +1 -0
  31. package/lib/module/index.js +55 -4
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/prompt-options.interface.js +4 -0
  34. package/lib/module/prompt-options.interface.js.map +1 -0
  35. package/lib/typescript/commonjs/src/NativeUnifiedActionSheet.d.ts +23 -0
  36. package/lib/typescript/commonjs/src/NativeUnifiedActionSheet.d.ts.map +1 -1
  37. package/lib/typescript/commonjs/src/action-sheet-options.interface.d.ts +5 -14
  38. package/lib/typescript/commonjs/src/action-sheet-options.interface.d.ts.map +1 -1
  39. package/lib/typescript/commonjs/src/common-options.interface.d.ts +17 -0
  40. package/lib/typescript/commonjs/src/common-options.interface.d.ts.map +1 -0
  41. package/lib/typescript/commonjs/src/index.d.ts +4 -0
  42. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/src/prompt-options.interface.d.ts +19 -0
  44. package/lib/typescript/commonjs/src/prompt-options.interface.d.ts.map +1 -0
  45. package/lib/typescript/module/src/NativeUnifiedActionSheet.d.ts +23 -0
  46. package/lib/typescript/module/src/NativeUnifiedActionSheet.d.ts.map +1 -1
  47. package/lib/typescript/module/src/action-sheet-options.interface.d.ts +5 -14
  48. package/lib/typescript/module/src/action-sheet-options.interface.d.ts.map +1 -1
  49. package/lib/typescript/module/src/common-options.interface.d.ts +17 -0
  50. package/lib/typescript/module/src/common-options.interface.d.ts.map +1 -0
  51. package/lib/typescript/module/src/index.d.ts +4 -0
  52. package/lib/typescript/module/src/index.d.ts.map +1 -1
  53. package/lib/typescript/module/src/prompt-options.interface.d.ts +19 -0
  54. package/lib/typescript/module/src/prompt-options.interface.d.ts.map +1 -0
  55. package/package.json +12 -2
  56. package/plugin/withUnifiedActionSheet.js +58 -0
  57. package/src/NativeUnifiedActionSheet.ts +22 -0
  58. package/src/action-sheet-options.interface.ts +10 -20
  59. package/src/common-options.interface.ts +18 -0
  60. package/src/index.tsx +100 -6
  61. package/src/prompt-options.interface.ts +27 -0
@@ -0,0 +1,18 @@
1
+ export interface BaseButtonInterface {
2
+ label: string;
3
+ style?: 'cancel' | 'destructive';
4
+ disabled?: boolean;
5
+ }
6
+
7
+ export interface BaseOptionsInterface {
8
+ title?: string;
9
+ message?: string;
10
+ tintColor?: string;
11
+ cancelButtonTintColor?: string;
12
+ destructiveColor?: string;
13
+ userInterfaceStyle?: 'light' | 'dark';
14
+ }
15
+
16
+ export interface BaseAndroidOptionsInterface {
17
+ buttonTextAlignment?: 'start' | 'center';
18
+ }
package/src/index.tsx CHANGED
@@ -3,6 +3,25 @@ import { Platform } from 'react-native';
3
3
  import type { Spec } from './NativeUnifiedActionSheet';
4
4
  import type { ActionSheetOptionsInterface } from './action-sheet-options.interface';
5
5
 
6
+ export type {
7
+ BaseButtonInterface,
8
+ BaseOptionsInterface,
9
+ BaseAndroidOptionsInterface,
10
+ } from './common-options.interface';
11
+
12
+ export type {
13
+ PromptButtonInterface,
14
+ PromptCommonOptionsInterface,
15
+ PromptAndroidOptionsInterface,
16
+ PromptOptionsInterface,
17
+ PromptResultInterface,
18
+ } from './prompt-options.interface';
19
+ import type { BaseButtonInterface } from './common-options.interface';
20
+ import type {
21
+ PromptOptionsInterface,
22
+ PromptResultInterface,
23
+ } from './prompt-options.interface';
24
+
6
25
  export type {
7
26
  ActionSheetAnchorInterface,
8
27
  ActionSheetButtonInterface,
@@ -34,13 +53,27 @@ const toWireOptions = ({
34
53
  // crosses the bridge. Omit<> would not strip it at runtime.
35
54
  anchor: _anchor,
36
55
  ...rest
37
- }: ActionSheetOptionsInterface): WireOptions => {
56
+ }: ActionSheetOptionsInterface): WireOptions => ({
57
+ ...rest,
58
+ ...toWireButtons(options),
59
+ });
60
+
61
+ type WirePromptOptions = Omit<PromptOptionsInterface, 'options'> & {
62
+ options: string[];
63
+ cancelButtonIndex?: number;
64
+ destructiveButtonIndices?: number[];
65
+ disabledButtonIndices?: number[];
66
+ };
67
+
68
+ /// Same flattening as the sheet: labels plus index sets. Kept generic over the
69
+ /// button shape so the two APIs cannot disagree about what 'cancel' means.
70
+ const toWireButtons = (buttons: ReadonlyArray<BaseButtonInterface>) => {
38
71
  const labels: string[] = [];
39
72
  const destructive: number[] = [];
40
73
  const disabled: number[] = [];
41
74
  let cancelButtonIndex: number | undefined;
42
75
 
43
- options.forEach((button, index) => {
76
+ buttons.forEach((button, index) => {
44
77
  labels.push(button.label);
45
78
 
46
79
  if (button.style === 'destructive') destructive.push(index);
@@ -51,7 +84,6 @@ const toWireOptions = ({
51
84
  });
52
85
 
53
86
  return {
54
- ...rest,
55
87
  options: labels,
56
88
  ...(cancelButtonIndex == null ? {} : { cancelButtonIndex }),
57
89
  ...(destructive.length === 0
@@ -72,6 +104,33 @@ const showWithNativeModule = (
72
104
  )
73
105
  .catch(() => options.cancelButtonIndex ?? -1);
74
106
 
107
+ let warnedMaterialDisabled = false;
108
+
109
+ /// 'bottom' needs Material on Android, which the app opts into at build time.
110
+ /// Without it the sheet falls back to a centered dialog; say so once, in
111
+ /// development, since the fallback alone looks like the option was ignored.
112
+ const warnIfBottomUnavailable = (
113
+ presentationStyle: ActionSheetOptionsInterface['presentationStyle']
114
+ ) => {
115
+ if (
116
+ !__DEV__ ||
117
+ warnedMaterialDisabled ||
118
+ Platform.OS !== 'android' ||
119
+ presentationStyle !== 'bottom' ||
120
+ nativeModule().getConstants().isMaterialEnabled
121
+ ) {
122
+ return;
123
+ }
124
+
125
+ warnedMaterialDisabled = true;
126
+ console.warn(
127
+ "react-native-unified-action-sheet: presentationStyle 'bottom' needs Material on Android, " +
128
+ 'so this sheet falls back to a centered dialog. Set unifiedActionSheet.material=true in ' +
129
+ "android/gradle.properties, or add ['react-native-unified-action-sheet', { material: true }] " +
130
+ 'to your Expo plugins, then rebuild the app.'
131
+ );
132
+ };
133
+
75
134
  /// The anchor is measured in JS and sent across as a rect, so neither native
76
135
  /// module has to resolve a view: a ref's own measureInWindow is the supported
77
136
  /// way to do this on both architectures, unlike a react tag.
@@ -111,9 +170,14 @@ export const showActionSheetWithOptions = (
111
170
  return Promise.resolve(undefined);
112
171
  }
113
172
 
114
- return withAnchorRect(wire, options.anchor)
115
- .then(showWithNativeModule)
116
- .then(press);
173
+ warnIfBottomUnavailable(options.presentationStyle);
174
+
175
+ // A bottom sheet has no anchor, so don't measure one: on iPad a rect would
176
+ // turn the sheet into an arrowed popover pointing at it.
177
+ const anchor =
178
+ options.presentationStyle === 'bottom' ? undefined : options.anchor;
179
+
180
+ return withAnchorRect(wire, anchor).then(showWithNativeModule).then(press);
117
181
  };
118
182
 
119
183
  export const dismissActionSheet = (): void => {
@@ -129,3 +193,33 @@ export const dismissAllActionSheets = (): void => {
129
193
  nativeModule().dismissAllActionSheets();
130
194
  }
131
195
  };
196
+
197
+ /// A prompt is the sheet's centered dialog with a text field: iOS presents a
198
+ /// UIAlertController alert with addTextField, Android the same AppCompatDialog
199
+ /// the centered style uses. React Native's own Alert.prompt is iOS-only and a
200
+ /// silent no-op on Android, which is the gap this fills.
201
+ export const showPromptWithOptions = (
202
+ options: PromptOptionsInterface
203
+ ): Promise<PromptResultInterface | undefined> => {
204
+ const { options: buttons, ...rest } = options;
205
+ const wire: WirePromptOptions = { ...rest, ...toWireButtons(buttons) };
206
+
207
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
208
+ return Promise.resolve(undefined);
209
+ }
210
+
211
+ return nativeModule()
212
+ .showPromptWithOptions(wire)
213
+ .then((result) =>
214
+ // A programmatic dismiss resolves with no selection, matching the sheet.
215
+ result.buttonIndex === DISMISSED_BY_API ? undefined : result
216
+ )
217
+ .catch(() => ({ buttonIndex: wire.cancelButtonIndex ?? -1, text: '' }))
218
+ .then((result) => {
219
+ // The text goes to the handler, so a caller using onPress alone never
220
+ // has to read the resolved value.
221
+ if (result) buttons[result.buttonIndex]?.onPress?.(result.text);
222
+
223
+ return result;
224
+ });
225
+ };
@@ -0,0 +1,27 @@
1
+ import type {
2
+ BaseAndroidOptionsInterface,
3
+ BaseButtonInterface,
4
+ BaseOptionsInterface,
5
+ } from './common-options.interface';
6
+
7
+ export interface PromptButtonInterface extends BaseButtonInterface {
8
+ onPress?: (text: string) => void;
9
+ }
10
+
11
+ export interface PromptCommonOptionsInterface extends BaseOptionsInterface {
12
+ options: PromptButtonInterface[];
13
+ placeholder?: string;
14
+ defaultValue?: string;
15
+ keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'url';
16
+ secureTextEntry?: boolean;
17
+ }
18
+
19
+ export type PromptAndroidOptionsInterface = BaseAndroidOptionsInterface;
20
+
21
+ export interface PromptOptionsInterface
22
+ extends PromptCommonOptionsInterface, PromptAndroidOptionsInterface {}
23
+
24
+ export interface PromptResultInterface {
25
+ buttonIndex: number;
26
+ text: string;
27
+ }