<file_sep>import { FormlyConfig } from 'react-native-formly';
import _ from 'lodash';

const {
  FieldsConfig,
  WrappersConfig,
  ControllersConfig,
  ValidationsConfig
} = FormlyConfig;
// require formly types
const inputComponent = require('./types/formlyTextInput');
const textareaComponent = require('./types/formlyTextarea');
const radioComponent = require('./types/formlyRadio');
const checkboxComponent = require('./types/formlyCheckbox');
const selectComponent = require('./types/formlySelect');

// require formly wrappers
const labelWrapper = require('./wrappers/formlyLabel');
const descOrErrorWrapper = require('./wrappers/formlyDescriptionOrError');

FieldsConfig.addType([
  {
    name: 'input',
    controller: ['inputTypeValidators', 'templateOptionsValidators'],
    component: inputComponent
  },
  {
    name: 'textarea',
    controller: ['inputTypeValidators', 'templateOptionsValidators'],
    component: textareaComponent
  },
  {
    name: 'radio',
    wrapper: ['label', 'descriptionOrError'],
    controller: ['templateOptionsValidators'],
    component: radioComponent
  },
  {
    name: 'checkbox',
    wrapper: ['descriptionOrError'],
    controller: ['templateOptionsValidators'],
    component: checkboxComponent
  },
  {
    name: 'select',
    controller: ['templateOptionsValidators'],
    component: selectComponent
  }
]);


WrappersConfig.setWrapper([
  { name: 'label', component: labelWrapper },
  { name: 'descriptionOrError', component: descOrErrorWrapper }
]);

ControllersConfig.addType([
  {
    name: 'templateOptionsValidators',
    controller: {
      componentWillMount() {
        // for initial component adding validation
        const to = this.props.config.templateOptions || {};
        const fieldValidator = this.state.fieldValidator;
        if (fieldValidator) {
          if (to.required) {
            fieldValidator.subscribeToValidation('required', to.required);
          } else {
            fieldValidator.unsubscribeToValidation('required');
          }
          if (to.pattern) {
            fieldValidator.subscribeToValidation('pattern', to.pattern);
          } else {
            fieldValidator.unsubscribeToValidation('pattern');
          }
          if (to.minlength) {
            fieldValidator.subscribeToValidation('minlength', to.minlength);
          } else {
            fieldValidator.unsubscribeToValidation('minlength');
          }
          if (to.maxlength) {
            fieldValidator.subscribeToValidation('maxlength', to.maxlength);
          } else {
            fieldValidator.unsubscribeToValidation('maxlength');
          }
        }
      },
      componentWillUpdate(nextProps) {
        const to = nextProps.config.templateOptions || {};
        const fieldValidator = this.state.fieldValidator;
        if (fieldValidator) {
          if (to.required) {
            fieldValidator.subscribeToValidation('required', to.required);
          } else {
            fieldValidator.unsubscribeToValidation('required');
          }
          if (to.pattern) {
            fieldValidator.subscribeToValidation('pattern', to.pattern);
          } else {
            fieldValidator.unsubscribeToValidation('pattern');
          }
          if (to.minlength) {
            fieldValidator.subscribeToValidation('minlength', to.minlength);
          } else {
            fieldValidator.unsubscribeToValidation('minlength');
          }
          if (to.maxlength) {
            fieldValidator.subscribeToValidation('maxlength', to.maxlength);
          } else {
            fieldValidator.unsubscribeToValidation('maxlength');
          }
        }
      }
    }
  },
  {
    name: 'inputTypeValidators',
    controller: {
      componentWillMount() {
        // for initial component adding validation
        const to = this.props.config.templateOptions || {};
        const fieldValidator = this.state.fieldValidator;
        if (fieldValidator) {
          fieldValidator.unsubscribeToValidation(['email', 'number', 'url']);
          switch (to.type) {
            case 'number':
            case 'email':
            case 'url':
              fieldValidator.subscribeToValidation(to.type);
              break;
            default:
              break;
          }
        }
      },
      componentWillUpdate(nextProps) {
        const to = nextProps.config.templateOptions || {};
        const fieldValidator = this.state.fieldValidator;
        if (fieldValidator) {
          fieldValidator.unsubscribeToValidation(['email', 'number', 'url']);
          switch (to.type) {
            case 'number':
            case 'email':
            case 'url':
              fieldValidator.subscribeToValidation(to.type);
              break;
            default:
              break;
          }
        }
      }
    }
  }
]);

