Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(action-sheet): Add cancelable parameter #2285

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
18 changes: 10 additions & 8 deletions action-sheet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,20 @@ to select.

#### ShowActionsResult

| Prop | Type | Description | Since |
| ----------- | ------------------- | -------------------------------------------- | ----- |
| **`index`** | <code>number</code> | The index of the clicked option (Zero-based) | 1.0.0 |
| Prop | Type | Description | Since |
| -------------- | -------------------- | -------------------------------------------- | ----- |
| **`index`** | <code>number</code> | The index of the clicked option (Zero-based), or -1 if the sheet was canceled. On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned | 1.0.0 |
| **`canceled`** | <code>boolean</code> | True if sheet was canceled by user; False otherwise | 6.1.0 |


#### ShowActionsOptions

| Prop | Type | Description | Since |
| ------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- |
| **`title`** | <code>string</code> | The title of the Action Sheet. | 1.0.0 |
| **`message`** | <code>string</code> | A message to show under the title. This option is only supported on iOS. | 1.0.0 |
| **`options`** | <code>ActionSheetButton[]</code> | Options the user can choose from. | 1.0.0 |
| Prop | Type | Description | Since |
| ---------------- | -------------------------------- | ------------------------------------------------------------------------ | ----- |
| **`title`** | <code>string</code> | The title of the Action Sheet. | 1.0.0 |
| **`message`** | <code>string</code> | A message to show under the title. This option is only supported on iOS. | 1.0.0 |
| **`options`** | <code>ActionSheetButton[]</code> | Options the user can choose from. | 1.0.0 |
| **`cancelable`** | <code>boolean</code> | If true, sheet is canceled when clicked outside; If false, it is not. By default, false. On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false. | 6.1.0 |


#### ActionSheetButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public interface OnCancelListener {
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
this.cancelListener.onCancel();
if (this.cancelListener != null) {
this.cancelListener.onCancel();
}
}

