Skip to content

Commit

Permalink
feat(custom): adding new engine to custom mask
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben-hur Santos Ott committed May 22, 2017
1 parent 09512c2 commit 249c042
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 56 deletions.
100 changes: 75 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ export default class MyComponent extends Component {
```

### Props
**type**

#### type

*credit-card*: use the mask 9999 9999 9999 9999. It accepts options (see later in this doc). <br />
*cpf*: use the mask `999.999.999-99` and `numeric` keyboard. <br />
Expand All @@ -64,7 +65,7 @@ export default class MyComponent extends Component {
*custom*: use your custom mask (see the options props later in this doc). <br />


**onChangeText**
#### onChangeText

Invoked after new value applied to mask.
```jsx
Expand All @@ -81,7 +82,7 @@ onChangeText(text) {
```


**checkText**
#### checkText

Allow you to check and prevent value to be inputed.

Expand All @@ -100,7 +101,8 @@ checkText(previous, next) {
checkText={this.checkText.bind(this)} />
```

**customTextInput** <br />
#### customTextInput

You can use this prop if you want custom text input instead native TextInput component:

```jsx
Expand All @@ -118,14 +120,16 @@ const Textfield = MKTextField.textfield()
placeholder="Enter text to see events" />
```

**TextInput Props** <br />
#### TextInput Props

You can use the native props of TextInput, with this in mind:

* onChangeText is intercepted by component.
* value is intercepted by component.
* if you pass keyboardType, it will override the keyboardType of masked component.

**TextInput Methods** <br />
#### TextInput Methods

If you want to use the methods of the native TextInput, use the `getElement()` method:

```jsx
Expand Down Expand Up @@ -154,11 +158,12 @@ export default class App extends React.Component {
}
```

**Options** <br />
#### Options

Some types accept options, use it like this: `<TextInputMask type={'money'} options={{ unit: 'US$' }} />`


For `type={'money'}` <br />
**For `type={'money'}`** <br />
* *options={...}*
* `precision` (Number, default 2): the decimal places.
* `separator` (String, default ','): the decimal separator.
Expand All @@ -167,12 +172,12 @@ For `type={'money'}` <br />
* `suffixUnit` (String, default ''): the suffix text.
* `zeroCents` (Boolean, default false): if must show cents.

For `type={'cel-phone'}` <br />
**For `type={'cel-phone'}`** <br />
* *options={...}*
* `withDDD` (Boolean, default true): if the ddd will be include in the mask.
* `dddMask` (String, default '(99) '): the default mask applied if `withDDD` is true.

For `type={'datetime'}` <br />
**For `type={'datetime'}`** <br />
* *options={...}*
* `format` (String, default DD/MM/YYYY HH:mm:ss): moment date format. It accepts the following:
* DD/MM/YYYY HH:mm:ss
Expand All @@ -184,22 +189,64 @@ For `type={'datetime'}` <br />
* HH
* *You can use all of dates with `-` instead of `/` if you want*

For `type={'custom'}` <br />
**For `type={'custom'}`** <br />
* *options={...}*
* `mask` (String, default ''): your mask template.
* `9`: accept digit.
* `A`: accept alpha.
* `S`: accept alphanumeric.
* `validator` (Function, default returns true): the function use to validate value. Must return true if valid or false if invalid.
* The function receives 2 parameters:
* `value`: current value.
* `settings`: current settings
* `getRawValue` (Function, default returns the current value): the function that's invoked when `getRawValue` method from component is called.
* The function receives 2 parameters:
* `value`: current value.
* `settings`: current settings

For `type={'credit-card'}` <br />

```jsx
{
/**
* mask: (String | required | default '')
* the mask pattern
* 9 - accept digit.
* A - accept alpha.
* S - accept alphanumeric.
* * - accept all.
*/
mask: '999#AAA',

/**
* validator: (Function | optional | defaults returns true)
* use this funcion to inform if the inputed value is a valid value (for invalid phone numbers, for example). The isValid method use this validator.
*/
validator: function(value, settings) {
return true
},
/**
* getRawValue: (Function | optional | defaults return current masked value)
* use this function to parse and return values to use what you want.
* for example, if you want to create a phone number mask (999) 999-99-99 but want to get only
* the numbers for value, use this method for this parse step.
*/
getRawValue: function(value, settings) {
return 'my converted value';
},
/**
* translation: (Object | optional | defaults 9, A, S, *)
* the dictionary that translate mask and value.
* you can change defaults by simple override the key (9, A, S, *) or create some new.
*/
translation: {
// this is a custom translation. The others (9, A, S, *) still works.
// this translation will be merged and turns into 9, A, S, *, #.
'#': function(val) {
if (val === ' ') {
return val;
}

// if returns null, undefined or '' (empty string), the value will be ignored.
return null;
},
// in this case, we will override build-in * translation (allow all characters)
// and set this to allow only blank spaces and some special characters.
'*': function(val) {
return [' ', '#', ',', '.', '!'].indexOf(val) >= 0 ? val : null;
}
}
}

```

**For `type={'credit-card'}`** <br />
* *options={...}*
* `obfuscated` (Boolean, default false): if the mask must be `9999 **** **** 9999`

Expand Down Expand Up @@ -302,6 +349,9 @@ var money = MaskService.toMask('money', '123', {


# Changelog
## 1.5.0
* Adding new and powerfull `custom` engine mask \m/.

## 1.4.0
* Adding `customTextInput` to allow other inputs instead native TextInput. (thank to [Hellon Canella](https://github.com/helloncanella))

Expand Down
53 changes: 52 additions & 1 deletion __tests__/custom.mask.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,55 @@ test('123 with mask 999 results 123 and raw value 123(type Number)', () => {

expect(received).toBe(expected);
expect(receivedRawValue).toBe(expectedRawValue);
});
});

test('mask with custom translation and match', () => {
var mask = new CustomMask();
var options = {
mask: '999&AAA',
translation: {
'&': function(val) {
return ['#', '.', ':'].indexOf(val) >= 0 ? val : null;
}
}
}

var expected = '123#ABC';
var received = mask.getValue('123#ABC', options);

expect(received).toBe(expected);
});

test('mask with custom translation and not match', () => {
var mask = new CustomMask();
var options = {
mask: '999&AAA',
translation: {
'&': function(val) {
return ['#', '.', ':'].indexOf(val) >= 0 ? val : null;
}
}
}

var expected = '123';
var received = mask.getValue('123|ABC', options);

expect(received).toBe(expected);
});

test('mask with custom translation and optionals and matching', () => {
var mask = new CustomMask();
var options = {
mask: '999***AAA&',
translation: {
'&': function(val) {
return ['#', '.', ':'].indexOf(val) >= 0 ? val : null;
}
}
}

var expected = '123|% ABC.';
var received = mask.getValue('123|% ABC.', options);

expect(received).toBe(expected);
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ test('1 results 1 and raw value 1', () => {

expect(received).toBe(expected);
expect(receivedRawValue).toBe(expectedRawValue);
});
});
111 changes: 83 additions & 28 deletions lib/masks/custom.mask.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,86 @@
import BaseMask from './_base.mask';

const DEFAULT_TRANSLATION = {
'9': function (val) {
return val.replace(/[^0-9]+/g, '');
},
'A': function (val) {
return val.replace(/[^a-zA-Z]+/g, '');
},
'S': function (val) {
return val.replace(/[^a-zA-Z0-9]+/g, '');
},
'*': function (val) {
return val
}
}

var invalidValues = [null, undefined, ''];

export default class CustomMask extends BaseMask {
static getType() {
return 'custom';
}

getKeyboardType() {
return "default";
}

getValue(value, settings) {
return this.getVMasker().toPattern(value, settings.mask);
}

getRawValue(maskedValue, settings) {
if(!!settings && settings.getRawValue) {
return settings.getRawValue(maskedValue, settings);
}

return maskedValue;
}

validate(value, settings) {
if(!!settings && settings.validator) {
return settings.validator(value, settings);
}

return true;
}
}
static getType() {
return 'custom';
}

getKeyboardType() {
return "default";
}

getValue(value, settings) {
let { mask } = settings;
let translation = this.mergeSettings(DEFAULT_TRANSLATION, settings.translation);

var result = '';

const maskSize = mask.length;
const valueSize = value.length;

var maskResolved = 0;
var valueResolved = 0;

while (maskResolved < maskSize && valueResolved < valueSize) {
const valueChar = value[valueResolved];
const maskChar = mask[maskResolved];

if (valueChar === maskChar) {
result += valueChar;
maskResolved++;
valueResolved++;
continue;
}

const handler = translation[maskChar];

if (!handler) {
result += maskChar;
maskResolved++;
continue;
}

var masked = handler(valueChar);
if (invalidValues.indexOf(masked) < 0) {
result += masked
maskResolved++
}
valueResolved++
}

return result;
}

getRawValue(maskedValue, settings) {
if (!!settings && settings.getRawValue) {
return settings.getRawValue(maskedValue, settings);
}

return maskedValue;
}

validate(value, settings) {
if (!!settings && settings.validator) {
return settings.validator(value, settings);
}

return true;
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-masked-text",
"version": "1.4.0",
"version": "1.5.0",
"description": "Text and TextInput with mask for React Native applications",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit 249c042

Please sign in to comment.