ValidationsConfig.addType({
  required: {
    expression({ viewValue }) {
      let isValid;
      if (_.isString(viewValue)) {
        isValid = _.trim(viewValue).length !== 0;
      } else if (_.isArray(viewValue) || _.isPlainObject(viewValue)) {
        isValid = !_.isEmpty(viewValue);
      } else {
        isValid = viewValue !== null && viewValue !== undefined;
      }
      return isValid;
    }
  },
  url: {
    expression({ viewValue }) {
      const regex = /^([a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?)?$/i;
      return viewValue === undefined || viewValue === null || regex.test(viewValue);
    }
  },
  email: {
    expression({ viewValue }) {
      // eslint-disable-next-line
      const regex = /^((?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*)?$/;
      return viewValue === undefined || viewValue === null || regex.test(viewValue);
    }
  },
  number: {
    expression({ viewValue }) {
      // eslint-disable-next-line
      const regex = /^(\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*)?$/;
      return viewValue === undefined || viewValue === null || regex.test(viewValue);
    }
  },
  pattern: {
    expression({ viewValue, param }) {
      const stringifiedViewValue = _.isString(viewValue) ? viewValue : '';
      return new RegExp(param).test(stringifiedViewValue);
    }
  },
  minlength: {
    expression({ viewValue, param }) {
      return viewValue && viewValue.length >= param;
    }
  },
  maxlength: {
    expression({ viewValue, param }) {
      return (!viewValue && param >= 0) || (viewValue && !(viewValue.length > param));
    }
  }
});
<file_sep>import React from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Text,
  StyleSheet
} from 'react-native';


const FormlyLabel = (props) => {
  const to = props.config.templateOptions || {};
  const fieldValidationResult = props.fieldValidation || {};
  return (
    <View style={[{ marginVertical: 1 }, defaultComponentStyle.Container]}>
      {to.label ? <Text style={[defaultComponentStyle.Label, { color: fieldValidationResult.isValid ? undefined : 'rgba(213, 0, 0, 1)' }]}>{to.label + (to.required ? ' *' : '')}</Text>
        : null}
      {props.children}
    </View>
  );
};

FormlyLabel.propTypes = {
  config: PropTypes.shape({
    templateOptions: PropTypes.shape({
      label: PropTypes.string,
      required: PropTypes.bool
    })
  }).isRequired,
  fieldValidation: PropTypes.shape({
    messages: PropTypes.object
  }),
  children: PropTypes.element
};

const defaultComponentStyle = StyleSheet.create({
  Label: {
    color: 'rgba(0, 0, 0, .38)',
    fontSize: 12,
    paddingTop: 32
  },
  Container: {
    flex: 1
  }
});

module.exports = FormlyLabel;
<file_sep>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Ripple from 'react-native-material-ripple';
import {
  Text,
  View,
  Easing,
  Animated
} from 'react-native';


class RadioButton extends Component {
  state = {
    innerScaleValue: new Animated.Value(this.props.isSelected ? 1 : 0)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.isSelected !== nextProps.isSelected) {
      this.selectionChanged(nextProps.isSelected);
    }
  }

  selectionChanged = (isSelected) => {
    if (isSelected) {
      Animated.timing(this.state.innerScaleValue, {
        toValue: 1,
        duration: 400,
        easing: Easing.bounce
      }).start();
    } else {
      Animated.timing(this.state.innerScaleValue, {
        toValue: 0,
        duration: 150,
        easing: Easing.bounce
      }).start();
    }
  }

  _getRadioButtonColor = () => {
    let color;
    if (this.props.disabled) {
      color = this.props.disabledColor;
    } else if (this.props.isSelected) {
      color = this.props.tintColor;
    } else {
      color = this.props.baseColor;
    }
    return color;
  }

  render() {
    const { scale, innerToOuterRatio } = this.props;

    const color = this._getRadioButtonColor();
    const outerSize = 24 * scale;
    const outerBorderWidth = 3 * scale;
    const rippleSize = outerSize * 2;
    const maxInnerSize = (outerSize - (outerBorderWidth * 2)) * innerToOuterRatio;
    const currentInnerSize = this.state.innerScaleValue.interpolate({
      inputRange: [0, 1],
      outputRange: [0, maxInnerSize]
    });
    // give the label the same padding arround the radio
    const labelPaddingHorizontal = this.props.labelHorizontal ? 0 : outerSize / 2;

    return (
      <View style={{ flexDirection: this.props.labelHorizontal ? 'row' : 'column', alignItems: 'center', alignSelf: 'flex-start' }}>
        <Ripple
          rippleColor={color}
          rippleOpacity={1}
          rippleDuration={400}
          rippleSize={rippleSize}
          rippleContainerBorderRadius={rippleSize / 2}
          rippleCentered
          style={{ height: rippleSize, width: rippleSize }}
          onPress={() => {
            if (this.props.onButtonPress && !this.props.disabled) {
              this.props.onButtonPress(this.props.id);
            }
          }
          }
        >
          <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }} >
            <View
              style={{
                width: outerSize,
                height: outerSize,
                borderRadius: outerSize / 2,
                borderColor: color,
                borderWidth: outerBorderWidth,
                alignItems: 'center',
                justifyContent: 'center'
              }}
            >
              <Animated.View style={
                {
                  width: currentInnerSize,
                  height: currentInnerSize,
                  borderRadius: maxInnerSize / 2,
                  backgroundColor: color
                }

              }
              />
            </View>
          </View>
        </Ripple>
        <View style={{ paddingHorizontal: labelPaddingHorizontal }}>
          <Text style={[
            this.props.labelStyle, {
              color: this.props.disabled ? this.props.disabledColor : this.props.labelColor }
          ]}
          >
            {this.props.label}
          </Text>
        </View>
      </View>
    );
  }
}

