cerebral-forms
Advanced tools
Comparing version 1.0.0-alpha.1-alpha.ed1e4568 to 1.0.0-b-alpha.3b97c05c
@@ -9,6 +9,9 @@ 'use strict'; | ||
_templateObject2 = _taggedTemplateLiteral(['field'], ['field']), | ||
_templateObject3 = _taggedTemplateLiteral(['value'], ['value']); | ||
_templateObject3 = _taggedTemplateLiteral(['value'], ['value']), | ||
_templateObject4 = _taggedTemplateLiteral(['', ''], ['', '']); | ||
var _operators = require('cerebral/operators'); | ||
var _tags = require('cerebral/tags'); | ||
var _validateField = require('../factories/validateField'); | ||
@@ -22,3 +25,3 @@ | ||
exports.default = [(0, _operators.set)((0, _operators.state)(_templateObject, (0, _operators.input)(_templateObject2)), (0, _operators.input)(_templateObject3)), (0, _validateField2.default)()]; | ||
exports.default = [(0, _operators.set)((0, _tags.state)(_templateObject, (0, _tags.input)(_templateObject2)), (0, _tags.input)(_templateObject3)), (0, _validateField2.default)((0, _tags.state)(_templateObject4, (0, _tags.input)(_templateObject2)))]; | ||
//# sourceMappingURL=changeField.js.map |
@@ -16,11 +16,28 @@ 'use strict'; | ||
var state = _ref.state, | ||
path = _ref.path; | ||
path = _ref.path, | ||
resolveArg = _ref.resolveArg; | ||
var form = state.get(formPath); | ||
if (typeof formPath === 'string') { | ||
console.warn('DEPRECATION: Cerebral Forms now requires STATE TAG to be passed into isValidForm factory'); | ||
if ((0, _isValidForm2.default)(form)) { | ||
return path.true(); | ||
var form = state.get(formPath); | ||
if ((0, _isValidForm2.default)(form)) { | ||
return path.true(); | ||
} | ||
return path.false(); | ||
} else { | ||
if (!resolveArg.isTag(formPath, 'state')) { | ||
throw new Error('Cerebral Forms - isValidForm factory requires a STATE TAG'); | ||
} | ||
var _form = resolveArg.value(formPath); | ||
if ((0, _isValidForm2.default)(_form)) { | ||
return path.true(); | ||
} | ||
return path.false(); | ||
} | ||
return path.false(); | ||
} | ||
@@ -27,0 +44,0 @@ |
@@ -16,13 +16,15 @@ 'use strict'; | ||
return Object.keys(form).reduce(function (newForm, key) { | ||
if (Array.isArray(form[key])) { | ||
newForm[key] = resetArray(form[key]); | ||
} else if ('value' in form[key]) { | ||
var newField = Object.keys(form[key]).reduce(function (newField, fKey) { | ||
newField[fKey] = form[key][fKey]; | ||
return newField; | ||
}, {}); | ||
newField.value = newField.defaultValue; | ||
newForm[key] = (0, _configureField2.default)(form, newField); | ||
} else { | ||
newForm[key] = resetObject(form[key]); | ||
if (form[key] === Object(form[key])) { | ||
if (Array.isArray(form[key])) { | ||
newForm[key] = resetArray(form[key]); | ||
} else if ('value' in form[key]) { | ||
var newField = Object.keys(form[key]).reduce(function (newField, fKey) { | ||
newField[fKey] = form[key][fKey]; | ||
return newField; | ||
}, {}); | ||
newField.value = newField.defaultValue; | ||
newForm[key] = (0, _configureField2.default)(form, newField); | ||
} else { | ||
newForm[key] = resetObject(form[key]); | ||
} | ||
} | ||
@@ -43,7 +45,20 @@ | ||
function resetForm(_ref) { | ||
var state = _ref.state; | ||
var state = _ref.state, | ||
resolveArg = _ref.resolveArg; | ||
var form = state.get(formPath); | ||
if (typeof formPath === 'string') { | ||
console.warn('DEPRECATION: Cerebral Forms now requires STATE TAG to be passed into resetForm factory'); | ||
state.set(formPath, resetObject(form)); | ||
var form = state.get(formPath); | ||
state.merge(formPath, resetObject(form)); | ||
} else { | ||
if (!resolveArg.isTag(formPath, 'state')) { | ||
throw new Error('Cerebral Forms - isValidForm factory requires a STATE TAG'); | ||
} | ||
var _form = resolveArg.value(formPath); | ||
state.merge(resolveArg.path(formPath), resetObject(_form)); | ||
} | ||
} | ||
@@ -50,0 +65,0 @@ |
@@ -8,38 +8,32 @@ 'use strict'; | ||
var _validate = require('../utils/validate'); | ||
var _runValidation = require('../utils/runValidation'); | ||
var _validate2 = _interopRequireDefault(_validate); | ||
var _runValidation2 = _interopRequireDefault(_runValidation); | ||
var _checkHasValue = require('../utils/checkHasValue'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _checkHasValue2 = _interopRequireDefault(_checkHasValue); | ||
function validateFieldFactory(fieldPath) { | ||
function validateField(_ref) { | ||
var state = _ref.state, | ||
resolveArg = _ref.resolveArg; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var path = void 0; | ||
function runValidation(fieldPath, field, form) { | ||
var hasValue = (0, _checkHasValue2.default)(form, field.value, field.isValueRules); | ||
var result = (0, _validate2.default)(form, field.value, field.validationRules); | ||
var isValid = result.isValid && (field.isRequired && hasValue || !field.isRequired); | ||
if (typeof fieldPath === 'string') { | ||
console.warn('DEPRECATION: Cerebral Forms now requires STATE TAG to be passed into validateField factory'); | ||
path = fieldPath; | ||
} else { | ||
if (!resolveArg.isTag(fieldPath, 'state')) { | ||
throw new Error('Cerebral Forms - validateField factory requires a STATE TAG'); | ||
} | ||
return { | ||
isValid: isValid, | ||
isPristine: false, | ||
hasValue: (0, _checkHasValue2.default)(form, field.value, field.isValueRules), | ||
errorMessage: result.isValid ? null : field.errorMessages[result.failedRuleIndex] | ||
}; | ||
} | ||
path = resolveArg.path(fieldPath); | ||
} | ||
function validateFieldFactory(path) { | ||
function validateField(_ref) { | ||
var input = _ref.input, | ||
state = _ref.state; | ||
var fieldPath = (path || input.field).split('.'); | ||
var formPath = fieldPath.slice().splice(0, fieldPath.length - 1); | ||
var field = state.get(fieldPath); | ||
var fieldPathAsArray = path.split('.'); | ||
var formPath = fieldPathAsArray.slice().splice(0, fieldPathAsArray.length - 1); | ||
var field = state.get(path); | ||
var form = state.get(formPath); | ||
var validationResult = runValidation(fieldPath, field, form); | ||
var validationResult = (0, _runValidation2.default)(field, form); | ||
state.merge(fieldPath, validationResult); | ||
var dependentFields = []; | ||
@@ -52,17 +46,16 @@ if (Array.isArray(field.dependsOn)) { | ||
var depententOfValidationResult = dependentFields.reduce(function (currentValidationResult, stringPath) { | ||
var dependentOfValidationResult = dependentFields.reduce(function (currentValidationResult, stringPath) { | ||
var dependentFieldPath = stringPath.split('.'); | ||
var dependentFormPath = dependentFieldPath.slice().splice(0, dependentFieldPath.length - 1); | ||
var field = state.get(dependentFieldPath); | ||
var form = state.get(dependentFormPath); | ||
if (!form) { | ||
var dependentField = state.get(dependentFieldPath); | ||
var dependentForm = state.get(dependentFormPath); | ||
if (!dependentForm || !dependentField) { | ||
throw new Error('The path ' + stringPath + ' used with "dependsOn" on field ' + fieldPath.join('.') + ' is not correct, please check it'); | ||
} | ||
var dependentValidationResult = runValidation(dependentFieldPath, field, form); | ||
var dependentValidationResult = (0, _runValidation2.default)(dependentField, dependentForm); | ||
state.merge(dependentFieldPath, dependentValidationResult); | ||
if (currentValidationResult.isValid && !dependentValidationResult.isValid) { | ||
return dependentValidationResult; | ||
return Object.assign(currentValidationResult, { isValid: false }); | ||
} | ||
@@ -73,3 +66,3 @@ | ||
state.merge(fieldPath, depententOfValidationResult); | ||
state.merge(path, dependentOfValidationResult); | ||
} | ||
@@ -76,0 +69,0 @@ |
@@ -8,28 +8,24 @@ 'use strict'; | ||
var _validate = require('../utils/validate'); | ||
var _runValidation = require('../utils/runValidation'); | ||
var _validate2 = _interopRequireDefault(_validate); | ||
var _runValidation2 = _interopRequireDefault(_runValidation); | ||
var _checkHasValue = require('../utils/checkHasValue'); | ||
var _checkHasValue2 = _interopRequireDefault(_checkHasValue); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function validateFormFactory(passedFormPath) { | ||
function validateFormFactory(formPath) { | ||
function validateForm(_ref) { | ||
var input = _ref.input, | ||
state = _ref.state; | ||
var state = _ref.state, | ||
input = _ref.input, | ||
resolveArg = _ref.resolveArg; | ||
var formPath = (passedFormPath || input.form).split('.'); | ||
var currentPathValue = state.get(formPath); | ||
function validateForm(path, form) { | ||
function validate(path, form) { | ||
Object.keys(form).forEach(function (key) { | ||
if (Array.isArray(form[key])) { | ||
validateArray(path.concat(key), form[key]); | ||
} else if ('value' in form[key]) { | ||
doValidation(path.concat(key), form, key); | ||
} else { | ||
validateForm(path.concat(key), form[key]); | ||
if (form[key] === Object(form[key])) { | ||
if (Array.isArray(form[key])) { | ||
validateArray(path.concat(key), form[key]); | ||
} else if ('value' in form[key]) { | ||
state.merge(path.concat(key), (0, _runValidation2.default)(form[key], form)); | ||
} else { | ||
validate(path.concat(key), form[key]); | ||
} | ||
} | ||
@@ -41,21 +37,15 @@ }); | ||
formArray.forEach(function (form, index) { | ||
validateForm(path.concat(index), form); | ||
validate(path.concat(index), form); | ||
}); | ||
} | ||
if (typeof formPath === 'string') { | ||
console.warn('DEPRECATION: Cerebral Forms now requires STATE TAG to be passed into validateForm factory'); | ||
validate(formPath.split('.'), state.get(formPath)); | ||
} else { | ||
if (!resolveArg.isTag(formPath, 'state')) { | ||
throw new Error('Cerebral Forms - validateField factory requires a STATE TAG'); | ||
} | ||
function doValidation(path, form, key) { | ||
var field = form[key]; | ||
var hasValue = (0, _checkHasValue2.default)(form, field.value, field.isValueRules); | ||
var result = (0, _validate2.default)(form, field.value, field.validations); | ||
var isValid = result.isValid && (field.isRequired && hasValue || !field.isRequired); | ||
state.merge(path, { | ||
isValid: isValid, | ||
hasValue: hasValue, | ||
errorMessage: isValid ? null : field.errorMessages[result.failedRuleIndex], | ||
isPristine: false | ||
}); | ||
validate(resolveArg.path(formPath).split('.'), resolveArg.value(formPath)); | ||
} | ||
validateForm(formPath, currentPathValue); | ||
} | ||
@@ -62,0 +52,0 @@ |
@@ -6,14 +6,13 @@ 'use strict'; | ||
}); | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
exports.default = formToJSON; | ||
function extractObject(object) { | ||
return Object.keys(object).reduce(function (newObject, key) { | ||
if (Array.isArray(object[key])) { | ||
newObject[key] = extractArray(object[key]); | ||
} else if (object[key] && 'value' in object[key]) { | ||
newObject[key] = object[key].value; | ||
} else if (object[key] && _typeof(object[key]) === 'object') { | ||
newObject[key] = extractObject(object[key]); | ||
if (object[key] && object[key] === Object(object[key])) { | ||
if (Array.isArray(object[key])) { | ||
newObject[key] = extractArray(object[key]); | ||
} else if ('value' in object[key]) { | ||
newObject[key] = object[key].value; | ||
} else { | ||
newObject[key] = extractObject(object[key]); | ||
} | ||
} | ||
@@ -20,0 +19,0 @@ |
@@ -22,3 +22,3 @@ 'use strict'; | ||
return allFields; | ||
} else if ('value' in object[key]) { | ||
} else if (object[key] === Object(object[key]) && 'value' in object[key]) { | ||
allFields[currentPath.join('.')] = object[key]; | ||
@@ -25,0 +25,0 @@ currentPath.pop(); |
@@ -90,2 +90,4 @@ 'use strict'; | ||
var _tags = require('cerebral/tags'); | ||
var _isValidForm = require('./helpers/isValidForm'); | ||
@@ -102,3 +104,3 @@ | ||
function isValidForm(form) { | ||
if (typeof form === 'string') { | ||
if (typeof form === 'string' || form instanceof _tags.Tag) { | ||
return (0, _isValidForm4.default)(form); | ||
@@ -105,0 +107,0 @@ } |
@@ -6,2 +6,3 @@ 'use strict'; | ||
}); | ||
/* eslint-disable no-useless-escape */ | ||
var rules = { | ||
@@ -73,3 +74,3 @@ isExisty: function isExisty(value) { | ||
minLength: function minLength(value, form, length) { | ||
return value.length >= length; | ||
return !rules.isExisty(value) || rules.isEmpty(value) || value.length >= length; | ||
} | ||
@@ -76,0 +77,0 @@ }; |
@@ -8,28 +8,12 @@ 'use strict'; | ||
var _rules = require('../rules'); | ||
var _validate = require('./validate'); | ||
var _rules2 = _interopRequireDefault(_rules); | ||
var _validate2 = _interopRequireDefault(_validate); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function parseKey(value) { | ||
if (typeof value === 'string') { | ||
var args = value.split(/:(.+)?/); | ||
return args[1] ? { key: args[0], params: JSON.parse(args[1]) } : { key: value }; | ||
} | ||
return { key: value }; | ||
} | ||
function checkHasValue(form, value, isValueRules) { | ||
return isValueRules.reduce(function (isValue, key) { | ||
if (!isValue) { | ||
return false; | ||
} | ||
var parsedKey = parseKey(key); | ||
return _rules2.default[parsedKey.key](value, form, parsedKey.params); | ||
}, true); | ||
var result = (0, _validate2.default)(form, value, isValueRules); | ||
return result.isValid; | ||
} | ||
//# sourceMappingURL=checkHasValue.js.map |
@@ -8,15 +8,11 @@ 'use strict'; | ||
var _checkHasValue = require('./checkHasValue'); | ||
var _runValidation = require('./runValidation'); | ||
var _checkHasValue2 = _interopRequireDefault(_checkHasValue); | ||
var _runValidation2 = _interopRequireDefault(_runValidation); | ||
var _validate = require('./validate'); | ||
var _validate2 = _interopRequireDefault(_validate); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function configureField(formData, field) { | ||
// If not an actual field, just a namespace | ||
if (!('value' in field)) { | ||
// If not an actual field but a global property or just a namespace | ||
if (field !== Object(field) || !('value' in field)) { | ||
return field; | ||
@@ -30,7 +26,5 @@ } | ||
var validationRules = field.validationRules || null; | ||
var errorMessages = field.errorMessages || []; | ||
var hasValue = (0, _checkHasValue2.default)(formData, value, isValueRules); | ||
var validationResult = (0, _validate2.default)(formData, value, validationRules); | ||
var validationMessages = field.validationMessages || []; | ||
var requiredMessage = field.requiredMessage || null; | ||
field.value = value; | ||
field.defaultValue = defaultValue; | ||
@@ -41,8 +35,12 @@ field.validationRules = validationRules; | ||
// has a value | ||
field.isValid = hasValue && validationResult.isValid || !isRequired && !hasValue; | ||
field.errorMessages = errorMessages; | ||
field.errorMessage = validationResult.isValid ? null : errorMessages[validationResult.failedRuleIndex]; | ||
field.validationMessages = validationMessages; | ||
field.requiredMessage = requiredMessage; | ||
field.isValueRules = isValueRules; | ||
field.isRequired = isRequired; | ||
field.hasValue = hasValue; | ||
var validationResult = (0, _runValidation2.default)(field, formData); | ||
field.isValid = validationResult.isValid; | ||
field.errorMessage = validationResult.errorMessage; | ||
field.hasValue = validationResult.hasValue; | ||
field.isPristine = true; | ||
@@ -49,0 +47,0 @@ |
@@ -53,3 +53,3 @@ 'use strict'; | ||
var rule = _rules2.default[key] || function () { | ||
console.warn('Rule ' + key + ' is not found'); | ||
throw new Error('Rule ' + key + ' is not found'); | ||
}; | ||
@@ -56,0 +56,0 @@ |
{ | ||
"name": "cerebral-forms", | ||
"version": "1.0.0-alpha.1-alpha.ed1e4568", | ||
"version": "1.0.0-b-alpha.3b97c05c", | ||
"description": "Signals, actions and state factories to create forms", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"test": "../../node_modules/.bin/mocha --compilers js:../../node_modules/babel-register 'src/**/*.test.js'", | ||
"test": "mocha --compilers js:babel-register 'src/**/*.test.js'", | ||
"test:watch": "npm run test -- --watch", | ||
"prebuild": "npm run test", | ||
"build": "BABEL_ENV=production ../../node_modules/.bin/babel src/ --out-dir=lib/ -s", | ||
"coverage": "../../node_modules/.bin/nyc --reporter=lcov --reporter=json npm run test", | ||
"build": "BABEL_ENV=production babel src/ --out-dir=lib/ -s", | ||
"coverage": "nyc --reporter=lcov --reporter=json npm run test", | ||
"prepublish": "npm run build" | ||
@@ -29,6 +28,7 @@ }, | ||
"peerDependencies": { | ||
"cerebral": "^2.0.0-alpha.4-alpha.ed1e4568" | ||
"cerebral": "^2.0.0-b-alpha.3b97c05c" | ||
}, | ||
"devDependencies": { | ||
"cerebral": "^2.0.0-alpha.4-alpha.ed1e4568" | ||
"@cerebral/monorepo": "^0.0.1-alpha.3b97c05c", | ||
"cerebral": "^2.0.0-b-alpha.3b97c05c" | ||
}, | ||
@@ -35,0 +35,0 @@ "nyc": { |
# cerebral-forms | ||
Signals, actions and state factories to create forms | ||
## Install | ||
This project is still in alpha. To test alpha version check [instructions in monorepo](https://github.com/cerebral/cerebral/blob/master/README.md). | ||
## API | ||
@@ -10,2 +13,4 @@ Cerebral forms is basically a function that creates state needed to handle validation and an action factory for validating fields. It is simple in nature, but handles all the complexity that comes with forms. | ||
You can add any properties to a form where properties containing an object with a "value" property are identified as fields. | ||
```js | ||
@@ -24,6 +29,8 @@ import {form} from 'cerebral-forms' | ||
// Error messages mapped to same index as validation rule | ||
errorMessages: ['Must be at least 3 characters long'], | ||
validationMessages: ['Must be at least 3 characters long'], | ||
// When setting isRequired to true the field will be invalid if there | ||
// is no value. To determine if there is not value, check "isValueRules" below | ||
isRequired: false, | ||
// Error message when field is required but has no value | ||
requiredMessage: null, | ||
// Will only be valid if this other field is also valid. | ||
@@ -62,3 +69,3 @@ // Point to a field in the model | ||
// Combine rules using an object | ||
errorMessages: [{ | ||
validationRules: [{ | ||
minLength: 3, | ||
@@ -92,2 +99,44 @@ isAlpha: true | ||
#### Set a default value for the whole form | ||
You can set a default value for a property using a factory: | ||
```js | ||
import {form, getFormFields} from 'cerebral-forms' | ||
const MyFormFactory = (formObject) => { | ||
const myForm = form(formObject) | ||
const fields = getFormFields(myForm) | ||
// You can also set some special properties for the whole form | ||
newForm.showErrors = false | ||
fields.forEach((field) => { | ||
field.requiredMessage = field.requiredMessage || 'This field is required' | ||
field.someProp = field.someProp || 'Some default' | ||
}) | ||
return myForm | ||
} | ||
``` | ||
#### Custom global props | ||
You can add custom props to the root to the form state. | ||
For example if you want to show validation errors only | ||
when submitting the form you can add a `showErrors` prop | ||
which you set true when validation fails during form submit. | ||
```js | ||
import {form} from 'cerebral-forms' | ||
export default function MyAction({state}) { | ||
state.set('some.new.form', form({ | ||
name: { | ||
value: '', | ||
}, | ||
showErrors: false | ||
})) | ||
} | ||
``` | ||
### field | ||
@@ -146,3 +195,3 @@ To add a new field you simply merge a new form into the existing one. | ||
onChange={(event) => fieldChanged({ | ||
field: 'someModule.firstName', | ||
field: 'someModule.form.firstName', | ||
value: event.target.value | ||
@@ -161,2 +210,3 @@ })} | ||
```js | ||
import {input} from 'cerebral/operators' | ||
import {validateField} from 'cerebral-forms' | ||
@@ -166,3 +216,6 @@ | ||
doThis, | ||
// static | ||
validateField('path.to.form.field'), | ||
// dynamic | ||
validateField(input`fieldPath`), | ||
doThat | ||
@@ -176,6 +229,10 @@ ] | ||
```js | ||
import {input} from 'cerebral/operators' | ||
import {validateForm} from 'cerebral-forms' | ||
export default [ | ||
// static | ||
validateForm('path.to.form'), | ||
// dynamic | ||
validateForm(input`formPath`), | ||
isFormValid, { | ||
@@ -196,2 +253,3 @@ true: [ | ||
```js | ||
import {input} from 'cerebral/operators' | ||
import {resetForm} from 'cerebral-forms' | ||
@@ -201,3 +259,6 @@ | ||
doThis, | ||
// static | ||
resetForm('path.to.form'), | ||
// dynamic | ||
resetForm(input`formPath`), | ||
doThat | ||
@@ -293,6 +354,10 @@ ] | ||
```js | ||
import {input} from 'cerebral/operators' | ||
import {isValidForm} from 'cerebral-forms' | ||
export default [ | ||
isValidForm('path.to.form'), { | ||
// static | ||
isValidForm('path.to.form') | ||
// dynamic | ||
isValidForm(input`formPath`), { | ||
true: [], | ||
@@ -310,3 +375,3 @@ false: [] | ||
- **isUndefined** - Checks if undefined | ||
- **isEmpyString** - Checks if empty string | ||
- **isEmpty** - Checks if empty string | ||
- **isEmail** - Checks if valid email format | ||
@@ -313,0 +378,0 @@ - **isUrl** - Checks if valid url format |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
229067
395
2
48
1183