private String title;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class ActionSheetPlugin extends Plugin {
@PluginMethod
public void showActions(final PluginCall call) {
String title = call.getString("title");
boolean cancelable = Boolean.TRUE.equals(call.getBoolean("cancelable", false));
JSArray options = call.getArray("options");
if (options == null) {
call.reject("Must supply options");
Expand All @@ -39,19 +40,23 @@ public void showActions(final PluginCall call) {
}
implementation.setTitle(title);
implementation.setOptions(actionOptions);
implementation.setCancelable(false);
implementation.setOnSelectedListener(
index -> {
JSObject ret = new JSObject();
ret.put("index", index);
call.resolve(ret);
implementation.dismiss();
}
);
implementation.setCancelable(cancelable);
if (cancelable) {
implementation.setOnCancelListener(() -> resolve(call, -1));
}
implementation.setOnSelectedListener(index -> resolve(call, index));
implementation.show(getActivity().getSupportFragmentManager(), "capacitorModalsActionSheet");
} catch (JSONException ex) {
Logger.error("JSON error processing an option for showActions", ex);
call.reject("JSON error processing an option for showActions", ex);
}
}

private void resolve(final PluginCall call, int selectedIndex) {
JSObject ret = new JSObject();
ret.put("index", selectedIndex);
ret.put("canceled", selectedIndex < 0);
call.resolve(ret);
implementation.dismiss();
}
}
34 changes: 32 additions & 2 deletions action-sheet/ios/Sources/ActionSheetPlugin/ActionSheetPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,26 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin {
@objc func showActions(_ call: CAPPluginCall) {
let title = call.options["title"] as? String
let message = call.options["message"] as? String
let cancelable = call.options["cancelable"] as? Bool ?? false
OS-ricardomoreirasilva marked this conversation as resolved.
Show resolved Hide resolved

let options = call.getArray("options", JSObject.self) ?? []
var alertActions = [UIAlertAction]()
var forceCancelableOnClickOutside = cancelable
for (index, option) in options.enumerated() {
let style = option["style"] as? String ?? "DEFAULT"
let title = option["title"] as? String ?? ""
Comment on lines 26 to 27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're now touching the call.options[...] ... calls, shouldn't these be considered as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a method for getString for JSObject? It makes sense for there to be, but xcode is saying there is no such method

var buttonStyle: UIAlertAction.Style = .default
if style == "DESTRUCTIVE" {
buttonStyle = .destructive
} else if style == "CANCEL" {
// if there's a cancel action, then it will already be cancelable when clicked outside
forceCancelableOnClickOutside = false
buttonStyle = .cancel
}
let action = UIAlertAction(title: title, style: buttonStyle, handler: { (_) -> Void in
call.resolve([
"index": index
"index": index,
"canceled": false
])
})
alertActions.append(action)
Expand All @@ -40,9 +45,34 @@ public class ActionSheetPlugin: CAPPlugin, CAPBridgedPlugin {
DispatchQueue.main.async { [weak self] in
if let alertController = self?.implementation.buildActionSheet(title: title, message: message, actions: alertActions) {
self?.setCenteredPopover(alertController)
self?.bridge?.viewController?.present(alertController, animated: true, completion: nil)
self?.bridge?.viewController?.present(alertController, animated: true) {
if (forceCancelableOnClickOutside) {
let gestureRecognizer = TapGestureRecognizerWithClosure {
alertController.dismiss(animated: true, completion: nil)
call.resolve([
"index": -1,
"canceled": true
])
}
let backroundView = alertController.view.superview?.subviews[0]
backroundView?.addGestureRecognizer(gestureRecognizer)
}
}
}
}
}

private final class TapGestureRecognizerWithClosure: UITapGestureRecognizer {
private let onTap: () -> Void

init(onTap: @escaping () -> Void) {
self.onTap = onTap
super.init(target: nil, action: nil)
self.addTarget(self, action: #selector(action))
}

@objc private func action() {
onTap()
}
}
}
19 changes: 18 additions & 1 deletion action-sheet/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ export interface ShowActionsOptions {
* @since 1.0.0
*/
options: ActionSheetButton[];

/**
* If true, sheet is canceled when clicked outside; If false, it is not. By default, false.
*
* On iOS, the sheet is also cancelable if a button with ActionSheetButtonStyle.Cancel is provided and cancelable is false.
*
* @since 6.1.0
*/
cancelable?: boolean;
}

export enum ActionSheetButtonStyle {
Expand Down Expand Up @@ -76,11 +85,19 @@ export interface ActionSheetButton {

export interface ShowActionsResult {
/**
* The index of the clicked option (Zero-based)
* The index of the clicked option (Zero-based), or -1 if the sheet was canceled.
*
* On iOS, if there is a button with ActionSheetButtonStyle.Cancel, and user clicks outside the sheet, the index of the cancel option is returned
*
* @since 1.0.0
*/
index: number;
/**
* True if sheet was canceled by user; False otherwise
*
* @since 6.1.0
*/
canceled: boolean;
}

export interface ActionSheetPlugin {
Expand Down
11 changes: 10 additions & 1 deletion action-sheet/src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,30 @@

export class ActionSheetWeb extends WebPlugin implements ActionSheetPlugin {
async showActions(options: ShowActionsOptions): Promise<ShowActionsResult> {
return new Promise<ShowActionsResult>((resolve, _reject) => {

Check warning on line 11 in action-sheet/src/web.ts

View workflow job for this annotation

GitHub Actions / lint (action-sheet)

'_reject' is defined but never used
let actionSheet: any = document.querySelector('pwa-action-sheet');
if (!actionSheet) {
actionSheet = document.createElement('pwa-action-sheet');
document.body.appendChild(actionSheet);
}
actionSheet.header = options.title;
actionSheet.cancelable = false;
actionSheet.cancelable = options.cancelable;
actionSheet.options = options.options;
actionSheet.addEventListener('onSelection', async (e: any) => {
const selection = e.detail;
resolve({
index: selection,
canceled: false,
});
});
if (options.cancelable) {
actionSheet.addEventListener('onCanceled', async () => {
resolve({
index: -1,
canceled: true,
});
});
}
});
}
}
Loading