RadioButton.defaultProps = {
  onButtonPress: () => { },
  isSelected: false,
  tintColor: 'rgb(0, 145, 234)',
  disabledColor: 'rgba(0,0,0,0.26)',
  baseColor: 'rgba(0,0,0,0.54)',
  scale: 1,
  innerToOuterRatio: 0.65,
  labelHorizontal: true,
  labelColor: 'rgba(0, 0, 0, .87)'
};

RadioButton.propTypes = {
  id: PropTypes.any,
  onButtonPress: PropTypes.func,
  isSelected: PropTypes.bool,
  label: PropTypes.string,
  tintColor: PropTypes.string,
  disabledColor: PropTypes.string,
  baseColor: PropTypes.string,
  scale: PropTypes.number,
  disabled: PropTypes.bool,
  innerToOuterRatio: PropTypes.number,
  labelHorizontal: PropTypes.bool,
  labelColor: PropTypes.string,
  labelStyle: PropTypes.object
};

module.exports = RadioButton;
<file_sep>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  View,
  Text,
  StyleSheet
} from 'react-native';

class FormlyDescriptionOrError extends Component {
  _getText = () => {
    const to = this.props.config.templateOptions || {};
    const fieldValidationResult = this.props.fieldValidation || {};
    const messages = fieldValidationResult.messages;

    if (messages && Object.keys(messages).length !== 0) {
      return (
        <Text style={defaultComponentStyle.VaildationErrorText}>
          {Object.values(messages)[0]}
        </Text>
      );
    } else if (to.description) {
      return <Text style={defaultComponentStyle.DescriptionText}>{to.description}</Text>;
    }
    return null;
  }
  render() {
    return (
      <View style={defaultComponentStyle.Container}>
        {this.props.children}
        {this._getText()}
      </View>
    );
  }
}

FormlyDescriptionOrError.propTypes = {
  config: PropTypes.shape({
    key: PropTypes.string.isRequired,
    templateOptions: PropTypes.shape({
      description: PropTypes.string
    })
  }).isRequired,
  fieldValidation: PropTypes.shape({
    messages: PropTypes.object
  }),
  children: PropTypes.element
};

const defaultComponentStyle = StyleSheet.create({
  VaildationErrorText: {
    color: 'rgba(213, 0, 0, 1)',
    fontSize: 12
  },
  DescriptionText: {
    color: 'rgba(0, 0, 0, .38)',
    fontSize: 12
  },
  Container: {
    flex: 1
  }
});

