Support regular RN color value, no more crashing after first callback is consumed. (#82)

* Update DialogAndroid.java

Support regular RN color value, no more crashing after first callback is consumed.

* new api in js

* updated js

* types fixing

* fixed class name from MaterialDialog to AndroidDialog

* Whops it should be DialogAndroid, not AndroidDialog

* fix showProgress - plan to remove this, just testing

* update readme

* added mroe examples

* assignDefaults example

* alternative shorthand signatures

* more readme

* deprecate string as item, selctedIndex, selectedIndices. moved to object as item, with label

* changes based on discusion with @vonovak

* readme nits

* readme nit

* readme touchups

* readme nit

* testing and fixes per testing

* .padStart is not same as prefix
This commit is contained in:
Noitidart 2018-05-15 10:59:36 -07:00 committed by Vojtech Novak
parent a1d7ef121d
commit 5b55a623b8
3 changed files with 1055 additions and 318 deletions

View File

@ -1,82 +1,494 @@
/** // @flow
* @providesModule DialogAndroid import { NativeModules } from 'react-native'
*/ import processColor from 'react-native/Libraries/StyleSheet/processColor'
'use strict'; import type { ColorValue } from 'react-native/Libraries/StyleSheet/StyleSheetTypes'
var { NativeModules } = require('react-native'); type IdKey = string | 'id';
type LabelKey = string | 'label';
type ListItem = { label:string, id?:any };
var callbackNames = [ type OptionsCommon = {|
'onPositive', title?: null | string,
'onNegative', titleColor?: ColorValue,
'onNeutral', content?: null | string,
'onAny', contentIsHtml?: boolean,
'itemsCallback', contentColor?: string,
'itemsCallbackSingleChoice', positiveText?: string, // default "OK"
'itemsCallbackMultiChoice', negativeText?: string,
'showListener', neutralText?: string,
'cancelListener', positiveColor?: ColorValue,
'dismissListener', negativeColor?: ColorValue,
]; neutralColor?: ColorValue,
cancelable?: boolean,
linkColor?: ColorValue, // applies if contentIsHtml is true, and there are <a> elements in content string
forceStacking?: boolean
|}
class DialogAndroid { type ListItemJustLabel = { label:string };
constructor() { type ListItemJustId = { id:string };
this.options = {}; type ListItemFull = { label:string, id:any };
} type ListItemBare = {};
set(obj) { type OptionsRadio = {|
Object.assign(this.options, obj); type: typeof ListType.listRadio,
} widgetColor?: ColorValue // radio color
|}
type OptionsCheckbox = {|
type: typeof ListType.listCheckbox,
neutralIsClear?: boolean,
widgetColor?: ColorValue // checkbox color
|}
show() { type OptionsPicker = {|
var finalOptions = Object.assign({}, this.options); ...OptionsCommon,
type?: typeof ListType.listPlain,
items: ListItemJustLabel[],
|} | {|
...OptionsCommon,
type?: typeof ListType.listPlain,
items: ListItemBare[],
labelKey: string
|} | {|
// radio - no preselected
...OptionsCommon,
...OptionsRadio,
items: ListItemJustLabel[],
|} | {|
// radio - no preselected
...OptionsCommon,
...OptionsRadio,
items: ListItemBare[],
labelKey: string
|} | {|
// radio - preselected - ListItemFull
...OptionsCommon,
...OptionsRadio,
items: ListItemFull[],
selectedId: any
|} | {|
// radio - preselected - ListItemJustlabel
...OptionsCommon,
...OptionsRadio,
items: ListItemJustLabel[],
idKey: string,
selectedId: any
|} | {|
// radio - preselected - ListItemJustId
...OptionsCommon,
...OptionsRadio,
items: ListItemJustId[],
labelKey: string,
selectedId: any
|} | {|
// radio - preselected - ListItemBare
...OptionsCommon,
...OptionsRadio,
items: ListItemBare[],
idKey: string,
labelKey: string,
selectedId: any
|} | {|
// checklist - no preselected - ListItemJustLabel
...OptionsCommon,
...OptionsCheckbox,
items: ListItemJustLabel[]
|} | {|
// checklist - no preselected - ListItemBare
...OptionsCommon,
...OptionsCheckbox,
items: ListItemBare[],
labelKey: string
|} | {|
// checklist - preselected - ListItemFull
...OptionsCommon,
...OptionsCheckbox,
items: ListItemFull[],
selectedIds: any[]
|} | {|
// checklist - preselected - ListItemJustlabel
...OptionsCommon,
...OptionsCheckbox,
items: ListItemJustLabel[],
idKey: string,
selectedIds: any
|} | {|
// checklist - preselected - ListItemJustId
...OptionsCommon,
...OptionsCheckbox,
items: ListItemJustId[],
labelKey: string,
selectedIds: any
|} | {|
// checklist - preselected - ListItemBare
...OptionsCommon,
...OptionsCheckbox,
items: ListItemBare[],
idKey: string,
labelKey: string,
selectedIds: any
|}
var callbacks = { type ListType =
error: (err, op) => console.error(err, op), | typeof DialogAndroid.listCheckbox
| typeof DialogAndroid.listPlain
| typeof DialogAndroid.listRadio;
type ActionType =
| typeof DialogAndroid.actionDismiss
| typeof DialogAndroid.actionNegative
| typeof DialogAndroid.actionNeutral
| typeof DialogAndroid.actionPositive
| typeof DialogAndroid.actionSelect;
type Options = OptionsCommon | OptionsPicker | OptionsProgress | OptionsPrompt;
type OptionsProgress = {|
contentColor?: $PropertyType<OptionsCommon, 'contentColor'>,
contentIsHtml?: $PropertyType<OptionsCommon, 'contentIsHtml'>,
linkColor?: $PropertyType<OptionsCommon, 'linkColor'>,
style?: ProgressStyle,
title?: $PropertyType<OptionsCommon, 'title'>,
titleColor?: $PropertyType<OptionsCommon, 'titleColor'>,
widgetColor?: $PropertyType<OptionsCommon, 'widgetColor'>
|}
type ProgressStyle = typeof DialogAndroid.progressHorizontal;
type OptionsPrompt = {|
...OptionsCommon,
keyboardType?: 'numeric' | 'numbers-and-punctuation' | 'numeric-password' | 'email-address' | 'password' | 'phone-pad' | 'decimal-pad',
defaultValue?: string,
placeholder?: string,
allowEmptyInput?: boolean,
minLength?: number,
maxLength?: number
|}
type Title = void | null | string;
type Content = void | null | string;
type NativeConfig = {|
...OptionsCommon,
items: string[],
widgetColor?: ColorValue,
selectedIndices?: number[],
selectedIndex?: number[],
progress?: {
indeterminate: true,
style?: 'horizontal'
} }
|}
// Remove callbacks from the options, and store them separately function processColors(nativeConfig: {}) {
callbackNames.forEach(cb => { for (const prop of Object.keys(nativeConfig)) {
if (cb in finalOptions) { if (prop.endsWith('Color')) {
callbacks[cb] = finalOptions[cb]; nativeConfig[prop] = processColor(nativeConfig[prop]);
finalOptions[cb] = true;
}
});
// Handle special case of input separately
if ('input' in finalOptions) {
finalOptions.input = Object.assign({}, finalOptions.input);
var inputCallback = finalOptions.input.callback || (x => console.log(x));
finalOptions.input.callback = true;
callbacks['input'] = inputCallback;
}
// Parse the result form multiple choice dialog
if ('itemsCallbackMultiChoice' in callbacks) {
var originalCallback = callbacks.itemsCallbackMultiChoice;
callbacks.itemsCallbackMultiChoice = selected => {
var indices = selected.split(',').map(x => parseInt(x));
var elements = indices.map(ind => (finalOptions.items || [])[ind]);
if(indices.length === 1 && isNaN(indices[0])){
indices=[] // the case of empty selection
elements=[]
} }
originalCallback(indices, elements);
}
} }
var callbackFunc = (cb, ...rest) => callbacks[cb](...rest);
NativeModules.DialogAndroid.show(finalOptions, callbackFunc);
}
dismiss() {
NativeModules.DialogAndroid.dismiss();
}
list(options, cb){
NativeModules.DialogAndroid.list(options, cb)
}
} }
module.exports = DialogAndroid; function pick(source, ...keys) {
const target = {};
for (const key of keys) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
return target;
}
class DialogAndroid {
static listPlain = 'listPlain'
static listRadio = 'listRadio'
static listCheckbox = 'listCheckbox'
static actionDismiss = 'actionDismiss'
static actionNegative = 'actionNegative'
static actionNeutral = 'actionNeutral'
static actionPositive = 'actionPositive'
static actionSelect = 'actionSelect'
static progressHorizontal = 'progressHorizontal'
static defaults = {
positiveText: 'OK'
}
static dismiss(): void {
NativeModules.DialogAndroid.dismiss();
}
static assignDefaults(defaults: { title?:Title, content?:Content, ...Options }): void {
Object.assign(DialogAndroid.defaults, defaults);
}
static alert(title: Title, content: Content, options?: OptionsCommon = {}): Promise<
{|
action: typeof DialogAndroid.actionPositive | typeof DialogAndroid.actionNegative | typeof DialogAndroid.actionNeutral | typeof DialogAndroid.actionDismiss
|}
> {
return new Promise((resolve, reject) => {
const nativeConfig: NativeConfig = {
...DialogAndroid.defaults,
...options,
onAny: true,
dismissListener: true
};
if (title) nativeConfig.title = title;
if (content) nativeConfig.content = content;
processColors(nativeConfig);
NativeModules.DialogAndroid.show(nativeConfig, (kind: string, ...rest) => {
switch (kind) {
case 'error': {
const [ error, nativeConfig ] = rest;
return reject(`DialogAndroid ${error}. nativeConfig: ${nativeConfig}`);
}
case 'dismissListener': {
return resolve({ action:DialogAndroid.actionDismiss });
}
case 'onAny': {
const [ dialogAction ] = rest;
switch (dialogAction) {
case 0: return resolve({ action:DialogAndroid.actionPositive });
case 1: return resolve({ action:DialogAndroid.actionNeutral });
case 2: return resolve({ action:DialogAndroid.actionNegative });
}
}
default: {
return reject(`Unknown callback kind: "${kind}"`);
}
}
});
});
}
static showPicker(title: Title, content: Content, options: OptionsPicker): Promise<
{|
action: typeof DialogAndroid.actionNegative | typeof DialogAndroid.actionNeutral | typeof DialogAndroid.actionDismiss
|} | {|
action: typeof DialogAndroid.actionSelect,
selectedItem: ListItem
|} | {|
action: typeof DialogAndroid.actionSelect,
selectedItems: ListItem[]
|}
> {
// options is required, must defined items
return new Promise((resolve, reject) => {
const {
idKey='id',
items,
labelKey='label',
type,
neutralIsClear,
selectedId,
selectedIds,
...filteredOptions
} = options;
const nativeConfig: NativeConfig = {
...DialogAndroid.defaults,
...filteredOptions,
onAny: true,
dismissListener: true
};
if (title) nativeConfig.title = title;
if (content) nativeConfig.content = content;
if (items) {
nativeConfig.items = items.map(item => item[labelKey]);
switch (type) {
case DialogAndroid.listCheckbox: {
nativeConfig.itemsCallbackMultiChoice = true;
if (selectedIds) {
nativeConfig.selectedIndices = selectedIds.map(id => items.findIndex(item => item[idKey] === id));
}
break;
}
case DialogAndroid.listRadio: {
nativeConfig.itemsCallbackSingleChoice = true;
if (selectedId !== undefined) {
nativeConfig.selectedIndex = items.findIndex(item => item[idKey] === selectedId);
}
break;
}
default:
nativeConfig.itemsCallback = true;
}
}
if (neutralIsClear) nativeConfig.multiChoiceClearButton = true;
processColors(nativeConfig);
NativeModules.DialogAndroid.show(nativeConfig, (kind: string, ...rest) => {
switch (kind) {
case 'error': {
const [ error, nativeConfig ] = rest;
return reject(`DialogAndroid ${error}. nativeConfig: ${nativeConfig}`);
}
case 'itemsCallbackMultiChoice': {
const selectedIndices = rest[0].split(',');
let selectedItems;
if (selectedIndices.length === 1 && isNaN(selectedIndices[0])) {
// the case of empty selection
selectedItems = [];
} else {
selectedItems = selectedIndices.map(index => items[index]);
}
return resolve({ action:DialogAndroid.actionPositive, selectedItems });
}
case 'itemsCallback':
case 'itemsCallbackSingleChoice': {
const [ selectedIndex ] = rest;
const selectedItem = items[selectedIndex];
return resolve({ action:DialogAndroid.actionSelect, selectedItem });
}
case 'onAny': {
const [ dialogAction ] = rest;
switch (dialogAction) {
case 0: return resolve({ action:DialogAndroid.actionPositive });
case 1: return resolve({ action:DialogAndroid.actionNeutral });
case 2: return resolve({ action:DialogAndroid.actionNegative });
}
}
case 'dismissListener': {
return resolve({ action:DialogAndroid.actionDismiss });
}
default: {
return reject(`Unknown callback kind: "${kind}"`);
}
}
});
})
}
static showProgress(content: string, options?: OptionsProgress = {}): Promise<
{|
action: typeof DialogAndroid.actionDismiss
|}
> {
return new Promise((resolve, reject) => {
const defaults = pick(DialogAndroid.defaults,
'contentColor',
'contentIsHtml',
'linkColor',
'title',
'widgetColor',
'titleColor'
)
const {
style,
...finalOptions
} = options;
const nativeConfig = {
...defaults,
progress: {
indeterminate: true,
style: style === DialogAndroid.progressHorizontal ? 'horizontal' : undefined
},
cancelable: false,
...finalOptions,
dismissListener: true
}
if (content) nativeConfig.content = ' '.repeat(5) + content;
processColors(nativeConfig);
NativeModules.DialogAndroid.show(nativeConfig, (kind: string, ...rest) => {
switch (kind) {
case 'error': {
const [ error, nativeConfig ] = rest;
return reject(`DialogAndroid ${error}. nativeConfig: ${nativeConfig}`);
}
case 'dismissListener': {
return resolve({ action:DialogAndroid.actionDismiss });
}
}
});
})
}
static prompt(title: Title, content: Content, options?: OptionsPrompt = {}): Promise<
{|
action: typeof DialogAndroid.actionNegative | typeof DialogAndroid.actionNeutral | typeof DialogAndroid.actionDismiss
|} | {|
action: typeof DialogAndroid.actionPositive,
text: string
|}
> {
return new Promise((resolve, reject) => {
const {
keyboardType,
defaultValue,
placeholder,
allowEmptyInput,
minLength,
maxLength,
...finalOptions
} = options;
const inputConfig = {};
if (defaultValue) inputConfig.prefill = defaultValue;
if (placeholder) inputConfig.hint = placeholder;
if (allowEmptyInput !== undefined) inputConfig.allowEmptyInput = allowEmptyInput;
if (minLength) inputConfig.minLength = minLength;
if (maxLength) inputConfig.maxLength = maxLength;
// if (keyboardType) inputConfig.keyboardType = keyboardType; // TODO: support this on native side - https://github.com/aakashns/react-native-dialogs/pull/55
const nativeConfig = {
...DialogAndroid.defaults,
input: inputConfig,
...finalOptions,
onAny: true,
dismissListener: true
}
if (title) nativeConfig.title = title;
if (content) nativeConfig.content = content;
processColors(nativeConfig);
NativeModules.DialogAndroid.show(nativeConfig, (kind: string, ...rest) => {
switch (kind) {
case 'error': {
const [ error, nativeConfig ] = rest;
return reject(`DialogAndroid ${error}. nativeConfig: ${nativeConfig}`);
}
case 'onAny': {
const [ dialogAction ] = rest;
switch (dialogAction) {
case 1: return resolve({ action:DialogAndroid.actionNeutral });
case 2: return resolve({ action:DialogAndroid.actionNegative });
}
}
case 'input': {
const [ text ] = rest;
return resolve({ action:DialogAndroid.actionPositive, text });
}
case 'dismissListener': {
return resolve({ action:DialogAndroid.actionDismiss });
}
case 'cancelListener': {
// fires when input text field is there and hit back or in back to dismiss
return resolve({ action:DialogAndroid.actionDismiss });
}
default: {
return reject(`Unknown callback kind: "${kind}"`);
}
}
});
})
}
}
export default DialogAndroid

