2018-05-15 10:59:36 -07:00
// @flow
import { NativeModules } from 'react-native'
import processColor from 'react-native/Libraries/StyleSheet/processColor'
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
import type { ColorValue } from 'react-native/Libraries/StyleSheet/StyleSheetTypes'
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
type IdKey = string | 'id' ;
type LabelKey = string | 'label' ;
type ListItem = { label : string , id ?: any };
2015-11-01 22:58:44 +05:30
2018-05-30 22:10:29 -07:00
type OptionsAlert = { |
... OptionsCheckbox ,
... OptionsCommon
| }
type OptionsCheckbox = { |
checkboxLabel ?: string ,
checkboxDefaultValue ?: boolean
| }
2018-05-15 10:59:36 -07:00
type OptionsCommon = { |
title ?: null | string ,
titleColor ?: ColorValue ,
content ?: null | string ,
contentIsHtml ?: boolean ,
contentColor ?: string ,
positiveText ?: string , // default "OK"
negativeText ?: string ,
neutralText ?: string ,
positiveColor ?: ColorValue ,
negativeColor ?: ColorValue ,
neutralColor ?: ColorValue ,
cancelable ?: boolean ,
linkColor ?: ColorValue , // applies if contentIsHtml is true, and there are <a> elements in content string
forceStacking ?: boolean
| }
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
type ListItemJustLabel = { label : string };
type ListItemJustId = { id : string };
type ListItemFull = { label : string , id : any };
type ListItemBare = {};
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
type OptionsRadio = { |
type : typeof ListType . listRadio ,
widgetColor ?: ColorValue // radio color
| }
type OptionsCheckbox = { |
type : typeof ListType . listCheckbox ,
neutralIsClear ?: boolean ,
widgetColor ?: ColorValue // checkbox color
| }
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
type OptionsPicker = { |
... 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
| }
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
type ListType =
| 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'
2015-11-01 22:58:44 +05:30
}
2018-05-15 10:59:36 -07:00
| }
2015-11-01 22:58:44 +05:30
2018-05-15 10:59:36 -07:00
function processColors ( nativeConfig : {}) {
for ( const prop of Object . keys ( nativeConfig )) {
if ( prop . endsWith ( 'Color' )) {
nativeConfig [ prop ] = processColor ( nativeConfig [ prop ]);
2016-06-17 11:25:31 +02:00
}
2015-11-01 22:58:44 +05:30
}
}
2018-05-15 10:59:36 -07:00
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 );
}
2018-05-30 22:10:29 -07:00
static alert ( title : Title , content : Content , options ?: OptionsAlert = {}) : Promise <
{ | action : typeof DialogAndroid . actionPositive | typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral | typeof DialogAndroid . actionDismiss | } |
{ | action : typeof DialogAndroid . actionPositive | typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral , checked : boolean | }
2018-05-15 10:59:36 -07:00
> {
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' : {
2018-05-30 22:10:29 -07:00
const [ dialogAction , checked ] = rest ;
2018-05-15 10:59:36 -07:00
switch ( dialogAction ) {
2018-05-30 22:10:29 -07:00
case 0 : return resolve ({ action : DialogAndroid . actionPositive , ... getChecked ( nativeConfig , checked ) });
case 1 : return resolve ({ action : DialogAndroid . actionNeutral , ... getChecked ( nativeConfig , checked ) });
case 2 : return resolve ({ action : DialogAndroid . actionNegative , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
}
default : {
return reject ( `Unknown callback kind: " ${ kind } "` );
}
}
});
});
}
static showPicker ( title : Title , content : Content , options : OptionsPicker ) : Promise <
2018-05-30 22:10:29 -07:00
{ | action : typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral | typeof DialogAndroid . actionDismiss | } |
{ | action : typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral , checked : boolean | } |
{ | action : typeof DialogAndroid . actionSelect , selectedItem : ListItem | } |
{ | action : typeof DialogAndroid . actionSelect , selectedItem : ListItem , checked : boolean | } |
{ | action : typeof DialogAndroid . actionSelect , selectedItems : ListItem [] | } |
{ | action : typeof DialogAndroid . actionSelect , selectedItems : ListItem [], checked : boolean | }
2018-05-15 10:59:36 -07:00
> {
// 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' : {
2018-05-30 22:10:29 -07:00
const [ selectedIndicesString , checked ] = rest ; // blank string when nothing selected
2018-05-16 12:26:58 -07:00
const selectedItems = selectedIndicesString === '' ? [] : selectedIndicesString . split ( ',' ). map ( index => items [ index ]);
2018-05-15 10:59:36 -07:00
2018-05-30 22:10:29 -07:00
return resolve ({ action : DialogAndroid . actionPositive , selectedItems , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
case 'itemsCallback' :
case 'itemsCallbackSingleChoice' : {
2018-05-30 22:10:29 -07:00
const [ selectedIndex , checked ] = rest ;
2018-05-15 10:59:36 -07:00
const selectedItem = items [ selectedIndex ];
2018-05-30 22:10:29 -07:00
return resolve ({ action : DialogAndroid . actionSelect , selectedItem , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
case 'onAny' : {
2018-05-30 22:10:29 -07:00
const [ dialogAction , checked ] = rest ;
2018-05-15 10:59:36 -07:00
switch ( dialogAction ) {
2018-05-30 22:10:29 -07:00
case 0 : return resolve ({ action : DialogAndroid . actionPositive , ... getChecked ( nativeConfig , checked ) });
case 1 : return resolve ({ action : DialogAndroid . actionNeutral , ... getChecked ( nativeConfig , checked ) });
case 2 : return resolve ({ action : DialogAndroid . actionNegative , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
}
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
}
2018-05-16 12:26:58 -07:00
if ( content ) nativeConfig . content = content ;
if ( content && style !== DialogAndroid . progressHorizontal ) nativeConfig . content = ' ' + content ;
2018-05-15 10:59:36 -07:00
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 <
2018-05-30 22:10:29 -07:00
{ | action : typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral | typeof DialogAndroid . actionDismiss | } |
{ | action : typeof DialogAndroid . actionNegative | typeof DialogAndroid . actionNeutral , checked : boolean | } |
{ | action : typeof DialogAndroid . actionPositive , text : string | } |
{ | action : typeof DialogAndroid . actionPositive , text : string , checked : boolean | }
2018-05-15 10:59:36 -07:00
> {
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' : {
2018-05-30 22:10:29 -07:00
const [ dialogAction , checked ] = rest ;
2018-05-15 10:59:36 -07:00
switch ( dialogAction ) {
2018-05-30 22:10:29 -07:00
case 1 : return resolve ({ action : DialogAndroid . actionNeutral , ... getChecked ( nativeConfig , checked ) });
case 2 : return resolve ({ action : DialogAndroid . actionNegative , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
}
case 'input' : {
2018-05-30 22:10:29 -07:00
const [ text , checked ] = rest ;
return resolve ({ action : DialogAndroid . actionPositive , text , ... getChecked ( nativeConfig , checked ) });
2018-05-15 10:59:36 -07:00
}
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 } "` );
}
}
});
})
}
}
2018-05-30 22:10:29 -07:00
function getChecked ( nativeConfig , checked ) {
return nativeConfig . checkboxLabel ? { checked } : {};
}
2018-05-15 10:59:36 -07:00
export default DialogAndroid