module.exports = FormlyDescriptionOrError;
<file_sep>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {
  View
} from 'react-native';
import RadioButton from './radioButton';

class RadioGroup extends Component {
  state = {}

  componentWillMount() {
    if (!_.isEqual(this.state.selectedKey, this.props.selectedItem)) {
      if (this.props.onSelectionChange) this.props.onSelectionChange(this.props.selectedItem);
      this.setState({ selectedKey: this.props.selectedItem });
    }
  }
  componentWillReceiveProps(nextProps) {
    if (!_.isEqual(this.state.selectedKey, nextProps.selectedItem)) {
      this.setState({ selectedKey: nextProps.selectedItem });
    }
  }
  componentWillUpdate(nextProps, nextState) {
    if (nextProps.onSelectionChange &&
      !_.isEqual(nextState.selectedKey, this.state.selectedKey)) {
      nextProps.onSelectionChange(nextState.selectedKey);
    }
  }
  _renderValidChildren = () => {
    const buttonOnPress = this._onButtonPress;
    const selectedKey = this.state.selectedKey;
    const that = this;
    const children = React.Children.toArray(this.props.children);
    const uniqueChildren = _.uniqWith(children, (childOne, childTwo) =>
      _.isEqual(childOne.props.id, childTwo.props.id));

    if (children.length > uniqueChildren.length) { console.warn('RadioGroup contains RadioButtons with duplicate ids'); }
    return _.map(uniqueChildren, (child) => {
      if (child.type === RadioButton) {
        return React.cloneElement(child, {
          isSelected: _.isEqual(selectedKey, child.props.id),
          onButtonPress: buttonOnPress,
          tintColor: that.props.tintColor || child.props.tintColor,
          disabledColor: that.props.disabledColor || child.props.disabledColor,
          baseColor: that.props.baseColor || child.props.baseColor,
          scale: that.props.scale || child.props.scale,
          disabled: that.props.disabled || child.props.disabled,
          innerToOuterRatio: child.props.innerToOuterRatio || that.props.innerToOuterRatio,
          labelHorizontal: that.props.labelHorizontal !== undefined ?
            that.props.labelHorizontal : child.props.labelHorizontal,
          labelColor: that.props.labelColor || child.props.labelColor,
          labelStyle: that.props.labelStyle || child.props.labelStyle
        });
      }

      console.warn(`RadioGroup childrens must be of type ${RadioButton.displayName}`);
      return null;
    });
  }

  _onButtonPress = (id) => {
    this.setState({ selectedKey: id });
  }

  render() {
    return (
      <View {...this.props} style={[{ flexDirection: this.props.formHorizontal ? 'row' : 'column', flexWrap: 'wrap' }]}>
        {this._renderValidChildren()}
      </View>
    );
  }
}

RadioGroup.defaultProps = {
  formHorizontal: false
};

RadioGroup.propTypes = {
  onSelectionChange: PropTypes.func.isRequired,
  selectedItem: PropTypes.any,
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.element),
    PropTypes.element
  ]).isRequired,
  formHorizontal: PropTypes.bool
};


module.exports = RadioGroup;
<file_sep># React-native-formly: Material Template