704
README.md
View File

@ -1,254 +1,524 @@
[![npm version](https://img.shields.io/npm/v/react-native-dialogs.svg?style=flat-square)](https://www.npmjs.com/package/react-native-dialogs) ## react-native-dialogs
[![npm downloads](https://img.shields.io/npm/dm/react-native-dialogs.svg?style=flat-square)](https://www.npmjs.com/package/react-native-dialogs)
# react-native-dialogs
Material Design dialogs for React Native Android apps (wrapper over [afollestad/material-dialogs](https://github.com/afollestad/material-dialogs))
<img src="https://pbs.twimg.com/media/CSww5lhUAAAE487.png" width="240" hspace="10"><img src="https://pbs.twimg.com/media/CSww5hBUYAA7Ijo.png" width="240" hspace="10"><img src="https://pbs.twimg.com/media/CSww5pMUcAEry95.png" width="227"> An Android only module for Material Design dialogs. This is a wrapper over [afollestad/material-dialogs](https://github.com/afollestad/material-dialogs). This module is designed for Android only with no plans to support iOS.
### Table of Contents
- [Installation](#installation)
- [Manual Linking](#manual-linking)
- [Usage](#usage)
- [API](#api)
- [Properties](#properties)
- [`defaults`](#defaults)
- [`actionDismiss`](#actiondismiss)
- [`actionNegative`](#actionnegative)
- [`actionNeutral`](#actionneutral)
- [`actionPositive`](#actionpositive)
- [`listPlain`](#listplain)
- [`listRadio`](#listradio)
- [`listCheckbox`](#listcheckbox)
- [`progressHorizontal`](#progresshorizontal)
- [Methods](#methods)
- [`alert`](#alert)
- [`assignDefaults`](#assigndefaults)
- [`dismiss`](#dismiss)
- [`prompt`](#prompt)
- [`showPicker`](#showpicker)
- [`showProgress`](#showprogress)
- [Types](#types)
- [Internal Types](#internal-types)
- [`type ActionType`](#type-actiontype)
- [`type ListItem`](#type-listitem)
- [`type ListType`](#type-listtype)
- [`type OptionsCommon`](#type-optionscommon)
- [`type OptionsProgress`](#type-optionsprogress)
- [`type OptionsPicker`](#type-optionspicker)
- [`type OptionsPrompt`](#type-optionsprompt)
- [`type ProgressStyle`](#type-progressstyle)
- [Examples](#examples)
- [Progress overlay](#progress-overlay)
- [List of radio items dismissed on press](#list-of-radio-items-dismissed-on-press)
- [Checklist with clear button](#checklist-with-clear-button)
- [Prompt](#prompt)
- [HTML](#html)
- [assignDefaults](#assigndefaults)
### Installation
1. Install:
- Using [npm](https://www.npmjs.com/#getting-started): `npm install react-native-dialogs --save`
- Using [Yarn](https://yarnpkg.com/): `yarn add react-native-dialogs`
2. [Link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html):
- `react-native link react-native-dialogs`
- Or if this fails, link manually using [these steps](#manual-linking)
3. Compile application using `react-native run-android`
#### Manual Linking
Follow these steps if automatic linking (`react-native link`) failed.
1. Include this module in `android/settings.gradle`:
```
...
include ':autocompletetextview' // Add this
project(':autocompletetextview').projectDir = file("../node_modules/autocompletetextview/android") // Add this
...
include ':app'
```
2. In `android/app/build.gradle`, add the dependency to your app build:
```
dependencies {
...
compile project(':react-native-dialogs') // Add this
}
```
3. In `android/build.gradle`, it should already be there, but in case it is not, add Jitpack maven repository to download the library [afollestad/material-dialogs](https://github.com/afollestad/material-dialogs):
```
allprojects {
repositories {
...
jcenter() // Add this if it is not already here
...
}
}
```
4. In `android/settings.gradle`:
```
rootProject.name = ...
...
include ':react-native-dialogs' // Add this
project(':react-native-dialogs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dialogs/android') // Add this
...
include ':app'
```
5. Import and add package, in `android/app/src/main/.../MainApplication.java`:
```java
...
import android.app.Application;
...
import com.aakashns.reactnativedialogs.ReactNativeDialogsPackage; // Add new import
...
public class MainApplication extends Application implements ReactApplication {
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
...
new ReactNativeDialogsPackage() // Add the package here
);
}
}
```
### Usage
1. Import it in your JS:
```js
import DialogAndroid from 'react-native-dialogs';
```
2. Call API:
```js
class Blah extends Component {
render() {
return <Button title="Show DialogAndroid" onPress={this.showDialogAndroid} />
}
showDialogAndroid = async () => {
const { action } = await DialogAndroid.alert('Title', 'Message');
switch (action) {
case DialogAndroid.actionPositive:
console.log('positive!')
break;
case DialogAndroid.actionNegative:
console.log('negative!')
break;
case DialogAndroid.actionNeutral:
console.log('netural!')
break;
case DialogAndroid.actionDismiss:
console.log('dismissed!')
break;
}
}
}
```
### API
#### Properties
##### `defaults`
> {
> positiveText: 'OK'
> }
The default options to be used by all methods. To modify this, either directly manipulate with `DialogAndroid.defaults = { ... }` or use [`assignDefaults`](#assigndefaults)
##### `actionDismiss`
> static actionDismiss = "actionDismiss"
##### `actionNegative`
> static actionNegative = "actionNegative"
##### `actionNeutral`
> static actionNeutral = "actionNeutral"
##### `actionPositive`
> static actionPositive = "actionPositive"
##### `listPlain`
> static listPlain = "listPlain"
##### `listRadio`
> static listRadio = "listRadio"
##### `listCheckbox`
> static listCheckbox = "listCheckbox"
##### `progressHorizontal`
> static progressHorizontal = "progressHorizontal"
#### Methods
##### `alert`
> static alert(
> title: Title,
> content: Content,
> options: Options
> ): Promise<{| action: "actionDismiss" | "actionNegative" | "actionNeutral" | "actionPositive" |}>
Shows a dialog.
| Parameter | Type | Default | Required | Description |
|-----------|----------------------------------------|---------|----------|--------------------------------------------|
| title | `string, null, void` | | | Title of dialog |
| content | `string, null, void` | | | Message of dialog |
| options | [`OptionsCommon`](#type-optionscommon) | | | See [`OptionsCommon`](#type-optionscommon) |
##### `assignDefaults`
> static assignDefaults({
> [string]: value
> ): void
Set default colors for example, so you don't have to provide it on every method call.
> {
> positiveText: 'OK'
> }
Installation ##### `dismiss`
------------
### Installation using RNPM
1 . `yarn add react-native-dialogs` > static dismiss(): void
or Hides the currently showing dialog.
`npm i react-native-dialogs --save` ##### `prompt`
> static prompt(
> title?: null | string,
> content?: null | string,
> options: OptionsPrompt
> ): Promise<
> {| action: "actionNegative" | "actionNeutral" | "actionDismiss" |} |
> {| action: "actionPositive", text: string |}
> >
Shows a dialog with a text input field.
2 . `react-native link react-native-dialogs` | Parameter | Type | Default | Required | Description |
|-----------|----------------------------------------|---------|----------|--------------------------------------------|
| title | `string, null, void` | | | Title of dialog |
| content | `string, null, void` | | | Message of dialog |
| options | [`OptionsPrompt`](#type-optionsprompt) | | | See [`OptionsPrompt`](#type-optionsprompt) |
In `android/app/build.gradle`, add a dependency to `':react-native-dialogs'` and URL of the Jitpack maven repository (to download the library https://github.com/afollestad/material-dialogs) : ##### `showPicker`
```
repositories {
maven { url "https://jitpack.io" }
}
> static showProgress(
> title?: null | string,
> content?: null | string,
> options: OptionsPicker
> ): Promise<
> {| action: "actionNegative" | "actionNeutral" | "actionDismiss" |} |
> {| action: "actionSelect", selectedItem: ListItem |} |
> {| action: "actionSelect", selectedItems: ListItem[] |}
> >
Shows a regular alert, but also with items that can be selected.
| Parameter | Type | Default | Required | Description |
|-----------|------------------------------------------|---------|----------|--------------------------------------------|
| title | `string, null, void` | | | Title of dialog |
| content | `string, null, void` | | | Message of dialog |
| options | [`OptionsPicker`](#type-optionsprogress) | | | See [`OptionsPrompt`](#type-optionspicker) |
##### `showProgress`
> static showProgress(
> content?: null | string,
> options: OptionsProgress
> ): Promise<{| action:"actionDismiss" |}>
Shows a progress dialog. By default no buttons are shown, and hardware back button does not close it. You must close it with `DialogAndroid.dismiss()`.
| Parameter | Type | Default | Required | Description |
|-----------|--------------------------------------------|---------|----------|----------------------------------------------|
| content | `string, null, void` | | | Message of dialog |
| options | [`OptionsProgress`](#type-optionsprogress) | | | See [`OptionsPrompt`](#type-optionsprogress) |
### Types
[Flow](http://flow.org/) is used as the typing system.
#### Internal Types
##### `type ActionType`
> "actionDismiss" | "actionNegative" | "actionNeutral" | "actionPositive" | "actionSelect"
##### `type ListItem`
> { label:string } | { label:string, id:any } | {}
**Notes**
* If `label` key does not exist, specify which key should be used as the label with `labelKey` property of [`OptionsPicker`](#type-optionspicker).
* `id` is only required if `selectedId`/`selectedIds` needs to be used.
* If `id` key does not exist, specify which key should be used as the id with `idKey` property of [`OptionsPicker`](#type-optionspicker).
##### `type ListType`
> "listCheckbox" | "listPlain" | "listRadio"
##### `type OptionsCommon`
> {
> cancelable?: boolean,
> content?: string,
> contentColor?: string,
> contentIsHtml?: boolean,
> forceStacking?: boolean
> linkColor?: ColorValue,
> negativeColor?: ColorValue,
> negativeText?: string,
> neutralColor?: ColorValue,
> neutralText?: string,
> positiveColor?: ColorValue,
> positiveText?: string, // default "OK"
> title?: string,
> titleColor?: ColorValue,
> }
| Key | Type | Default | Required | Description |
|---------------|----------------------------------------------------------------------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| cancelable | `boolean` | | | If tapping outside of dialog area, or hardware back button, should dismiss dialog. |
| content | `string` | | | Dialog message |
| contentColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | Color of dialog message |
| contentIsHtml | `boolean` | | | If dialog message should be parsed as html. (supported tags include: `<a>`, `<img>`, etc) |
| forceStacking | `boolean` | | | If you have multiple action buttons that together are too wide to fit on one line, the dialog will stack the buttons to be vertically oriented. |
| linkColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | If `contentIsHtml` is true, and `content` contains `<a>` tags, these are colored with this color |
| negativeColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | |
| negativeText | `string` | | | If falsy, button is not shown. |
| neutralColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | |
| neutralText | `string` | | | Shows button in far left with this string as label. If falsy, button is not shown. |
| positiveColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | |
| positiveText | `string` | | | If falsy, button is not shown. |
| title | `string` | | | Title of dialog |
| titleColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | Color of title |
##### `type OptionsProgress`
> {
> contentColor: $PropertyType<OptionsCommon, 'contentColor'>,
> contentIsHtml: $PropertyType<OptionsCommon, 'contentIsHtml'>,
> linkColor: $PropertyType<OptionsCommon, 'linkColor'>,
> style?: ProgressStyle,
> title: $PropertyType<OptionsCommon, 'title'>,
> titleColor: $PropertyType<OptionsCommon, 'titleColor'>',
> widgetColor: $PropertyType<OptionsCommon, 'widgetColor'>
> widgetColor?: ColorValue
> }
| Key | Type | Default | Required | Description |
|---------------|----------------------------------------------------------------------------|---------|----------|----------------------------------------------------------|
| contentColor | [`OptionsCommon#contentColor`](#type-optionscommon) | | | See [`OptionsCommon#contentColor`](#type-optionscommon) |
| contentIsHtml | [`OptionsCommon#contentIsHtml`](#type-optionscommon) | | | See [`OptionsCommon#contentIsHtml`](#type-optionscommon) |
| linkColor | [`OptionsCommon#linkColor`](#type-optionscommon) | | | See [`OptionsCommon#linkColor`](#type-optionscommon) |
| style | [`ProgressStyle`](#type-ProgressStyle) | | | See [`ProgressStyle`](#type-progressstyle) |
| title | [`OptionsCommon#title`](#type-optionscommon) | | | See [`OptionsCommon#title`](#type-optionscommon) |
| titleColor | [`OptionsCommon#titleColor`](#type-optionscommon) | | | See [`OptionsCommon#titleColor`](#type-optionscommon) |
| widgetColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | Color of progress indicator |
##### `type OptionsPicker`
> {
> ...OptionsCommon,
> idKey?: string,
> items: ListItem[],
> labelKey?: string,
> neutralIsClear?: boolean,
> selectedId?: any,
> selectedIds?: any[],
> type?: string,
> widgetColor?: ColorValue
> }
| Key | Type | Default | Required | Description |
|----------------|----------------------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OptionsCommon | [`OptionsCommon`](#type-optionscommon) | | | See [`OptionsCommon`](#type-optionscommon) |
| idKey | `string` | "id" | | |
| items | [`ListItem`](#type-listitem)[] | | Yes | See [`ListItem`](#type-listitem) |
| labelKey | `string` | "label" | | |
| neutralIsClear | `boolean` | | | Pressing the neutral button causes the dialog to be closed and `selectedItems` to be an empty array. Only works if `neutralText` is also supplied. |
| selectedId | `any` | | | The respective radio will be selected on dialog show. If no such id is found, then nothing is selected. Only applicable if `type` is `DialogAndroid.listRadio`. Requires that `items[]` contain key described by `idKey`. |
| selectedIds | `any[]` | | | The respective checkbox will be selected on dialog show. If no such id is found, nothing is selected for that id. Only applicable if `type` is `DialogAndroid.listCheckbox`. Requires that `items[]` contain key described by `idKey`. |
| type | [`ListType`](#type-listtype) | `DialogAndroid.listPlain` | | See [`ListType`](#type-listtype) |
| widgetColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | Color of radio or checkbox |
##### `type OptionsPrompt`
> {
> ...OptionsCommon,
>
> widgetColor?: ColorValue
> }
| Key | Type | Default | Required | Description |
|---------------|----------------------------------------------------------------------------|---------|----------|--------------------------------------------|
| OptionsCommon | [`OptionsCommon`](#type-optionscommon) | | | See [`OptionsCommon`](#type-optionscommon) |
| widgetColor | [`ColorValue`](https://facebook.github.io/react-native/docs/colors.html) | | | Color of field underline and cursor |
##### `type ProgressStyle`
> "progressHorizontal"
### Examples
#### Progress dialog
```js
DialogAndroid.showProgress(null, 'Downloading...', {
style: DialogAndroid.progressHorizontal
});
setTimeout(DialogAndroid.dismiss, 5000);
``` ```
### Manual installation #### List of radio items dismissed on press
Install the npm package [`react-native-dialogs`](https://www.npmjs.com/package/react-native-dialogs). Inside your React Native project, run ([example](https://github.com/aakashns/react-native-dialogs-example/commit/e6b83bf3d2238cf7e4ec3688519f38b2544ccad5)): If we want the first press on an item to close and accept the dialog, we pass `null` to `positiveText`:
```bash
npm install --save react-native-dialogs
```
```js
In `android/app/build.gradle`, add a dependency to `':react-native-dialogs'` and URL of the Jitpack maven repository (to download the library https://github.com/afollestad/material-dialogs) : const { selectedItem } = await DialogAndroid.alert('Title', null, {
``` positiveText: null,
repositories { items: [
maven { url "https://jitpack.io" } { label:'Apple', id:'apple' },
} { label:'Orange', id:'orange' },
{ label:'Pear', id:'pear' }
``` ],
The changes should look like [this](https://github.com/aakashns/react-native-dialogs-example/commit/b58086d8fb9ece99f0e678dd8bf0e689a856bd43). selectedId: 'apple'
});
You also need to update your ```settings.gradle``` and add: if (selectedItem) {
``` console.log('You selected item:', item);
include ':react-native-dialogs'
project(':react-native-dialogs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-dialogs/android')
```
update your ```app.gradle``` and add:
```
dependencies {
compile project(':react-native-dialogs') // <-- Add this line
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
//...
} }
``` ```
Next, you need to change the `MainApplication` of your app to register `ReactNativeDialogsPackage` :
```java
// ... Other imports
import com.aakashns.reactnativedialogs.ReactNativeDialogsPackage; // <-- Add this import.
//... #### Checklist with clear button
public class MainApplication extends ReactActivity {
//...
@Override We can make the neutral button be a special button. Pressing it will clear the list and close the dialog.
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new ReactNativeDialogsPackage() // <-- Add this package.
);
}
}
``` ```js
See [this changelog](https://github.com/aakashns/react-native-dialogs-example/commit/52cac27756963bcd2f4fdcd039e1a78028bb0abd) for reference(Earlier versions of React native used MainActivity instead of MainApplication). const { selectedItems } = await DialogAndroid.alert('Title', null, {
items: [
Now you're finally ready to start using module in your React Native application. See [this changelog](https://github.com/aakashns/react-native-dialogs-example/commit/2d8e02c22275479d2fbbb89f99dcb846834bec9d) for an example that uses `DialogAndroid`. { label:'Apple', id:'apple' },
{ label:'Orange', id:'orange' },
Usage { label:'Pear', id:'pear' }
----- ],
```javascript selectedIds: ['apple', 'orange'], // or if is not array of objects with "id" can use selectedIndices
import DialogAndroid from 'react-native-dialogs'; neutralIsClear: true,
neutralText: 'Empty List'
let options = { });
title: 'Hello, World!', if (selectedItems) {
content: 'I\'m just simple Dialog', if (!selectedItems.length) {
positiveText: 'OK', console.log('You emptied the list');
negativeText: 'Cancel' } else {
}; console.log('You selected items:', selectedItems);
}
let showDialog = function () {
var dialog = new DialogAndroid();
dialog.set(options);
dialog.show();
} }
``` ```
Creation of a dialog works in 3 steps :
1. Create a new dialog using `new DialogAndroid()`.
2. Set some options using `dialog.set(options)`. `set` can be called multiple times, and options from multiple calls will be merged.
3. Show the dialog using `dialog.show()`.
4. (optional) dismiss the dialog with `dialog.dismiss()`.
This library is a thin wrapper over [afollestad/material-dialogs](https://github.com/afollestad/material-dialogs), which provides builders for showing Material Design dialogs in Android apps. The options provided to `set` map more or less directly to the methods provided in the original library. See [its documentation](https://github.com/afollestad/material-dialogs#basic-dialog) for reference. #### Prompt
The following options are currently supported (value type is `String` unless mentioned otherwise) : ```js
* [`title`](https://github.com/afollestad/material-dialogs#basic-dialog) const { action, text } = await DialogAndroid.prompt('Title', 'Message', {
* [`titleColor`](https://github.com/afollestad/material-dialogs#colors) isHorizontal:true
* [`content`](https://github.com/afollestad/material-dialogs#basic-dialog) });
* [`contentColor`](https://github.com/afollestad/material-dialogs#colors) if (action === DialogAndroid.actionPositive) {
* [`positiveText`](https://github.com/afollestad/material-dialogs#basic-dialog) alert(`You submitted: ${text}`)
* [`positiveColor`](https://github.com/afollestad/material-dialogs#basic-dialog)
* [`onPositive`](https://github.com/afollestad/material-dialogs#callbacks) (function with no arguments)
* [`negativeText`](https://github.com/afollestad/material-dialogs#basic-dialog)
* [`negativeColor`](https://github.com/afollestad/material-dialogs#basic-dialog)
* [`onNegative`](https://github.com/afollestad/material-dialogs#callbacks) (function with no arguments)
* [`neutralText`](https://github.com/afollestad/material-dialogs#neutral-action-button)
* [`neutralColor`](https://github.com/afollestad/material-dialogs#neutral-action-button)
* [`onNeutral`](https://github.com/afollestad/material-dialogs#callbacks) (function with no arguments)
* [`onAny`](https://github.com/afollestad/material-dialogs#callbacks) (function with no arguments)
* [`items`](https://github.com/afollestad/material-dialogs#list-dialogs) (array of strings)
* [`itemsCallback`](https://github.com/afollestad/material-dialogs#list-dialogs) (function with 2 arguments : selectedIndex (int) and selectedItem (string))
* [`itemsCallbackSingleChoice`](https://github.com/afollestad/material-dialogs#single-choice-list-dialogs) (function with 2 arguments : selectedIndex (int) and selectedItem (string))
* [`selectedIndex`](https://github.com/afollestad/material-dialogs#single-choice-list-dialogs) (int) - set the preselected index for Single Choice List
* [`itemsCallbackMultiChoice`](https://github.com/afollestad/material-dialogs#multi-choice-list-dialogs) (function with 2 arguments : selected indices (array of ints) and selected items (array of strings)
* [`selectedIndices`](https://github.com/afollestad/material-dialogs#multi-choice-list-dialogs) (array of ints) - set the preselected indices for Multiple Choice List
* [`widgetColor`](https://github.com/afollestad/material-dialogs#coloring-radio-buttons) - set the color of Radio Buttons and EditText
* [`linkColor`](https://github.com/afollestad/material-dialogs#colors)
* `multiChoiceClearButton` (boolean) - provide a 'Clear' button in Multiple Choice List
* `autoDismiss` (boolean)
* [`forceStacking`](https://github.com/afollestad/material-dialogs#stacked-action-buttons) (boolean)
* [`alwaysCallSingleChoiceCallback`](https://github.com/afollestad/material-dialogs#single-choice-list-dialogs) (boolean)
* [`alwaysCallMultiChoiceCallback`](https://github.com/afollestad/material-dialogs#multi-choice-list-dialogs) (boolean)
* [`cancelable`](https://github.com/afollestad/material-dialogs#show-cancel-and-dismiss-callbacks) (boolean)
* [`showListener`](https://github.com/afollestad/material-dialogs#show-cancel-and-dismiss-callbacks) (function)
* [`cancelListener`](https://github.com/afollestad/material-dialogs#show-cancel-and-dismiss-callbacks) (function)
* [`dismissListener`](https://github.com/afollestad/material-dialogs#show-cancel-and-dismiss-callbacks) (function)
* [`input`](https://github.com/afollestad/material-dialogs#input-dialogs) - Object containing the following keys (all optional except callback) :
* [`hint`](https://github.com/afollestad/material-dialogs#input-dialogs)
* [`prefill`](https://github.com/afollestad/material-dialogs#input-dialogs)
* [`allowEmptyInput`](https://github.com/afollestad/material-dialogs#input-dialogs) (boolean)
* [`minLength`](https://github.com/afollestad/material-dialogs#limiting-input-length) (int)
* [`maxLength`](https://github.com/afollestad/material-dialogs#limiting-input-length) (int)
* [`type`](https://github.com/afollestad/material-dialogs#input-dialogs) (int)
* [`callback`](https://github.com/afollestad/material-dialogs#input-dialogs) (function with 1 argument : user provided input)
* [`alwaysCallInputCallback`](https://github.com/afollestad/material-dialogs#input-dialogs) (boolean)
* [`progress`](https://github.com/afollestad/material-dialogs#progress-dialogs) - Object containing following keys
* [`indeterminate`](https://github.com/afollestad/material-dialogs#indeterminate-progress-dialogs) (boolean) - must be true, determinate is not supported
* [`style`](https://github.com/afollestad/material-dialogs#make-an-indeterminate-dialog-horizontal) - (string) either 'horizontal' or undefined
Examples
--------
Simple example project : https://github.com/aakashns/react-native-dialogs-example
Complex example project : [examples/ExampleApp](https://github.com/aakashns/react-native-dialogs/tree/54d1253213b1a6a453a3ffb1d2dcc65b8dc287fd/examples/ExampleApp)
Try out the following values for option (taken from [examples/ExampleApp/dialogData.js](https://github.com/aakashns/react-native-dialogs/tree/54d1253213b1a6a453a3ffb1d2dcc65b8dc287fd/examples/ExampleApp/dialogData.js)):
```javascript
{
"title": "Use Google's Location Services?",
"content": "This app wants to access your location.",
"positiveText": "Agree",
"negativeText": "Disagree"
} }
``` ```
```javascript #### HTML
{
"title": "Use Google's Location Services?", ```js
"content": "Let Google help apps determine location. This means sending anonymous location data to Google, even when no apps are running.", DialogAndroid.alert('Title', `This is a link <a href="https://www.duckduckgo.com/">DuckDuckGo</a>`, {
"positiveText": "Agree", contentIsHtml: true
"negativeText": "Disagree", });
"neutralText": "More Info",
"onPositive": () => ToastAndroid.show("POSITIVE!", ToastAndroid.SHORT),
"onNegative": () => ToastAndroid.show("NEGATIVE!", ToastAndroid.SHORT),
"onNeutral": () => ToastAndroid.show("NEUTRAL!", ToastAndroid.SHORT),
}
``` ```
```javascript #### assignDefaults
"data": {
"items": [
"Twitter",
"Google+",
"Instagram",
"Facebook"
],
"title": "Social Networks",
itemsCallback: (id, text) => ToastAndroid.show(id + ": " + text, ToastAndroid.SHORT);
}
```
```javascript You can set some defaults so you don't have to change it everytime.
"data": {
"items": [
"Twitter",
"Google+",
"Instagram",
"Facebook"
],
"title": "Social Networks",
itemsCallbackSingleChoice: (id, text) => ToastAndroid.show(id + ": " + text, ToastAndroid.SHORT);
}
```
```javascript ```js
"data": { DialogAndroid.assignDefaults({
"items": [ title: 'Default Title',
"Twitter", contentColor: 'rgba(0, 0, 0, 0.2)',
"Google+", widgetColor: 'blue'
"Instagram", })
"Facebook"
],
"title": "Social Networks",
"positiveText": "Choose",
itemsCallbackMultiChoice: (id, text) => ToastAndroid.show(id + ": " + text, ToastAndroid.SHORT);
}
```
Progress dialog colored blue, with no buttons, disables hardware back, and dismisses after 5 seconds:
```
const dialog = new DialogAndroid();
dialog.set({
content: 'Downloading...',
progress: {
indeterminate: true,
style: 'horizontal'
},
widgetColor: 'blue',
cancelable: false
})
dialog.show();
setTimeout(dialog.dismiss, 5000);
``` ```
Known Issues Now any time you supply `undefined` to title, it will use the default assigned above.
------------
TODO ```js
DialogAndroid.alert(undefined, 'message here')
```
This will show title "Default Title", with no negative button, and the color of the message will be 20% black. If you did `Dialog.showProgress`, the progress indicator would be blue. etc.
Upcoming Features
-------
TODO

View File

@ -2,7 +2,9 @@ package com.aakashns.reactnativedialogs.modules;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Color; import android.graphics.Color;
import android.text.Html;
import android.view.View; import android.view.View;
import android.os.Build;
import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.GravityEnum; import com.afollestad.materialdialogs.GravityEnum;
@ -46,37 +48,48 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
builder.title(options.getString("title")); builder.title(options.getString("title"));
break; break;
case "content": case "content":
builder.content(options.getString("content")); if(options.hasKey("contentIsHtml") && options.getBoolean("contentIsHtml")) {
// // i have no idea how to get this to work, it seems its all api level 24 stuff
// // requires buildToolsVersion >= "24.0.1"
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// builder.content(Html.fromHtml(options.getString("content"), Html.FROM_HTML_MODE_LEGACY | Html.FROM_HTML_MODE_COMPACT));
// } else {
// builder.content(Html.fromHtml(options.getString("content"), Html.FROM_HTML_MODE_COMPACT));
// }
builder.content(Html.fromHtml(options.getString("content")));
} else {
builder.content(options.getString("content"));
}
break; break;
case "positiveText": case "positiveText":
builder.positiveText(options.getString("positiveText")); builder.positiveText(options.getString("positiveText"));
break; break;
case "positiveColor": case "positiveColor":
builder.positiveColor(Color.parseColor(options.getString("positiveColor"))); builder.positiveColor(options.getInt("positiveColor"));
break; break;
case "negativeText": case "negativeText":
builder.negativeText(options.getString("negativeText")); builder.negativeText(options.getString("negativeText"));
break; break;
case "negativeColor": case "negativeColor":
builder.negativeColor(Color.parseColor(options.getString("negativeColor"))); builder.negativeColor(options.getInt("negativeColor"));
break; break;
case "neutralText": case "neutralText":
builder.neutralText(options.getString("neutralText")); builder.neutralText(options.getString("neutralText"));
break; break;
case "neutralColor": case "neutralColor":
builder.neutralColor(Color.parseColor(options.getString("neutralColor"))); builder.neutralColor(options.getInt("neutralColor"));
break; break;
case "titleColor": case "titleColor":
builder.titleColor(Color.parseColor(options.getString("titleColor"))); builder.titleColor(options.getInt("titleColor"));
break; break;
case "widgetColor": case "widgetColor":
builder.widgetColor(Color.parseColor(options.getString("widgetColor"))); builder.widgetColor(options.getInt("widgetColor"));
break; break;
case "linkColor": case "linkColor":
builder.linkColor(Color.parseColor(options.getString("linkColor"))); builder.linkColor(options.getInt("linkColor"));
break; break;
case "contentColor": case "contentColor":
builder.contentColor(Color.parseColor(options.getString("contentColor"))); builder.contentColor(options.getInt("contentColor"));
break; break;
case "items": case "items":
ReadableArray arr = options.getArray("items"); ReadableArray arr = options.getArray("items");
@ -172,6 +185,7 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
MaterialDialog.Builder mBuilder; MaterialDialog.Builder mBuilder;
MaterialDialog mDialog; MaterialDialog mDialog;
private boolean mCallbackConsumed = false;
@ReactMethod @ReactMethod
public void show(ReadableMap options, final Callback callback) { public void show(ReadableMap options, final Callback callback) {
@ -186,7 +200,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.onPositive(new MaterialDialog.SingleButtonCallback() { mBuilder.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override @Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
callback.invoke("onPositive"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("onPositive");
}
} }
}); });
} }
@ -195,7 +212,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.onNegative(new MaterialDialog.SingleButtonCallback() { mBuilder.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override @Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
callback.invoke("onNegative"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("onNegative");
}
} }
}); });
} }
@ -204,7 +224,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.onNeutral(new MaterialDialog.SingleButtonCallback() { mBuilder.onNeutral(new MaterialDialog.SingleButtonCallback() {
@Override @Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
callback.invoke("onNeutral"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("onNeutral");
}
} }
}); });
} }
@ -213,7 +236,16 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.onAny(new MaterialDialog.SingleButtonCallback() { mBuilder.onAny(new MaterialDialog.SingleButtonCallback() {
@Override @Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
callback.invoke("onAny"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
if (dialogAction == DialogAction.POSITIVE) {
callback.invoke("onAny", 0);
} else if (dialogAction == DialogAction.NEUTRAL) {
callback.invoke("onAny", 1);
} else {
callback.invoke("onAny", 2);
}
}
} }
}); });
} }
@ -223,7 +255,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
@Override @Override
public void onSelection(MaterialDialog materialDialog, View view, int i, public void onSelection(MaterialDialog materialDialog, View view, int i,
CharSequence charSequence) { CharSequence charSequence) {
callback.invoke("itemsCallback", i, charSequence == null ? null : charSequence.toString()); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("itemsCallback", i, charSequence == null ? null : charSequence.toString());
}
} }
}); });
} }
@ -237,8 +272,11 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
@Override @Override
public boolean onSelection(MaterialDialog materialDialog, View view, int i, public boolean onSelection(MaterialDialog materialDialog, View view, int i,
CharSequence charSequence) { CharSequence charSequence) {
charSequence = charSequence == null ? "" : charSequence; if (!mCallbackConsumed) {
callback.invoke("itemsCallbackSingleChoice", i, charSequence.toString()); mCallbackConsumed = true;
charSequence = charSequence == null ? "" : charSequence;
callback.invoke("itemsCallbackSingleChoice", i, charSequence.toString());
}
return true; return true;
} }
}); });
@ -270,7 +308,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
selected.append(integers[integers.length - 1]); selected.append(integers[integers.length - 1]);
} }
callback.invoke("itemsCallbackMultiChoice", selected.toString()); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("itemsCallbackMultiChoice", selected.toString());
}
return true; return true;
} }
}); });
@ -278,7 +319,7 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
// Provide a 'Clear' button to unselect all choices // Provide a 'Clear' button to unselect all choices
if (options.hasKey("multiChoiceClearButton") && if (options.hasKey("multiChoiceClearButton") &&
options.getBoolean("multiChoiceClearButton")) { options.getBoolean("multiChoiceClearButton")) {
mBuilder.neutralText("Clear").onNeutral(new MaterialDialog.SingleButtonCallback() { mBuilder.onNeutral(new MaterialDialog.SingleButtonCallback() {
@Override @Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) { public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
materialDialog.clearSelectedIndices(); materialDialog.clearSelectedIndices();
@ -287,20 +328,25 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
} }
} }
if (options.hasKey("showListener")) { mBuilder.showListener(new DialogInterface.OnShowListener() {
mBuilder.showListener(new DialogInterface.OnShowListener() { @Override
@Override public void onShow(DialogInterface dialog) {
public void onShow(DialogInterface dialog) { // if (!mCallbackConsumed) {
callback.invoke("showListener"); // mCallbackConsumed = true;
} // callback.invoke("showListener");
}); // }
} mCallbackConsumed = false;
}
});
if (options.hasKey("cancelListener")) { if (options.hasKey("cancelListener")) {
mBuilder.cancelListener(new DialogInterface.OnCancelListener() { mBuilder.cancelListener(new DialogInterface.OnCancelListener() {
@Override @Override
public void onCancel(DialogInterface dialog) { public void onCancel(DialogInterface dialog) {
callback.invoke("cancelListener"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("cancelListener");
}
} }
}); });
} }
@ -309,7 +355,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.dismissListener(new DialogInterface.OnDismissListener() { mBuilder.dismissListener(new DialogInterface.OnDismissListener() {
@Override @Override
public void onDismiss(DialogInterface dialog) { public void onDismiss(DialogInterface dialog) {
callback.invoke("dismissListener"); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("dismissListener");
}
} }
}); });
} }
@ -338,7 +387,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
mBuilder.input(hint, prefill, allowEmptyInput, new MaterialDialog.InputCallback() { mBuilder.input(hint, prefill, allowEmptyInput, new MaterialDialog.InputCallback() {
@Override @Override
public void onInput(MaterialDialog materialDialog, CharSequence charSequence) { public void onInput(MaterialDialog materialDialog, CharSequence charSequence) {
callback.invoke("input", charSequence.toString()); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke("input", charSequence.toString());
}
} }
}); });
} }
@ -369,7 +421,10 @@ public class DialogAndroid extends ReactContextBaseJavaModule {
.adapter(simpleListAdapter, new MaterialDialog.ListCallback() { .adapter(simpleListAdapter, new MaterialDialog.ListCallback() {
@Override @Override
public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) {
callback.invoke(which, text); if (!mCallbackConsumed) {
mCallbackConsumed = true;
callback.invoke(which, text);
}
if (simple != null) { if (simple != null) {
simple.dismiss(); simple.dismiss();
} }