Comparing version 0.0.1 to 0.0.3
@@ -8,3 +8,3 @@ /*global describe, it, expect*/ | ||
var formatic = require('../')('react'); | ||
var formatic = require('../').create('react'); | ||
@@ -20,3 +20,7 @@ var compile = function (field, props) { | ||
_.each(props, function (value, key) { | ||
expect(form.root.fields[0][key]).toEqual(value); | ||
var child = form.root.fields[0]; | ||
if (child.type === 'field') { | ||
child = child.fields[0]; | ||
} | ||
expect(child[key]).toEqual(value); | ||
}); | ||
@@ -51,2 +55,4 @@ }; | ||
}); | ||
}); |
@@ -6,3 +6,3 @@ /*global describe, it, expect*/ | ||
var formatic = require('../')('react'); | ||
var formatic = require('../').create('react'); | ||
@@ -9,0 +9,0 @@ it('form can hold data', function () { |
@@ -6,3 +6,3 @@ /*global describe, it, expect*/ | ||
var Formatic = require('../'); | ||
var Formatic = require('../').create; | ||
@@ -184,2 +184,39 @@ var pigLatin = function (word) { | ||
it('should take parameters for a plugin', function () { | ||
var plugin = function (formatic, plugin) { | ||
formatic.method('ask', function () { | ||
return plugin.config.answer; | ||
}); | ||
}; | ||
var formatic = Formatic(); | ||
formatic.plugin(plugin, { | ||
answer: 42 | ||
}); | ||
expect(formatic.ask()).toEqual(42); | ||
}); | ||
it('should listen for plugin', function () { | ||
var formatic = Formatic(); | ||
var foundPlugin = false; | ||
formatic.onPlugin(function (plugin) { | ||
if (plugin.tag === 'gotcha') { | ||
foundPlugin = true; | ||
} | ||
}); | ||
formatic.plugin(function (formatic, plugin) { | ||
plugin.tag = 'gotcha'; | ||
}); | ||
expect(foundPlugin).toEqual(true); | ||
}); | ||
}); |
@@ -18,3 +18,3 @@ /*global describe, it, expect*/ | ||
var typeMap = formatic.options.typeMap || {}; | ||
var typeMap = formatic.config('core-type.typeMap') || {}; | ||
var types = Object.keys(typeMap).filter(function (key) { | ||
@@ -27,3 +27,7 @@ return typeMap[key] === type; | ||
if (types.length === 0) { | ||
types.push(type); | ||
// If this type maps to itself, make sure that type doesn't map to | ||
// something else. | ||
if (!(type in typeMap)) { | ||
types.push(type); | ||
} | ||
} | ||
@@ -34,3 +38,3 @@ | ||
var testValueType = function (field, tagName) { | ||
var testType = function (field, fn) { | ||
@@ -54,15 +58,56 @@ var types = unmapType(field.type); | ||
}); | ||
form.set('name', 'Joe'); | ||
var node = component.getDOMNode().getElementsByTagName(tagName)[0]; | ||
fn(form, component); | ||
}); | ||
}); | ||
}; | ||
expect(node.value).toEqual('Joe'); | ||
var testValueType = function (field, selector, setter) { | ||
testType(field, function (form, component) { | ||
form.set('name', 'Joe'); | ||
var value; | ||
var node; | ||
if (typeof selector === 'function') { | ||
value = selector(component.getDOMNode()); | ||
} else { | ||
if (selector[0] === '.') { | ||
node = component.getDOMNode().getElementsByClassName(selector)[0]; | ||
} else { | ||
node = component.getDOMNode().getElementsByTagName(selector)[0]; | ||
} | ||
value = node.value; | ||
} | ||
expect(value).toEqual('Joe'); | ||
if (typeof setter === 'function') { | ||
setter(component.getDOMNode(), 'Mary'); | ||
} else { | ||
node.value = 'Mary'; | ||
TestUtils.Simulate.change(node); | ||
} | ||
TestUtils.Simulate.change(node); | ||
expect(form.val()).toEqual({name: 'Mary'}); | ||
}); | ||
expect(form.val()).toEqual({name: 'Mary'}); | ||
}); | ||
}; | ||
var testValuesType = function (field, selector, setter) { | ||
testType(field, function (form, component) { | ||
form.set('colors', ['red', 'green']); | ||
var value = selector(component.getDOMNode()); | ||
expect(value).toEqual(['red', 'green']); | ||
setter(component.getDOMNode(), ['red']); | ||
expect(form.val()).toEqual({colors: ['red']}); | ||
}); | ||
}; | ||
testValueType({ | ||
@@ -85,10 +130,74 @@ type: 'text', | ||
testValueType({ | ||
type: 'dropdown', | ||
key: 'name', | ||
choices: ['Joe', 'Mary'] | ||
}, function (node) { | ||
return node.getElementsByClassName('field-value')[0].innerHTML; | ||
}, function (node, value) { | ||
var arrowNode = node.getElementsByClassName('field-toggle')[0]; | ||
TestUtils.Simulate.click(arrowNode); | ||
var choiceNodes = node.getElementsByClassName('field-choice'); | ||
choiceNodes = Array.prototype.slice.call(choiceNodes, 0); | ||
var matchingNodes = choiceNodes.filter(function (node) { | ||
return node.innerHTML === value; | ||
}); | ||
TestUtils.Simulate.click(matchingNodes[0]); | ||
}); | ||
testValueType({ | ||
type: 'password', | ||
key: 'name' | ||
}, 'input'); | ||
testValuesType({ | ||
type: 'checkbox', | ||
key: 'colors', | ||
choices: ['red', 'blue', 'green'] | ||
}, function (node) { | ||
var choiceNodes = node.getElementsByTagName('input'); | ||
choiceNodes = Array.prototype.slice.call(choiceNodes, 0); | ||
return choiceNodes.filter(function (choiceNode) { | ||
return choiceNode.checked; | ||
}).map(function (choiceNode) { | ||
return choiceNode.value; | ||
}); | ||
}, function (node, value) { | ||
var choiceNodes = node.getElementsByTagName('input'); | ||
choiceNodes = Array.prototype.slice.call(choiceNodes, 0); | ||
choiceNodes.forEach(function (choiceNode) { | ||
if (value.indexOf(choiceNode.value) >= 0) { | ||
if (!choiceNode.checked) { | ||
choiceNode.checked = true; | ||
TestUtils.Simulate.change(choiceNode); | ||
} | ||
} else { | ||
if (choiceNode.checked) { | ||
choiceNode.checked = false; | ||
TestUtils.Simulate.change(choiceNode); | ||
} | ||
} | ||
}); | ||
}); | ||
testType({ | ||
type: 'checkbox', | ||
key: 'optIn' | ||
}, function (form, component) { | ||
form.set('optIn', true); | ||
var node = component.getDOMNode().getElementsByTagName('input')[0]; | ||
expect(node.checked).toEqual(true); | ||
node.checked = false; | ||
TestUtils.Simulate.change(node); | ||
expect(form.val()).toEqual({optIn: false}); | ||
}); | ||
}; | ||
testWithFormatic(require('../')('react')); | ||
testWithFormatic(require('../')('zapier')); | ||
testWithFormatic(require('../').create('react')); | ||
testWithFormatic(require('../').create('zapier')); | ||
}); |
@@ -119,2 +119,118 @@ /*global describe, it, expect*/ | ||
it('should parse blank text', function () { | ||
expect(utils.parseTextWithTags('')).toEqual([]); | ||
}); | ||
it('should parse plain text', function () { | ||
expect(utils.parseTextWithTags('foo')).toEqual([{type: 'text', value: 'foo'}]); | ||
}); | ||
it('should parse text with tag in middle', function () { | ||
expect(utils.parseTextWithTags('foo {{bar}} baz')).toEqual([ | ||
{type: 'text', value: 'foo '}, | ||
{type: 'tag', value: 'bar'}, | ||
{type: 'text', value: ' baz'} | ||
]); | ||
}); | ||
it('should parse text with tag at front', function () { | ||
expect(utils.parseTextWithTags('{{bar}} baz')).toEqual([ | ||
{type: 'tag', value: 'bar'}, | ||
{type: 'text', value: ' baz'} | ||
]); | ||
}); | ||
it('should parse text with tag at back', function () { | ||
expect(utils.parseTextWithTags('{{bar}} baz')).toEqual([ | ||
{type: 'tag', value: 'bar'}, | ||
{type: 'text', value: ' baz'} | ||
]); | ||
}); | ||
it('should convert ascii text to hidden unicode and back', function () { | ||
var hidden = utils.hideUnicodeMessage('foobar'); | ||
var original = utils.unhideUnicodeMessage(hidden); | ||
expect(original).toEqual('foobar'); | ||
}); | ||
it('should convert unicode text to hidden unicode and back', function () { | ||
var hidden = utils.hideUnicodeMessage('€'); | ||
var original = utils.unhideUnicodeMessage(hidden); | ||
expect(original).toEqual('€'); | ||
}); | ||
// it('should diff same', function () { | ||
// | ||
// expect(utils.diff('foo', 'foo')).toEqual(null); | ||
// }); | ||
// | ||
// it('should diff front insert', function () { | ||
// | ||
// expect(utils.diff('bar', 'foobar')).toEqual({ | ||
// insert: 'foo', | ||
// delete: '', | ||
// pos: 0 | ||
// }); | ||
// }); | ||
// | ||
// it('should diff back insert', function () { | ||
// | ||
// expect(utils.diff('foo', 'foobar')).toEqual({ | ||
// insert: 'bar', | ||
// delete: '', | ||
// pos: 3 | ||
// }); | ||
// }); | ||
// | ||
// it('should diff front delete', function () { | ||
// | ||
// expect(utils.diff('foobar', 'bar')).toEqual({ | ||
// insert: '', | ||
// delete: 'foo', | ||
// pos: 0 | ||
// }); | ||
// }); | ||
// | ||
// it('should diff back delete', function () { | ||
// | ||
// expect(utils.diff('foobar', 'foo')).toEqual({ | ||
// insert: '', | ||
// delete: 'bar', | ||
// pos: 3 | ||
// }); | ||
// }); | ||
// | ||
// it('should diff middle insert', function () { | ||
// | ||
// expect(utils.diff('foobaz', 'foobarbaz')).toEqual({ | ||
// insert: 'bar', | ||
// delete: '', | ||
// pos: 3 | ||
// }); | ||
// }); | ||
// | ||
// it('should diff middle delete', function () { | ||
// | ||
// expect(utils.diff('foobarbaz', 'foobaz')).toEqual({ | ||
// insert: '', | ||
// delete: 'bar', | ||
// pos: 3 | ||
// }); | ||
// }); | ||
// | ||
// it('should char diff', function () { | ||
// | ||
// expect(utils.charDiff('foobar', 'foocar')).toEqual(3); | ||
// | ||
// expect(utils.charDiff('foobar', 'feebar', -1)).toEqual(3); | ||
// }); | ||
}); |
@@ -16,4 +16,8 @@ 'use strict'; | ||
gulp.task('watch', ['bundle-watch']); | ||
gulp.task('test-watch', function () { | ||
gulp.watch(['./formatic-dev.js', './__tests__/**/*.js'], ['test']); | ||
}); | ||
gulp.task('watch', ['bundle-watch', 'test-watch']); | ||
gulp.task('build', ['build-dev', 'build-prod']); | ||
@@ -20,0 +24,0 @@ |
@@ -7,2 +7,4 @@ 'use strict'; | ||
var pluginRegistry = {}; | ||
var Formatic = function () { | ||
@@ -16,15 +18,128 @@ | ||
formatic.options = {}; | ||
formatic.registerPlugin = function (name, pluginFactory, config) { | ||
pluginRegistry[name] = { | ||
create: pluginFactory, | ||
config: config || {} | ||
}; | ||
}; | ||
formatic.config = function (options) { | ||
_.extend(formatic.options, options); | ||
formatic.registerPlugins = function () { | ||
var args = Array.prototype.slice.call(arguments, 0); | ||
args.forEach(function (plugin) { | ||
formatic.registerPlugin(plugin[0], plugin[1], plugin[2]); | ||
}); | ||
}; | ||
formatic.plugin = function (plugin) { | ||
if (_.isString(plugin)) { | ||
plugin = Formatic.plugins[plugin]; | ||
var pluginCallbacks = []; | ||
var unknownPluginId = 0; | ||
var plugins = {}; | ||
var getConfig = function (name, key) { | ||
if (!plugins[name]) { | ||
return null; | ||
} | ||
plugin(formatic); | ||
if (!key) { | ||
return plugins[name].config; | ||
} | ||
return plugins[name].config[key] || null; | ||
}; | ||
var setConfig = function (name, key, config) { | ||
if (!plugins[name]) { | ||
throw new Error('No config for plugin: ' + name); | ||
} | ||
if (!key) { | ||
if (!_.isObject(config)) { | ||
throw new Error('Invalid config for plugin: ' + name); | ||
} | ||
_.extend(plugins[name].config, config); | ||
} | ||
plugins[name].config[key] = config; | ||
}; | ||
formatic.config = function (key, config) { | ||
var pluginName = key; | ||
if (key.indexOf('.') >= 0) { | ||
pluginName = key.substring(0, key.indexOf('.')); | ||
key = key.substring(key.indexOf('.') + 1); | ||
} else { | ||
key = null; | ||
} | ||
if (!config) { | ||
return getConfig(pluginName, key); | ||
} | ||
setConfig(pluginName, key, config); | ||
}; | ||
formatic.loadPlugin = function (pluginFactory, config) { | ||
config = config || {}; | ||
var name = config.name; | ||
if (!name) { | ||
name = 'unknown_' + unknownPluginId; | ||
unknownPluginId++; | ||
} | ||
var plugin = utils.hookable(); | ||
plugin.create = pluginFactory; | ||
plugin.config = config; | ||
plugin.name = name; | ||
pluginFactory(formatic, plugin); | ||
pluginCallbacks.forEach(function (cb) { | ||
cb(plugin); | ||
}); | ||
plugins[name] = plugin; | ||
return plugin; | ||
}; | ||
formatic.configurePlugin = function (name, config) { | ||
var plugin = plugins[name]; | ||
_.extend(plugin.config, config); | ||
return plugin; | ||
}; | ||
formatic.findPlugin = function (name, config) { | ||
if (config && config.name) { | ||
name = config.name; | ||
} | ||
if (plugins[name]) { | ||
if (config) { | ||
return formatic.configurePlugin(name, config); | ||
} else { | ||
return plugins[name]; | ||
} | ||
} | ||
return null; | ||
}; | ||
formatic.plugin = function (pluginFactory, config) { | ||
var plugin; | ||
if (_.isString(pluginFactory)) { | ||
plugin = formatic.findPlugin(pluginFactory, config); | ||
if (plugin) { | ||
return plugin; | ||
} | ||
plugin = pluginRegistry[pluginFactory]; | ||
if (plugin) { | ||
config = _.extend({}, plugin.config, config); | ||
config.name = config.name || pluginFactory; | ||
return formatic.loadPlugin(plugin.create, config); | ||
} | ||
var name = pluginFactory; | ||
if (config && config.name) { | ||
name += '/' + config.name; | ||
} | ||
throw new Error('Plugin not found: ' + name); | ||
} | ||
return formatic.loadPlugin(pluginFactory, config); | ||
}; | ||
formatic.onPlugin = function (cb) { | ||
pluginCallbacks.push(cb); | ||
}; | ||
formatic.create = function () { | ||
return Formatic.apply(null, arguments); | ||
}; | ||
var Form = function () { | ||
@@ -42,2 +157,3 @@ this.init(); | ||
utils.hookable(Form.prototype); | ||
formatic.wrappable = utils.hookable; | ||
@@ -48,5 +164,16 @@ Form.prototype.method('init', function () { | ||
_.each(arguments, function (plugin) { | ||
formatic.plugin(plugin); | ||
}); | ||
// Take args like ('pluginA', {x: 1}). | ||
// Where {x: 1} is config for pluginA. | ||
for (var i = 0; i < arguments.length; i++) { | ||
var pluginFactory = arguments[i]; | ||
var config = config || {}; | ||
if (arguments[i + 1]) { | ||
var nextArg = arguments[i + 1]; | ||
if (!_.isFunction(nextArg) && _.isObject(nextArg)) { | ||
config = nextArg; | ||
i++; | ||
} | ||
} | ||
formatic.plugin(pluginFactory, config); | ||
} | ||
@@ -56,47 +183,57 @@ return formatic; | ||
module.exports = Formatic; | ||
var defaultFormatic = Formatic(); | ||
Formatic.plugins = { | ||
coreData: require('./plugins/core-data'), | ||
coreType: require('./plugins/core-type'), | ||
coreView: require('./plugins/core-view'), | ||
core: require('./plugins/core'), | ||
terse: require('./plugins/terse'), | ||
react: require('./plugins/react'), | ||
zapier: require('./plugins/zapier'), | ||
reactViewer: require('./plugins/react-viewer'), | ||
readOnly: require('./plugins/read-only') | ||
}; | ||
defaultFormatic.registerPlugins( | ||
['core-data', require('./plugins/core-data')], | ||
['core-meta', require('./plugins/core-meta')], | ||
['core-type', require('./plugins/core-type')], | ||
['core-types', require('./plugins/core-types')], | ||
['core-view', require('./plugins/core-view')], | ||
['core-validation', require('./plugins/core-validation')], | ||
['core', require('./plugins/core')], | ||
['terse', require('./plugins/terse')], | ||
['react', require('./plugins/react')], | ||
['zapier', require('./plugins/zapier')], | ||
['react-viewer', require('./plugins/react-viewer')], | ||
['read-only', require('./plugins/read-only')], | ||
['required', require('./plugins/required')], | ||
Formatic.types = { | ||
code: require('./types/code'), | ||
form: require('./types/form'), | ||
select: require('./types/select'), | ||
text: require('./types/text'), | ||
textarea: require('./types/text'), | ||
password: require('./types/text'), | ||
['type-code', require('./types/code')], | ||
['type-form', require('./types/form')], | ||
['type-select', require('./types/select')], | ||
['type-text', require('./types/text')], | ||
['type-textarea', require('./types/text')], | ||
['type-pretty-textarea', require('./types/text')], | ||
['type-password', require('./types/text')], | ||
['type-checkbox', require('./types/checkbox')], | ||
['type-dropdown', require('./types/select')], | ||
['type-string', require('./types/text')], | ||
['type-float', require('./types/number')], | ||
['type-integer', require('./types/number')], | ||
['type-number', require('./types/number')], | ||
['type-json', require('./types/json')], | ||
['type-if', require('./types/if')], | ||
['type-get', require('./types/get')], | ||
['type-eq', require('./types/eq')], | ||
string: require('./types/text'), | ||
float: require('./types/number'), | ||
integer: require('./types/number'), | ||
number: require('./types/number'), | ||
['view-form', require('./views/react/form')], | ||
['view-field', require('./views/react/field')], | ||
['view-code', require('./views/react/code')], | ||
['view-select', require('./views/react/select')], | ||
['view-text', require('./views/react/text')], | ||
['view-string', require('./views/react/text')], | ||
['view-textarea', require('./views/react/textarea')], | ||
['view-password', require('./views/react/text')], | ||
['view-checkbox', require('./views/react/checkbox')], | ||
['view-boolean-checkbox', require('./views/react/boolean-checkbox')], | ||
['view-pretty-textarea', require('./views/react/pretty-textarea')], | ||
['view-dropdown', require('./views/react/dropdown')], | ||
['view-json', require('./views/react/json')], | ||
['view-float', require('./views/react/text')], | ||
['view-integer', require('./views/react/text')], | ||
['view-number', require('./views/react/text')] | ||
); | ||
if: require('./types/if'), | ||
get: require('./types/get'), | ||
eq: require('./types/eq') | ||
}; | ||
defaultFormatic.plugin('react'); | ||
Formatic.views = { | ||
react: { | ||
code: require('./views/react/code'), | ||
form: require('./views/react/form'), | ||
select: require('./views/react/select'), | ||
text: require('./views/react/text'), | ||
textarea: require('./views/react/textarea'), | ||
password: require('./views/react/text'), | ||
float: require('./types/text'), | ||
integer: require('./types/text'), | ||
number: require('./types/text') | ||
} | ||
}; | ||
module.exports = defaultFormatic; |
@@ -8,2 +8,13 @@ 'use strict'; | ||
var Actions = function (form) { | ||
this.form = form; | ||
}; | ||
formatic.method('action', function (name) { | ||
Actions.prototype[name] = function () { | ||
var args = [name].concat(Array.prototype.slice.call(arguments)); | ||
this.form.emitter.emit.apply(this.form.emitter, args); | ||
}; | ||
}); | ||
formatic.form.wrap('init', function (next) { | ||
@@ -13,5 +24,13 @@ this.data = {}; | ||
this.emitter = new Emitter(); | ||
this.actions = new Actions(this); | ||
this.emitter.on('change', function (field, value) { | ||
this.onChange(field, value); | ||
}.bind(this)); | ||
next(); | ||
}); | ||
formatic.action('change'); | ||
formatic.action('focus'); | ||
formatic.action('blur'); | ||
formatic.form.method('defaultRoot', function () { | ||
@@ -32,3 +51,3 @@ return { | ||
if (typeof value === 'undefined') { | ||
this.data(key); | ||
this.setData(key); | ||
} else { | ||
@@ -52,4 +71,13 @@ this.setKey(key, value); | ||
formatic.form.method('onChange', function (field, value) { | ||
if (field.key) { | ||
this.set(field.key, value); | ||
if (field && field.key) { | ||
try { | ||
var type = formatic.type(field.type); | ||
if (type && type.hasMethod('parseField')) { | ||
value = type.parseField(value); | ||
} | ||
this.set(field.key, value); | ||
} catch (e) { | ||
// failed to parse; don't respond to this change and leave state in | ||
// view till it is parseable | ||
} | ||
} | ||
@@ -70,3 +98,3 @@ }); | ||
formatic.form.method('data', function (value) { | ||
formatic.form.method('setData', function (value) { | ||
this.data = value; | ||
@@ -102,3 +130,3 @@ }); | ||
}); | ||
formatic.method('splitKey', function (key) { | ||
@@ -105,0 +133,0 @@ return key.split('.'); |
@@ -5,6 +5,6 @@ 'use strict'; | ||
module.exports = function (formatic) { | ||
module.exports = function (formatic, plugin) { | ||
var fixType = function (field) { | ||
var typeMap = formatic.options.typeMap; | ||
var typeMap = plugin.config.typeMap; | ||
if (!typeMap) { | ||
@@ -19,4 +19,2 @@ return; | ||
var toValue = function (field) { | ||
fixType(field); | ||
if (field.fields && field.fields.length > 0) { | ||
@@ -42,27 +40,33 @@ field.value = field.fields[0]; | ||
formatic.method('type', function (name, type) { | ||
var types = {}; | ||
if (typeof type === 'function') { | ||
type = type(formatic); | ||
// Auto register types. | ||
formatic.onPlugin(function (plugin) { | ||
if (plugin.name.indexOf('type-') === 0) { | ||
var type = plugin.name.substring('type-'.length); | ||
if (plugin.hasFields) { | ||
plugin.initField = toFields; | ||
} else { | ||
plugin.initField = toValue; | ||
} | ||
if (!types[type]) { | ||
types[type] = plugin; | ||
} | ||
} | ||
}); | ||
var compile = type.compile; | ||
var evalField = type.eval; | ||
var hasFields = type.hasFields || false; | ||
formatic.method('type', function (name, methods) { | ||
if (compile) { | ||
formatic.method('compileField_' + name, compile); | ||
if (typeof methods === 'undefined') { | ||
return types[name]; | ||
} | ||
if (evalField) { | ||
formatic.method('evalField_' + name, evalField); | ||
} | ||
if (hasFields) { | ||
formatic.method('initField_' + name, toFields); | ||
} else { | ||
formatic.method('initField_' + name, toValue); | ||
} | ||
// Create a type plugin to be picked up. | ||
formatic.plugin(function (formatic, plugin) { | ||
_.extend(plugin, methods); | ||
}, {name: 'type-' + name}); | ||
}); | ||
formatic.type('default', {}); | ||
formatic.form.replaceMethod('compileField', function (field) { | ||
@@ -78,14 +82,10 @@ | ||
var initHook = 'initField_' + field.type; | ||
fixType(field); | ||
if (formatic.hasMethod(initHook)) { | ||
formatic[initHook](field); | ||
} else { | ||
toValue(field); | ||
} | ||
var type = formatic.type(field.type) || formatic.type('default'); | ||
var compileHook = 'compileField_' + field.type; | ||
type.initField(field); | ||
if (formatic.hasMethod(compileHook)) { | ||
return formatic[compileHook](field, this.compileField.bind(this)); | ||
if (type.hasMethod('compileField')) { | ||
return type.compileField(field, this.compileField.bind(this)); | ||
} else { | ||
@@ -121,7 +121,7 @@ | ||
var evalHook = 'evalField_' + field.type; | ||
var type = formatic.type(field.type) || formatic.type('default'); | ||
if (formatic.hasMethod(evalHook)) { | ||
if (type.hasMethod('evalField')) { | ||
field = formatic[evalHook](field, data, runField.bind(this, data)); | ||
field = type.evalField(field, data, runField.bind(this, data)); | ||
return field; | ||
@@ -128,0 +128,0 @@ } else { |
@@ -5,9 +5,47 @@ 'use strict'; | ||
var Formatic = require('../formatic'); | ||
module.exports = function (formatic) { | ||
_.each(Formatic.types, function (type, key) { | ||
formatic.type(key, type); | ||
formatic.form.wrap('compile', function (next, field) { | ||
if (typeof field === 'undefined') { | ||
field = { | ||
type: 'form', | ||
fields: [] | ||
}; | ||
} else if (_.isArray(field)) { | ||
field = { | ||
type: 'form', | ||
fields: field | ||
}; | ||
} else if (field.type !== 'form') { | ||
field = { | ||
type: 'form', | ||
fields: [field] | ||
}; | ||
} | ||
return next(field); | ||
}); | ||
_.each([ | ||
'form', | ||
'text', | ||
'textarea', | ||
'pretty-textarea', | ||
'password', | ||
'select', | ||
'dropdown', | ||
'checkbox', | ||
'string', | ||
'float', | ||
'integer', | ||
'number', | ||
'json', | ||
'if', | ||
'get', | ||
'eq', | ||
'code' | ||
], function (name) { | ||
formatic.plugin('type-' + name, {type: name}); | ||
}); | ||
}; |
@@ -9,2 +9,13 @@ 'use strict'; | ||
formatic.onPlugin(function (plugin) { | ||
if (plugin.name.indexOf('view-') === 0) { | ||
var type = plugin.name.substring('view-'.length); | ||
if (plugin.view) { | ||
views[type] = plugin.view; | ||
} else { | ||
throw new Error('View created without view property: ' + plugin.name); | ||
} | ||
} | ||
}); | ||
formatic.method('view', function (name, view) { | ||
@@ -68,3 +79,3 @@ | ||
field = field || this.root; | ||
field = field || this.run(this.root, this.data); | ||
@@ -82,12 +93,5 @@ var view = views[field.type]; | ||
if (field.key) { | ||
var onChange = function () { | ||
throw new Error('No onChange hook defined.'); | ||
}; | ||
if (formatic.hasMethod('onChangeComponent')) { | ||
onChange = formatic.onChangeComponent.bind(null, this.onChange.bind(this, field)); | ||
} | ||
props.onChange = onChange; | ||
var type = formatic.type(field.type); | ||
if (type && type.hasMethod('formatField')) { | ||
field.value = type.formatField(field.value); | ||
} | ||
@@ -94,0 +98,0 @@ |
@@ -5,6 +5,8 @@ 'use strict'; | ||
formatic.plugin(require('./core-data')); | ||
formatic.plugin(require('./core-type')); | ||
formatic.plugin(require('./core-types')); | ||
formatic.plugin(require('./core-view')); | ||
formatic.plugin('core-data'); | ||
formatic.plugin('core-meta'); | ||
formatic.plugin('core-validation'); | ||
formatic.plugin('core-type'); | ||
formatic.plugin('core-types'); | ||
formatic.plugin('core-view'); | ||
}; |
@@ -5,4 +5,6 @@ 'use strict'; | ||
module.exports = function (formatic) { | ||
module.exports = function (formatic, plugin) { | ||
plugin.React = React; | ||
formatic.method('attachComponent', function (component, node, done) { | ||
@@ -19,11 +21,2 @@ return React.renderComponent(component, node, done); | ||
}); | ||
formatic.method('onChangeComponent', function (onChange, event) { | ||
if (event && event.target) { | ||
onChange(event.target.value); | ||
} else { | ||
onChange(event); | ||
} | ||
}); | ||
}; |
@@ -5,14 +5,30 @@ 'use strict'; | ||
var Formatic = require('../formatic'); | ||
module.exports = function (formatic) { | ||
formatic.plugin(Formatic.plugins.core); | ||
formatic.plugin(Formatic.plugins.terse); | ||
formatic.plugin(Formatic.plugins.readOnly); | ||
formatic.plugin(Formatic.plugins.reactViewer); | ||
formatic.plugin('core'); | ||
formatic.plugin('terse'); | ||
formatic.plugin('read-only'); | ||
formatic.plugin('required'); | ||
formatic.plugin('react-viewer'); | ||
_.each(Formatic.views.react, function (view, key) { | ||
formatic.view(key, view); | ||
_.each([ | ||
'form', | ||
'field', | ||
'text', | ||
'textarea', | ||
'pretty-textarea', | ||
'password', | ||
'select', | ||
'dropdown', | ||
'checkbox', | ||
'boolean-checkbox', | ||
'string', | ||
'float', | ||
'integer', | ||
'number', | ||
'json', | ||
'code' | ||
], function (name) { | ||
formatic.plugin('view-' + name, {type: name}); | ||
}); | ||
}; |
@@ -76,7 +76,7 @@ 'use strict'; | ||
var parts = field.split(':'); | ||
return { | ||
return next({ | ||
type: parts[0], | ||
key: parts[1], | ||
value: parts[2] | ||
}; | ||
}); | ||
} | ||
@@ -83,0 +83,0 @@ } |
'use strict'; | ||
var Formatic = require('../formatic'); | ||
module.exports = function (formatic) { | ||
formatic.config({ | ||
typeMap: { | ||
'unicode': 'text', | ||
'str': 'text', | ||
'text': 'textarea', | ||
'decimal': 'float', | ||
'int': 'integer', | ||
'num': 'integer', | ||
'number': 'integer' | ||
} | ||
formatic.plugin('react'); | ||
formatic.config('core-type.typeMap', { | ||
'unicode': 'text', | ||
'str': 'text', | ||
'text': 'textarea', | ||
'decimal': 'float', | ||
'int': 'integer', | ||
'num': 'integer', | ||
'number': 'integer', | ||
'select': 'dropdown', | ||
'file': 'text', | ||
'checkbox': 'checkbox-boolean', | ||
'datetime': 'text' | ||
}); | ||
formatic.plugin(Formatic.plugins.react); | ||
formatic.replaceMethod('splitKey', function (key) { | ||
return key; | ||
return [key]; | ||
//return key.split('__'); | ||
}); | ||
}; |
'use strict'; | ||
module.exports = { | ||
module.exports = function (formatic, plugin) { | ||
compile: function (field) { | ||
plugin.compileField = function (field) { | ||
@@ -10,3 +10,3 @@ field.value = field.value || ''; | ||
return field; | ||
} | ||
}; | ||
}; |
'use strict'; | ||
module.exports = { | ||
module.exports = function (formatic, plugin) { | ||
hasFields: true, | ||
plugin.hasFields = true; | ||
eval: function (field, data, run) { | ||
plugin.evalField = function (field, data, run) { | ||
@@ -15,3 +15,3 @@ var fields = run(field.fields); | ||
}; | ||
} | ||
}; | ||
}; |
'use strict'; | ||
var _ = require('underscore'); | ||
module.exports = function (formatic, plugin) { | ||
module.exports = function (formatic) { | ||
plugin.hasFields = true; | ||
formatic.form.wrap('compile', function (next, field) { | ||
plugin.compileField = function (field, compile) { | ||
if (typeof field === 'undefined') { | ||
field = { | ||
type: 'form', | ||
fields: [] | ||
}; | ||
} else if (_.isArray(field)) { | ||
field = { | ||
type: 'form', | ||
fields: field | ||
}; | ||
} else if (field.type !== 'form') { | ||
field = { | ||
type: 'form', | ||
fields: [field] | ||
}; | ||
} | ||
field.fields = compile(field.fields); | ||
return next(field); | ||
}); | ||
return { | ||
hasFields: true, | ||
compile: function (field, compile) { | ||
field.fields = compile(field.fields); | ||
return field; | ||
} | ||
return field; | ||
}; | ||
}; |
'use strict'; | ||
module.exports = function (formatic) { | ||
module.exports = function (formatic, plugin) { | ||
return { | ||
plugin.evalField = function (field, data) { | ||
eval: function (field, data) { | ||
return { | ||
type: 'value', | ||
value: formatic.getObject(data, field.value) | ||
}; | ||
} | ||
return { | ||
type: 'value', | ||
value: formatic.getObject(data, field.value) | ||
}; | ||
}; | ||
}; |
'use strict'; | ||
module.exports = { | ||
module.exports = function (formatic, plugin) { | ||
hasFields: true, | ||
plugin.hasFields = true; | ||
eval: function (field, data, run) { | ||
plugin.evalField = function (field, data, run) { | ||
@@ -16,3 +16,3 @@ var test = run(field.fields[0]); | ||
} | ||
} | ||
}; | ||
}; |
'use strict'; | ||
module.exports = { | ||
// Just a placeholder for now. | ||
module.exports = function () { | ||
// just a placeholder | ||
}; |
@@ -5,6 +5,10 @@ 'use strict'; | ||
module.exports = { | ||
module.exports = function (formatic, plugin) { | ||
compile: function (field) { | ||
plugin.compileField = function (field) { | ||
if (field.isWrapped) { | ||
return field; | ||
} | ||
field.value = field.value || ''; | ||
@@ -36,16 +40,8 @@ | ||
var valueIndex = -1; | ||
_.find(field.choices, function (choice, i) { | ||
if (choice.value === field.value) { | ||
valueIndex = i; | ||
return; | ||
} | ||
}); | ||
if (valueIndex === -1 && field.choices.length > 0) { | ||
field.value = field.choices[0].value; | ||
} | ||
return field; | ||
} | ||
return { | ||
type: 'field', | ||
fields: [field], | ||
isWrapped: true | ||
}; | ||
}; | ||
}; |
'use strict'; | ||
module.exports = { | ||
module.exports = function (formatic, plugin) { | ||
compile: function (field) { | ||
plugin.compileField = function (field) { | ||
if (field.isWrapped) { | ||
return field; | ||
} | ||
field.value = field.value || ''; | ||
return field; | ||
} | ||
return { | ||
type: 'field', | ||
fields: [field], | ||
isWrapped: true | ||
}; | ||
}; | ||
}; |
215
lib/utils.js
@@ -0,3 +1,6 @@ | ||
/*global escape,unescape*/ | ||
'use strict'; | ||
//var _ = require('underscore'); | ||
var utils = {}; | ||
@@ -44,4 +47,6 @@ | ||
obj = obj || {}; | ||
if (obj.hook) { | ||
return; | ||
return obj; | ||
} | ||
@@ -84,3 +89,3 @@ | ||
obj.hasMethod = function (name) { | ||
return name in hooks; | ||
return name in hooks || (name in obj && typeof obj[name] === 'function'); | ||
}; | ||
@@ -101,4 +106,210 @@ | ||
return obj; | ||
}; | ||
// utils.arrayContains = function (array, target, equals) { | ||
// return _.any(array, function (value) { | ||
// return equals ? equals(target, value) : (target === value); | ||
// }); | ||
// }; | ||
// | ||
// utils.arrayDiff = function (a1, a2, equals) { | ||
// return _.filter(a1, function (value) { | ||
// return !utils.arrayContains(a2, value, equals); | ||
// }); | ||
// }; | ||
var textPart = function (value, type) { | ||
type = type || 'text'; | ||
return { | ||
type: type, | ||
value: value | ||
}; | ||
}; | ||
utils.parseTextWithTags = function (value) { | ||
value = value || ''; | ||
var parts = value.split('{{'); | ||
var frontPart = []; | ||
if (parts[0] !== '') { | ||
frontPart = [ | ||
textPart(parts[0]) | ||
]; | ||
} | ||
parts = frontPart.concat( | ||
parts.slice(1).map(function (part) { | ||
if (part.indexOf('}}') >= 0) { | ||
return [ | ||
textPart(part.substring(0, part.indexOf('}}')), 'tag'), | ||
textPart(part.substring(part.indexOf('}}') + 2)) | ||
]; | ||
} else { | ||
return textPart('{{' + part, 'text'); | ||
} | ||
}) | ||
); | ||
return [].concat.apply([], parts); | ||
}; | ||
var SAFE_CONTROL_CODES = [ | ||
0x80, | ||
0x81, | ||
0x82, | ||
0x83, | ||
0x84, | ||
0x86, | ||
0x87, | ||
0x88, | ||
0x89, | ||
0x8a, | ||
0x8b, | ||
0x8c, | ||
0x8d, | ||
0x8e, | ||
0x8f, | ||
0x90, | ||
0x91, | ||
0x92, | ||
0x93, | ||
0x94, | ||
0x95, | ||
0x96, | ||
0x97, | ||
0x98, | ||
0x99, | ||
0x9a, | ||
0x9b, | ||
0x9c, | ||
0x9d, | ||
0x9e, | ||
0x9f | ||
]; | ||
var CONTROL_CODE_INDEX = {}; | ||
SAFE_CONTROL_CODES.forEach(function (code, i) { | ||
CONTROL_CODE_INDEX[code] = i; | ||
}); | ||
var toUtfBytes = function (str) { | ||
var utf8 = unescape(encodeURIComponent(str)); | ||
var arr = []; | ||
for (var i = 0; i < utf8.length; i++) { | ||
arr.push(utf8.charCodeAt(i)); | ||
} | ||
return arr; | ||
}; | ||
var fromUtfBytes = function (uintArray) { | ||
var encodedString = String.fromCharCode.apply(null, uintArray), | ||
decodedString = decodeURIComponent(escape(encodedString)); | ||
return decodedString; | ||
}; | ||
utils.hexToUnicode = function (message) { | ||
message = message.toUpperCase(); | ||
return message.split('').map(function (char) { | ||
var code = char.charCodeAt(0); | ||
if (code >= '0'.charCodeAt(0) && code <= '9'.charCodeAt(0)) { | ||
code = code - '0'.charCodeAt(0); | ||
return String.fromCharCode(SAFE_CONTROL_CODES[code]); | ||
} else if (code >= 'A'.charCodeAt(0) && code <= 'F'.charCodeAt(0)) { | ||
code = code - 'A'.charCodeAt(0); | ||
return String.fromCharCode(SAFE_CONTROL_CODES[code + 10]); | ||
} | ||
}).join(''); | ||
}; | ||
utils.unicodeToHex = function (message) { | ||
return message.split('').map(function (char) { | ||
var code = char.charCodeAt(0); | ||
code = CONTROL_CODE_INDEX[code]; | ||
if (code < 10) { | ||
return String.fromCharCode(code + '0'.charCodeAt(0)); | ||
} else { | ||
return String.fromCharCode(code - 10 + 'A'.charCodeAt(0)); | ||
} | ||
}).join(''); | ||
}; | ||
utils.stringToHex = function (string) { | ||
return toUtfBytes(string).map(function (code) { | ||
return code.toString(16); | ||
}).join(''); | ||
}; | ||
utils.hexToString = function (hex) { | ||
var bytes = hex.match(/.{1,2}/g).map(function (hex) { | ||
return parseInt(hex, 16); | ||
}); | ||
return fromUtfBytes(bytes); | ||
}; | ||
utils.hideUnicodeMessage = function (message) { | ||
return utils.hexToUnicode(utils.stringToHex(message)); | ||
}; | ||
utils.unhideUnicodeMessage = function (message) { | ||
return utils.hexToString(utils.unicodeToHex(message)); | ||
}; | ||
// utils.charDiff = function (a, b, step) { | ||
// var start = 0; | ||
// var end = a.length; | ||
// if (step > 0 || typeof step === 'undefined') { | ||
// step = 1; | ||
// } else { | ||
// step = -1; | ||
// start = a.length - 1; | ||
// end = -1; | ||
// } | ||
// var i; | ||
// for (i = start; i !== end; i = i + step) { | ||
// if (a[i] !== b[i]) { | ||
// if (step > 0) { | ||
// return i; | ||
// } | ||
// return i + 1; | ||
// } | ||
// } | ||
// return null; | ||
// }; | ||
// utils.diff = function (a, b) { | ||
// // Simple cases first. | ||
// if (a === b) { | ||
// return null; | ||
// } | ||
// if (b.substring(0, a.length) === a) { | ||
// return { | ||
// insert: b.substring(a.length), | ||
// delete: '', | ||
// pos: a.length | ||
// }; | ||
// } | ||
// if (b.substring(b.length - a.length) === a) { | ||
// return { | ||
// insert: b.substring(0, b.length - a.length), | ||
// delete: '', | ||
// pos: 0 | ||
// }; | ||
// } | ||
// if (a.substring(0, b.length) === b) { | ||
// return { | ||
// delete: a.substring(b.length), | ||
// insert: '', | ||
// pos: b.length | ||
// }; | ||
// } | ||
// if (a.substring(a.length - b.length) === b) { | ||
// return { | ||
// delete: a.substring(0, a.length - b.length), | ||
// insert: '', | ||
// pos: 0 | ||
// }; | ||
// } | ||
// }; | ||
module.exports = utils; |
@@ -5,10 +5,20 @@ 'use strict'; | ||
var R = React.DOM; | ||
var _ = require('underscore'); | ||
module.exports = React.createClass({ | ||
module.exports = function (formatic, plugin) { | ||
render: function () { | ||
return R.pre({className: 'zf-field'}, | ||
this.props.field.value | ||
); | ||
} | ||
}); | ||
plugin.view = React.createClass({ | ||
render: function () { | ||
var className = 'field code-field'; | ||
if (plugin.config.className) { | ||
className += ' ' + plugin.config.className; | ||
} | ||
return R.pre(_.extend({className: className}, plugin.config.attributes), | ||
this.props.field.value | ||
); | ||
} | ||
}); | ||
}; |
@@ -5,12 +5,23 @@ 'use strict'; | ||
var R = React.DOM; | ||
var _ = require('underscore'); | ||
module.exports = React.createClass({ | ||
module.exports = function (formatic, plugin) { | ||
render: function () { | ||
return R.form({className: 'zf-form'}, | ||
this.props.field.fields.map(function (field) { | ||
return this.props.form.component(field); | ||
}.bind(this)) | ||
); | ||
} | ||
}); | ||
plugin.view = React.createClass({ | ||
render: function () { | ||
var className = 'formatic'; | ||
if (plugin.config.className) { | ||
className += ' ' + plugin.config.className; | ||
} | ||
return R.form(_.extend({className: className}, plugin.config.attributes), | ||
this.props.field.fields.map(function (field) { | ||
return this.props.form.component(field); | ||
}.bind(this)) | ||
); | ||
} | ||
}); | ||
}; |
'use strict'; | ||
var React = require('react/addons'); | ||
var React = require('react'); | ||
var R = React.DOM; | ||
var _ = require('underscore'); | ||
var hasAncestor = function (child, parent) { | ||
if (child.parentNode === parent) { | ||
return true; | ||
} | ||
if (child.parentNode === child.parentNode) { | ||
return false; | ||
} | ||
if (child.parentNode === null) { | ||
return false; | ||
} | ||
return hasAncestor(child.parentNode, parent); | ||
}; | ||
module.exports = function (formatic, plugin) { | ||
var isOutside = function (nodeOut, nodeIn) { | ||
if (nodeOut === nodeIn) { | ||
return false; | ||
} | ||
if (hasAncestor(nodeOut, nodeIn)) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
plugin.view = React.createClass({ | ||
module.exports = React.createClass({ | ||
mixins: [require('./mixins/input-actions')], | ||
getInitialState: function () { | ||
return { | ||
open: false | ||
}; | ||
}, | ||
render: function () { | ||
onToggle: function () { | ||
this.setState({open: !this.state.open}); | ||
}, | ||
var className = plugin.config.className || ''; | ||
onClose: function () { | ||
this.setState({open: false}); | ||
}, | ||
var choices = this.props.field.choices; | ||
onClickDocument: function (event) { | ||
if (isOutside(event.target, this.refs.select.getDOMNode())) { | ||
this.onClose(); | ||
} | ||
}, | ||
if (!this.props.field.value) { | ||
choices = [{ | ||
value: '', | ||
label: '' | ||
}].concat(choices); | ||
} | ||
fixChoicesWidth: function () { | ||
this.setState({ | ||
choicesWidth: this.refs.active.getDOMNode().offsetWidth | ||
}); | ||
}, | ||
onResize: function () { | ||
this.fixChoicesWidth(); | ||
}, | ||
componentDidMount: function () { | ||
this.fixChoicesWidth(); | ||
document.addEventListener('click', this.onClickDocument); | ||
window.addEventListener('resize', this.onResize); | ||
}, | ||
componentWillUnmount: function () { | ||
document.removeEventListener('click', this.onClickDocument); | ||
window.removeEventListener('resize', this.onResize); | ||
}, | ||
render: function () { | ||
var selectedLabel = ''; | ||
var matchingLabels = this.props.field.choices.filter(function (choice) { | ||
return choice.value === this.props.field.value; | ||
}.bind(this)); | ||
if (matchingLabels.length > 0) { | ||
selectedLabel = matchingLabels[0].label; | ||
return R.select(_.extend({ | ||
className: className, | ||
onChange: this.onChange, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
name: this.props.field.key, | ||
value: this.props.field.value //, | ||
//onFocus: this.props.actions.focus | ||
}, plugin.config.attributes), | ||
choices.map(function (choice) { | ||
return R.option({ | ||
value: choice.value | ||
}, choice.label); | ||
}.bind(this)) | ||
); | ||
} | ||
return R.div({key: this.props.field.key, className: 'zf-field'}, | ||
R.label({}, | ||
this.props.field.label | ||
), | ||
// R.select({onChange: this.props.onChange, name: this.props.field.key, value: this.props.field.value}, | ||
// this.props.field.choices.map(function (choice) { | ||
// return R.option({ | ||
// value: choice.value | ||
// }, choice.label); | ||
// }.bind(this)) | ||
// ), | ||
R.div({className: 'zf-select', ref: 'select'}, | ||
R.div({className: 'zf-select-active', ref: 'active', onClick: this.onToggle}, selectedLabel), | ||
R.div({className: 'zf-select-arrow ' + (this.state.open ? 'zf-open' : 'zf-closed'), onClick: this.onToggle}), | ||
React.addons.CSSTransitionGroup({transitionName: 'zf-slide'}, | ||
this.state.open ? R.ul({ref: 'choices', className: 'zf-select-choices', style: {width: this.state.choicesWidth}}, | ||
this.props.field.choices.map(function (choice) { | ||
return R.li({ | ||
className: 'zf-select-choice', | ||
onClick: function () { | ||
this.setState({open: false}); | ||
this.props.onChange(choice.value); | ||
}.bind(this) | ||
}, choice.label); | ||
}.bind(this)) | ||
) : [] | ||
) | ||
) | ||
); | ||
} | ||
}); | ||
}); | ||
}; |
@@ -5,22 +5,28 @@ 'use strict'; | ||
var R = React.DOM; | ||
var _ = require('underscore'); | ||
module.exports = React.createClass({ | ||
module.exports = function (formatic, plugin) { | ||
render: function () { | ||
plugin.view = React.createClass({ | ||
var field = this.props.field; | ||
mixins: [require('./mixins/input-actions')], | ||
return R.div({key: field.key, className: 'zf-field'}, | ||
R.label({}, | ||
field.label | ||
), | ||
R.input({ | ||
render: function () { | ||
var className = plugin.config.className || ''; | ||
var field = this.props.field; | ||
return R.input(_.extend({ | ||
className: className, | ||
type: field.type, | ||
name: field.key, | ||
value: field.value, | ||
onChange: this.props.onChange, | ||
onChange: this.onChange, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur, | ||
readOnly: field.isReadOnly | ||
}) | ||
); | ||
} | ||
}); | ||
}, plugin.config.attributes)); | ||
} | ||
}); | ||
}; |
@@ -5,17 +5,26 @@ 'use strict'; | ||
var R = React.DOM; | ||
var _ = require('underscore'); | ||
module.exports = React.createClass({ | ||
render: function () { | ||
return R.div({key: this.props.field.key, className: 'zf-field'}, | ||
R.label({}, | ||
this.props.field.label | ||
), | ||
R.textarea({ | ||
rows: 5, | ||
name: this.props.field.key, | ||
value: this.props.field.value, | ||
onChange: this.props.onChange | ||
}) | ||
); | ||
} | ||
}); | ||
module.exports = function (formatic, plugin) { | ||
plugin.view = React.createClass({ | ||
mixins: [require('./mixins/input-actions')], | ||
render: function () { | ||
var className = plugin.config.className || ''; | ||
var field = this.props.field; | ||
return R.textarea(_.extend({ | ||
className: className, | ||
name: field.key, | ||
value: field.value, | ||
onChange: this.onChange, | ||
onFocus: this.onFocus, | ||
onBlur: this.onBlur | ||
}, plugin.config.attributes)); | ||
} | ||
}); | ||
}; |
{ | ||
"name": "formatic", | ||
"version": "0.0.1", | ||
"version": "0.0.3", | ||
"description": "Automatic, pluggable form generation", | ||
@@ -21,2 +21,3 @@ "main": "index.js", | ||
"gulp": "^3.8.0", | ||
"gulp-changed": "^0.4.0", | ||
"gulp-eslint": "^0.1.8", | ||
@@ -23,0 +24,0 @@ "gulp-load-plugins": "^0.5.3", |
# Formatic | ||
[![travis](https://travis-ci.org/zapier/formatic.svg?branch=master)](https://travis-ci.org/zapier/formatic) | ||
Automatic forms. | ||
@@ -37,14 +39,13 @@ | ||
Formatic is a forms builder builder. Thats not a typo. Formatic can't build | ||
anything until you tell it how to build something. You tell it how to build | ||
things by providing plugins that configure it. | ||
Formatic is a configurable, pluggable forms builder. Out of the box, it can | ||
build React-based forms, but you can configure it and add/remove plugins to make | ||
it work exactly how you want. | ||
## Using formatic (quick version) | ||
If you don't want to extend formatic, you'll use it with an included plugin | ||
like this: | ||
If you don't want to extend formatic, you'll use it like this: | ||
```js | ||
// Get a formatic instance configured with the react plugin. | ||
var formatic = require('formatic')('react'); | ||
// Get the default formatic instance. | ||
var formatic = require('formatic'); | ||
@@ -85,3 +86,3 @@ // Get a new form. | ||
## Vanilla formatic | ||
<!-- ## Vanilla formatic | ||
@@ -292,2 +293,2 @@ If you require formatic: | ||
of that. In fact, the types plugin is a plugin that adds in "mini plugins" to | ||
support various form elements and cool dynamic stuff. | ||
support various form elements and cool dynamic stuff. --> |
@@ -10,3 +10,3 @@ 'use strict'; | ||
gulp.task('server-live-app', function () { | ||
gulp.task('server-live-app', function (done) { | ||
var app = express(); | ||
@@ -19,9 +19,11 @@ | ||
console.log('app server listening on %d', APP_PORT); | ||
done(); | ||
}); | ||
}); | ||
gulp.task('server-live-reload', function () { | ||
gulp.task('server-live-reload', function (done) { | ||
var lr = tinylr(); | ||
lr.listen(LR_PORT, function () { | ||
console.log('reload server listening on %d', LR_PORT); | ||
done(); | ||
}); | ||
@@ -28,0 +30,0 @@ gulp.watch(['index.html', 'style/**/*.css', 'formatic-dev.js'], function (evt) { |
derived fields | ||
buttons | ||
dependent fields | ||
dependent fields (like mutually exclusive/inclusive fields) | ||
multiple nodes (return react components) | ||
@@ -22,1 +22,4 @@ clean up construction api | ||
get bundle smaller!!! | ||
config vs meta!!! | ||
default values? |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
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
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
64
292
2358422
17
31210