## Getting Started
### Prerequisites
This is a material template for [react-native-formly](https://github.com/Assem-Hafez/react-native-formly). So make sure that it is installed first.
### Installing
```
npm install react-native-formly-templates-md --save
```
## Components
### input

`templateOptions` Configuration:

|  name      | description                            | type   						| default
|------------| ---------------------------------------|-------------------------------------------------|--------------
|label	     | Input Label                            | string 						| -
|placeholder | Input placeholder                      | string 						| -
|description | Input description                      | string 						| -
|required    | Input should have a value              | bool   						| -
|disabled    | Input is disabled                      | bool   						| -
|minlength   | Minimum character length to be entered | number 						| -
|maxlength   | Maximum character length to be entered | number 						| -
|type	     | Input text type 		              | string of (`text`, `number`, `url`, `email`, `password`)| 'text'
|pattern     | The property that contains radio value | string regex					|-


Example:
```js
{
	key: 'myMaterialInput',
	type: 'input',
	templateOptions: {
		label: "Number Input",
		placeholder:"Write a number here",
		required:true,
	        minlength: 3,
	        type:"number"
	}
}
```
	
### textarea

`templateOptions` Configuration:

|  name      | description                            | type   						| default
|------------| ---------------------------------------|-------------------------------------------------|--------------
|label	     | Input Label                            | string 						| -
|placeholder | Input placeholder                      | string 						| -
|description | Input description                      | string 						| -
|required    | Input should have a value              | bool   						| -
|disabled    | Input is disabled                      | bool   						| -
|minlength   | Minimum character length to be entered | number 						| -
|maxlength   | Maximum character length to be entered | number 						| -
|type	     | Input text type 		              | string of (`text`, `number`, `url`, `email`)	| 'text'
|pattern     | The property that contains radio value | string regex					|-


Example:
```js	
{
	key: 'myMaterialTextarea',
	type: 'textarea',
	templateOptions: {
		label: "Textarea",
		placeholder:"Say something here",
		required:true,
		maxlength: 200
	}
}
	
```

### select

	
|  name		| description							| type		| default	|
|---------------|---------------------------------------------------------------|---------------|---------------|
|label		| Radio group Label| string					| -		|		|
|description	| Radio group description| string 				| -		|		|
|required	| Radio group should have a value				| bool		| -		|
|disabled	| Radio group is disabled| bool					| -		|		|
|minlength   	| Minimum character length to be entered 			| number 	| -		|
|maxlength   	| Maximum character length to be entered 			| number 	| -		|
|options	| Array of select options to be rendered			| array		| []		|
|labelProp	| The property that contains radio label if option is object	| string	| 'name'	|
|valueProp	| The property that contains radio value if option is object	| string	| 'value'	|
|pattern     	| The property that contains radio value 			| string regex	| -


Example:
```js
{
	key: 'myMaterialSelect',
	type: 'select',
	templateOptions: {
		label: 'Select',
		required: true,
		description : 'Select your type',
		options:[
			'string',
			2,
			{name:'label', value:'value'}
			]
	}
}
```

### radio

	
|  name		| description							| type		| default	|
|---------------|---------------------------------------------------------------|---------------|---------------|
|label		| Radio group Label| string					| -		|		|
|description	| Radio group description| string 				| -		|		|
|required	| Radio group should have a value				| bool		| -		|
|disabled	| Radio group is disabled| bool					| -		|		|
|minlength   	| Minimum character length to be entered 			| number 	| -		|
|maxlength   	| Maximum character length to be entered 			| number 	| -		|
|options	| Array of radio buttons to be rendered				| array		| []		|
|labelProp	| The property that contains radio label if option is object	| string	| 'name'	|
|valueProp	| The property that contains radio value if option is object	| string	| 'value'	|
|pattern     	| The property that contains radio value 			| string regex	| -


Example:
```js
{
	key: 'myMaterialRadio',
	type: 'radio',
	templateOptions: {
		label: 'Radio Input',
		required:true,
		description : 'Select your type',
		options:[
			"string",
			2,
			{name:'array', value:[1,2,3]},
			{name:'date', value: new Date()},
			{name:'object', value:{prop1:'value1'}}
			]
	}
}
```

## Configuring Validation
Available validations are:

* required
* url
* email
* number
* pattern
* minlength
* maxlength

### Customize validation message
You can customize any of the previous validations messages by adding a message to formly's `MessagesConfig`.
```js
import { Formly, FormlyConfig } from 'react-native-formly';
let {MessagesConfig } = FormlyConfig;

MessagesConfig.addType([
  {
    name: 'required',
    message: "'This field is Required!!!'"
  },
  {
    name: 'number',
    message: "viewValue+' is not a number'"
  }
]);
```
### Overriding validations
If you ever wanted to change a validation you can override it using formly's `ValidationsConfig`. 
```js
import { Formly, FormlyConfig } from 'react-native-formly';
let {ValidationsConfig} = FormlyConfig;

ValidationsConfig.addType({
 minlength: {
    expression: function ({ viewValue, modelValue, param }) {
      return viewValue && viewValue.length >= param;
    },
    message:"'Minimum length is '+ param"
  }
});
```
Make sure that you add this validations after requiring the template

## Roadmap

* [x] input
* [x] radio
* [x] textarea
* [ ] checkbox
* [ ] multicheckbox
* [ ] customizable themes
* [x] select
<file_sep>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import {
  View
} from 'react-native';
import RadioButton from './../../controls/radioButton';
import RadioGroup from './../../controls/radioGroup';
import * as helpers from './../../helpers';

class FormlyRadio extends Component {
  componentWillReceiveProps(nextProps) {
    const key = nextProps.config.key;
    const to = nextProps.config.templateOptions || {};
    const model = nextProps.model[key];
    if (model !== undefined &&
      !helpers.valueExistsInOptions(to.labelProp, to.valueProp, to.options, model)) {
      // if the value doesn't exists in options update the model with undefined
      this.props.onChange(undefined);
    }
  }
  _renderItemsFromTemplateOptions = (to = {}) => {
    const items = [];
    // check if the options is of type array
    if (Array.isArray(to.options)) {
      to.options.forEach((option, i) => {
        const currentRadioIndex = i;
        const {
          label,
          value
        } = helpers.extractLabelAndValueFromOption(to.labelProp, to.valueProp, option);
        items.push(
          <RadioButton key={currentRadioIndex + _.toString(value)} id={value} label={label} />
        );
      }, this);
    }

    return items;
  }
  render() {
    const key = this.props.config.key;
    const to = this.props.config.templateOptions || {};
    const viewValue = this.props.viewValues[key];


    return (
      <View style={defaultComponentStyle.Container}>
        <View style={defaultComponentStyle.RadioContainer}>
          <RadioGroup
            selectedItem={viewValue}
            formHorizontal={to.inline}
            labelHorizontal={!to.labelVertical}
            disabled={to.disabled}
            onSelectionChange={this.props.onChange}
          >
            {this._renderItemsFromTemplateOptions(to)}
          </RadioGroup>
        </View>
      </View>
    );
  }
}

FormlyRadio.propTypes = {
  config: PropTypes.shape({
    key: PropTypes.string.isRequired,
    templateOptions: PropTypes.shape({
      required: PropTypes.bool,
      pattern: PropTypes.string,
      minlength: PropTypes.number,
      maxlength: PropTypes.number,
      disabled: PropTypes.bool,
      description: PropTypes.string,
      label: PropTypes.string,
      inline: PropTypes.bool,
      labelProp: PropTypes.string,
      valueProp: PropTypes.string,
      options: PropTypes.arrayOf(PropTypes.any).isRequired
    })
  }).isRequired,
  model: PropTypes.any,
  viewValues: PropTypes.any,
  onChange: PropTypes.func
};

const defaultComponentStyle = {
  RadioContainer: {
    margin: 1
  }
};

module.exports = FormlyRadio;
<file_sep>import _ from 'lodash';

export function extractLabelAndValueFromOption(labelProp = 'name', valueProp = 'value', option) {
  let extractedLabelAndValue = { label: '', value: undefined };
  // helper function that makes sure that label is string 
  function perpareLabelAndValue(l, v) {
    const label = _.isString(l) ? l : JSON.stringify(l);
    const value = v;
    return { label, value };
  }
  /* the options could be in one of the following formats:
      -literal value
      -object with a label and value 
      (the default value of the label and value is "name" and "value" respectively)
     */
  // if the option is a literal value  make it the label and the value
  if (_.isString(option) || _.isNumber(option)) {
    extractedLabelAndValue = perpareLabelAndValue(option, option);
  } else if (_.isPlainObject(option)) {
    // if the option is a object take the value of the labelprop 
    // to the label and the value of valueprop to value
    extractedLabelAndValue = perpareLabelAndValue(option[labelProp], option[valueProp]);
  } else {
    extractedLabelAndValue = perpareLabelAndValue(option, option);
  }

  return extractedLabelAndValue;
}

export function valueExistsInOptions(labelProp, valueProp, options, model) {
  let valueExistInOptions = false;
  // check if the value from recieved exists inside the available options 
  if (Array.isArray(options)) {
    options.forEach((option) => {
      const { value } = extractLabelAndValueFromOption(labelProp, valueProp, option);
      if (_.isEqual(value, model)) { valueExistInOptions = true; }
    }, this);
  }

  return valueExistInOptions;
}
