factorial-form
Advanced tools
Comparing version 1.0.1 to 1.0.2
module.exports = { | ||
parser: 'babel-eslint', | ||
plugins: [ 'flowtype' ], | ||
parser: '@typescript-eslint/parser', | ||
plugins: ['@typescript-eslint'], | ||
@@ -16,8 +16,10 @@ env: { | ||
sourceType: 'module', | ||
ecmaFeatures: { | ||
experimentalObjectRestSpread: true | ||
} | ||
}, | ||
extends: [ | ||
'plugin:flowtype/recommended', | ||
'standard' | ||
] | ||
rules: { | ||
'object-curly-spacing': ['warn', 'always'] | ||
} | ||
}; |
# Changelog | ||
## `1.0.0` | ||
- Upgrade mobx dependency | ||
- Removed decorators | ||
- Switched to typescript | ||
## `0.2.0` | ||
@@ -4,0 +10,0 @@ |
388
lib/index.js
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.numberParser = exports.Field = exports.Form = undefined; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var _Form = require('./Form'); | ||
var mobx = require('mobx'); | ||
var omitBy = require('lodash/omitBy'); | ||
var mapValues = require('lodash/mapValues'); | ||
var get = require('lodash/get'); | ||
var isNull = require('lodash/isNull'); | ||
var isObject = require('lodash/isObject'); | ||
var forEach = require('lodash/forEach'); | ||
var some = require('lodash/some'); | ||
var isFinite = require('lodash/isFinite'); | ||
var moment = require('moment'); | ||
var last = require('lodash/last'); | ||
var flat = require('flat'); | ||
var _Form2 = _interopRequireDefault(_Form); | ||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } | ||
var _Field = require('./Field'); | ||
var omitBy__default = /*#__PURE__*/_interopDefaultLegacy(omitBy); | ||
var mapValues__default = /*#__PURE__*/_interopDefaultLegacy(mapValues); | ||
var get__default = /*#__PURE__*/_interopDefaultLegacy(get); | ||
var isNull__default = /*#__PURE__*/_interopDefaultLegacy(isNull); | ||
var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject); | ||
var forEach__default = /*#__PURE__*/_interopDefaultLegacy(forEach); | ||
var some__default = /*#__PURE__*/_interopDefaultLegacy(some); | ||
var isFinite__default = /*#__PURE__*/_interopDefaultLegacy(isFinite); | ||
var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment); | ||
var last__default = /*#__PURE__*/_interopDefaultLegacy(last); | ||
var flat__default = /*#__PURE__*/_interopDefaultLegacy(flat); | ||
var _Field2 = _interopRequireDefault(_Field); | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. | ||
var _numberParser = require('./numberParser'); | ||
Permission to use, copy, modify, and/or distribute this software for any | ||
purpose with or without fee is hereby granted. | ||
var _numberParser2 = _interopRequireDefault(_numberParser); | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
PERFORMANCE OF THIS SOFTWARE. | ||
***************************************************************************** */ | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
} | ||
exports.Form = _Form2.default; | ||
exports.Field = _Field2.default; | ||
exports.numberParser = _numberParser2.default; | ||
function __generator(thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
} | ||
/** | ||
* Support any input that may contains decimals | ||
* and other stuff. Check the test cases to understand | ||
* which cases we handle. | ||
* | ||
* Improve this if you find how. | ||
*/ | ||
function numberParser(str) { | ||
var cleanString = str.replace(/[^\d,.-]/g, ''); | ||
var sign = cleanString.charAt(0) === '-' ? '-' : '+'; | ||
var parts = cleanString.split(/[.,]/); | ||
var decimals = parts.length > 1 && last__default['default'](parts).length < 3 | ||
? parts.pop() | ||
: null; | ||
var number = parts.join('').replace(/[.,]\d*$/, '').replace(/\D/g, ''); | ||
return Number(sign + number + (decimals ? '.' + decimals : '')); | ||
} | ||
var DATE_FORMAT = 'L'; | ||
var Field = /** @class */ (function () { | ||
function Field(value, type) { | ||
this.type = type; | ||
this.mapAndSet(value); | ||
this.originalValue = this.value; | ||
this.errors = null; | ||
mobx.makeObservable(this, { | ||
value: mobx.observable, | ||
errors: mobx.observable, | ||
originalValue: mobx.observable, | ||
isDirty: mobx.computed, | ||
mapAndSet: mobx.action, | ||
set: mobx.action, | ||
clean: mobx.action, | ||
setErrors: mobx.action | ||
}); | ||
} | ||
/** | ||
* Converts the incoming value | ||
* before being persisted in the field | ||
*/ | ||
Field.prototype._mapIn = function (value) { | ||
if (value == null) | ||
return ''; | ||
switch (this.type) { | ||
case 'timestamp': | ||
if (typeof value !== 'number') | ||
throw new Error('Fields expects a number'); | ||
return moment__default['default'].unix(value).format(DATE_FORMAT); | ||
case 'date': | ||
if (typeof value !== 'string') | ||
throw new Error('Fields expects an string'); | ||
return moment__default['default'](value, 'YYYY-MM-DD', true).format(DATE_FORMAT); | ||
case 'number': | ||
if (typeof value !== 'number') | ||
throw new Error('Fields expects a number'); | ||
return value.toString(); | ||
case 'cents': | ||
if (typeof value !== 'number') | ||
throw new Error('Fields expects a number'); | ||
return (value / 100).toString(); | ||
case 'boolean': | ||
case 'string': | ||
case 'file': | ||
case 'any': | ||
return value; | ||
default: | ||
throw new Error("unknown field type: " + this.type); | ||
} | ||
}; | ||
/** | ||
* Converts back the value from the field | ||
*/ | ||
Field.prototype._mapOut = function () { | ||
var value = typeof this.value === 'string' | ||
? this.value.trim() | ||
: this.value; | ||
switch (this.type) { | ||
case 'timestamp': | ||
return moment__default['default'].utc(String(value), DATE_FORMAT).unix() || null; | ||
case 'date': | ||
var date = moment__default['default'](String(value), DATE_FORMAT, true); | ||
return date.isValid() ? date.format('YYYY-MM-DD') : null; | ||
case 'number': | ||
var number = numberParser(typeof value === 'number' ? value.toString() : String(value)); | ||
return number != null && isFinite__default['default'](number) ? Math.round(number) : null; | ||
case 'cents': | ||
var cents = numberParser(typeof value === 'number' ? value.toString() : String(value)); | ||
return cents != null && isFinite__default['default'](cents) ? Math.round(cents * 100) : null; | ||
case 'boolean': | ||
return value; | ||
case 'string': | ||
case 'file': | ||
case 'any': | ||
return value || null; | ||
default: | ||
throw new Error("unknown field type: " + this.type); | ||
} | ||
}; | ||
Object.defineProperty(Field.prototype, "isDirty", { | ||
get: function () { | ||
return this.originalValue !== this.value; | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Field.prototype.mapAndSet = function (value) { | ||
this.value = this._mapIn(value); | ||
}; | ||
Field.prototype.set = function (value) { | ||
this.value = value; | ||
}; | ||
Field.prototype.clean = function () { | ||
this.originalValue = this.value; | ||
}; | ||
Field.prototype.setErrors = function (errors) { | ||
this.errors = errors; | ||
}; | ||
return Field; | ||
}()); | ||
var buildFields = function (values, schema) { | ||
return omitBy__default['default'](mapValues__default['default'](flat__default['default'](schema), function (value, attribute) { | ||
return isObject__default['default'](value) | ||
? null | ||
: new Field(get__default['default'](values, attribute), value); | ||
}), isNull__default['default']); | ||
}; | ||
var Form = /** @class */ (function () { | ||
function Form(values, schema) { | ||
this.fields = buildFields(values, schema); | ||
mobx.makeObservable(this, { | ||
cleanAll: mobx.action, | ||
resetErrors: mobx.action, | ||
setErrors: mobx.action, | ||
resetValues: mobx.action, | ||
setValues: mobx.action, | ||
handleErrors: mobx.action, | ||
isComplete: mobx.computed, | ||
values: mobx.computed, | ||
isDirty: mobx.computed | ||
}); | ||
} | ||
Form.prototype.data = function () { | ||
return flat__default['default'].unflatten(mapValues__default['default'](this.fields, function (field) { return field._mapOut(); })); | ||
}; | ||
Form.prototype.has = function (attribute) { | ||
return Boolean(this.fields[attribute]); | ||
}; | ||
Form.prototype.get = function (attribute) { | ||
var field = this.fields[attribute]; | ||
if (!field) | ||
throw new Error("Field \"" + attribute + "\" not found"); | ||
return field; | ||
}; | ||
/** | ||
* Cleans all the forms by reseting their original | ||
* values | ||
*/ | ||
Form.prototype.cleanAll = function () { | ||
forEach__default['default'](this.fields, function (field) { | ||
return field.clean(); | ||
}); | ||
}; | ||
/** | ||
* Resets all the error fields | ||
*/ | ||
Form.prototype.resetErrors = function () { | ||
forEach__default['default'](this.fields, function (field) { | ||
field.setErrors(null); | ||
}); | ||
}; | ||
/** | ||
* Sets the errors with a given | ||
* hash of attribute -> error | ||
*/ | ||
Form.prototype.setErrors = function (errors) { | ||
var _this = this; | ||
this.resetErrors(); | ||
var flatErrors = flat__default['default'](errors, { safe: true }); | ||
forEach__default['default'](flatErrors, function (error, attribute) { | ||
if (_this.has(attribute)) { | ||
_this.get(attribute).setErrors(error); | ||
} | ||
}); | ||
}; | ||
Object.defineProperty(Form.prototype, "isComplete", { | ||
/** | ||
* Checks if all fields have a value. | ||
*/ | ||
get: function () { | ||
return !some__default['default'](this.fields, function (field) { | ||
switch (field.type) { | ||
case 'number': | ||
case 'cents': | ||
case 'boolean': | ||
return field.value === '' || isNull__default['default'](field.value); | ||
default: | ||
return !field.value; | ||
} | ||
}); | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Form.prototype, "values", { | ||
/** | ||
* Return the values from the form as they are | ||
*/ | ||
get: function () { | ||
return mapValues__default['default'](this.fields, function (field) { return field.value; }); | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
Object.defineProperty(Form.prototype, "isDirty", { | ||
/** | ||
* Return whether the form is dirty | ||
*/ | ||
get: function () { | ||
return some__default['default'](this.fields, function (field) { return field.isDirty; }); | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
/** | ||
* Reset values | ||
*/ | ||
Form.prototype.resetValues = function () { | ||
forEach__default['default'](this.fields, function (field) { return field.set(''); }); | ||
}; | ||
/** | ||
* Sets the fields values with a given | ||
* hash of attribute -> value | ||
*/ | ||
Form.prototype.setValues = function (values) { | ||
var _this = this; | ||
forEach__default['default'](values, function (value, attribute) { | ||
if (!_this.has(attribute)) | ||
return; | ||
var field = _this.get(attribute); | ||
field.setErrors(null); | ||
field.mapAndSet(value); | ||
}); | ||
}; | ||
/** | ||
* Creates a new model on the given collection | ||
*/ | ||
Form.prototype.create = function (collection, options) { | ||
var _this = this; | ||
if (options === void 0) { options = { optimistic: true }; } | ||
return this.handleErrors(function () { return collection.create(_this.data(), options); }); | ||
}; | ||
/** | ||
* Saves the model with the given fields | ||
*/ | ||
Form.prototype.save = function (model, options) { | ||
var _this = this; | ||
if (options === void 0) { options = { optimistic: true, patch: true }; } | ||
return this.handleErrors(function () { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { | ||
return [2 /*return*/, model.save(this.data(), options)]; | ||
}); }); }); | ||
}; | ||
Form.prototype.handleErrors = function (fn) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var values, _a, error_1, payload_1; | ||
var _this = this; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
_b.trys.push([0, 2, , 3]); | ||
_a = mobx.toJS; | ||
return [4 /*yield*/, fn()]; | ||
case 1: | ||
values = _a.apply(void 0, [_b.sent()]); | ||
return [3 /*break*/, 3]; | ||
case 2: | ||
error_1 = _b.sent(); | ||
payload_1 = error_1.payload; | ||
mobx.action('handleErrors-error', function () { | ||
_this.setErrors(mobx.toJS(payload_1 || error_1)); | ||
_this.cleanAll(); | ||
})(); | ||
throw error_1; | ||
case 3: | ||
mobx.action('handleErrors-done', function () { | ||
_this.setValues(values); | ||
_this.resetErrors(); | ||
_this.cleanAll(); | ||
})(); | ||
return [2 /*return*/, values]; | ||
} | ||
}); | ||
}); | ||
}; | ||
return Form; | ||
}()); | ||
exports.Field = Field; | ||
exports.Form = Form; | ||
exports.numberParser = numberParser; |
{ | ||
"name": "factorial-form", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "Factorial form library", | ||
"repository": { | ||
"type": "git", | ||
"url": "git@github.com:factorial/factorial-form.git" | ||
"url": "git@github.com:factorialco/factorial-form.git" | ||
}, | ||
"license": "MIT", | ||
"jest": { | ||
"collectCoverage": true, | ||
"testRegex": "/__tests__/.*\\.spec\\.js$", | ||
"collectCoverageFrom": [ | ||
"src/**/*.js" | ||
] | ||
"roots": [ "." ], | ||
"transform": { ".+\\.tsx?$": "ts-jest" }, | ||
"testRegex": "/__tests__/.*\\.spec\\.ts$" | ||
}, | ||
"standard": { | ||
"parser": "babel-eslint", | ||
"globals": [ | ||
"it", | ||
"describe", | ||
"beforeEach", | ||
"expect", | ||
"Class", | ||
"jest" | ||
] | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.4", | ||
"moment": "2.21.0", | ||
"flat": "2.0.1" | ||
"flat": "5.0.2", | ||
"lodash": "^4.17.21", | ||
"moment": "2.29.1", | ||
"mobx": "^6.3.2", | ||
"rollup-plugin-dts": "^3.0.2" | ||
}, | ||
"peerDependencies": { | ||
"mobx": "^5.9.4" | ||
"mobx": "^6.3.2" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.24.1", | ||
"babel-core": "^6.25.0", | ||
"babel-eslint": "^7.2.3", | ||
"babel-jest": "^20.0.3", | ||
"babel-plugin-transform-async-to-generator": "^6.24.1", | ||
"babel-plugin-transform-decorators-legacy": "^1.3.4", | ||
"babel-plugin-transform-flow-strip-types": "^6.22.0", | ||
"babel-plugin-transform-runtime": "^6.23.0", | ||
"babel-polyfill": "^6.23.0", | ||
"babel-preset-es2015": "^6.24.1", | ||
"babel-preset-stage-1": "^6.24.1", | ||
"babel-register": "^6.24.1", | ||
"eslint": "^3.19.0", | ||
"eslint-config-standard": "^10.2.1", | ||
"eslint-plugin-flowtype": "2.34.0", | ||
"eslint-plugin-import": "^2.3.0", | ||
"eslint-plugin-node": "^5.0.0", | ||
"eslint-plugin-promise": "^3.5.0", | ||
"eslint-plugin-standard": "^3.0.1", | ||
"flow-bin": "^0.47.0", | ||
"flow-copy-source": "^1.1.0", | ||
"husky": "^0.13.4", | ||
"jest": "^20.0.4", | ||
"lint-staged": "^3.6.0", | ||
"mobx": "^5.9.4", | ||
"prettier-standard": "^5.0.0", | ||
"rimraf": "^2.6.1" | ||
"@types/jest": "26.0.23", | ||
"@types/lodash": "4.14.170", | ||
"@typescript-eslint/eslint-plugin": "4.26.1", | ||
"@typescript-eslint/parser": "4.26.1", | ||
"eslint": "7.28.0", | ||
"husky": "4.3.0", | ||
"jest": "27.0.4", | ||
"lint-staged": "11.0.0", | ||
"rimraf": "3.0.2", | ||
"rollup": "2.51.2", | ||
"rollup-plugin-node-resolve": "5.2.0", | ||
"rollup-plugin-typescript2": "^0.30.0", | ||
"ts-jest": "27.0.3", | ||
"tslib": "2.3.0", | ||
"typescript": "4.3.2" | ||
}, | ||
"main": "lib", | ||
"types": "./lib/index.d.ts", | ||
"scripts": { | ||
"build": "yarn build:clean && rollup --config", | ||
"build:clean": "rimraf lib", | ||
"build:lib": "babel -d lib src --ignore '**/__tests__/**'", | ||
"build:flow": "flow-copy-source -v -i '**/__tests__/**' src lib", | ||
"build": "npm run build:clean && npm run build:lib && npm run build:flow", | ||
"prepublish": "npm run build", | ||
"jest": "BABEL_ENV=test NODE_PATH=src jest --no-cache", | ||
"lint": "eslint src __tests__", | ||
"flow": "flow", | ||
"test": "npm run flow && npm run lint && npm run jest", | ||
"format": "prettier-standard --print-width 60 \"{src,__tests__}/**/*.js\"", | ||
"prepush": "npm test", | ||
"prepublish": "yarn build", | ||
"jest": "NODE_PATH=src jest --no-cache", | ||
"lint": "eslint --ext .ts --cache src/ __tests__/", | ||
"test": "yarn lint && yarn jest", | ||
"prepush": "yarn test", | ||
"watch": "rollup --config -w", | ||
"lint-staged": { | ||
"linters": { | ||
"{src|__tests__}/**/*.js": [ | ||
"prettier-standard", | ||
"{src|__tests__}/**/*.ts": [ | ||
"git add" | ||
@@ -83,0 +57,0 @@ ] |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
15
38981
6
13
953
+ Addedmobx@^6.3.2
+ Addedrollup-plugin-dts@^3.0.2
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Addedflat@5.0.2(transitive)
+ Addedfsevents@2.3.3(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedmagic-string@0.25.9(transitive)
+ Addedmobx@6.13.5(transitive)
+ Addedmoment@2.29.1(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedrollup@2.79.2(transitive)
+ Addedrollup-plugin-dts@3.0.2(transitive)
+ Addedsourcemap-codec@1.4.8(transitive)
+ Addedtypescript@4.9.5(transitive)
- Removedflat@2.0.1(transitive)
- Removedis-buffer@1.1.6(transitive)
- Removedmobx@5.15.7(transitive)
- Removedmoment@2.21.0(transitive)
Updatedflat@5.0.2
Updatedlodash@^4.17.21
Updatedmoment@2.29.1