react-form-with-constraints-tools
Version:
Simple form validation for React
135 lines (134 loc) • 5.19 kB
JavaScript
import * as React from 'react';
import { instanceOf } from 'prop-types';
import { Async as _Async, FieldEvent, FieldFeedback as _FieldFeedback, FieldFeedbacks as _FieldFeedbacks, FieldFeedbackType, FormWithConstraints, isHTMLInput } from 'react-form-with-constraints';
function beautifulStringify(obj, space) {
let str = JSON.stringify(obj, (_key, value) => (value === undefined ? '__undefined__' : value), space);
str = str.replace(/"__undefined__"/g, 'undefined');
str = str.replace(/"([^"]+)":/g, '$1:');
str = str.replace(/: "(.*[\\"].*)",/g, ': $1,');
str = str.replace(/\\"/g, '"');
return str;
}
function normalizeFieldElementProperty(fields) {
return fields.map(field => {
const { element, ...otherProps } = field;
return element
? {
element: isHTMLInput(element) ? element.outerHTML : element.props,
...otherProps
}
: field;
});
}
export class DisplayFields extends React.Component {
constructor() {
super(...arguments);
this.reRender = () => {
this.forceUpdate();
};
}
componentDidMount() {
this.context.form.fieldsStore.addListener(FieldEvent.Added, this.reRender);
this.context.form.fieldsStore.addListener(FieldEvent.Removed, this.reRender);
this.context.form.addFieldDidValidateEventListener(this.reRender);
this.context.form.addFieldDidResetEventListener(this.reRender);
}
componentWillUnmount() {
this.context.form.fieldsStore.removeListener(FieldEvent.Added, this.reRender);
this.context.form.fieldsStore.removeListener(FieldEvent.Removed, this.reRender);
this.context.form.removeFieldDidValidateEventListener(this.reRender);
this.context.form.removeFieldDidResetEventListener(this.reRender);
}
render() {
let str = beautifulStringify(normalizeFieldElementProperty(this.context.form.fieldsStore.fields), 2);
str = str.replace(/{\s+key: (.*),\s+type: (.*),\s+show: (.*)\s+}/g, '{ key: $1, type: $2, show: $3 }');
return str;
}
}
DisplayFields.contextTypes = {
form: instanceOf(FormWithConstraints).isRequired
};
export class FieldFeedbacks extends _FieldFeedbacks {
render() {
const { for: fieldName, stop } = this.props;
let attr = '';
if (fieldName)
attr += `for="${fieldName}" `;
attr += `stop="${stop}"`;
return (React.createElement(React.Fragment, null,
React.createElement("li", null,
"key=\"",
this.key,
"\" ",
attr),
React.createElement("ul", null, super.render())));
}
}
export class FieldFeedback extends _FieldFeedback {
constructor() {
super(...arguments);
this.rootEl = null;
}
getTextDecoration() {
const { show } = this.state.validation;
let textDecoration = '';
switch (show) {
case false:
textDecoration = 'line-through';
break;
case undefined:
textDecoration = 'line-through dotted';
break;
default:
textDecoration = '';
}
return textDecoration;
}
render() {
const { key, type } = this.state.validation;
return (React.createElement("li", { ref: rootEl => (this.rootEl = rootEl) },
React.createElement("span", { style: { textDecoration: this.getTextDecoration() } },
"key=\"",
key,
"\" type=\"",
type,
"\""),
' ',
super.render()));
}
componentDidUpdate() {
const fieldFeedbackSpans = this.rootEl.querySelectorAll('[data-feedback]');
fieldFeedbackSpans.forEach(fieldFeedbackSpan => {
fieldFeedbackSpan.style.display = 'inline';
});
const li = this.rootEl.closest('li.async');
if (li !== null) {
const async = li.querySelector('span[style]');
async.style.textDecoration = this.getTextDecoration();
}
const { type } = this.state.validation;
if (type === FieldFeedbackType.WhenValid) {
const span = this.rootEl.querySelector('span[style]');
const whenValid = this.rootEl.querySelector(`span.${this.props.classes.valid}`);
span.style.textDecoration = whenValid !== null ? '' : 'line-through';
}
}
}
export class Async extends _Async {
constructor() {
super(...arguments);
this.rootEl = null;
}
static getTextDecoration() {
return 'line-through dotted';
}
componentDidUpdate() {
const async = this.rootEl.querySelector('span[style]');
async.style.textDecoration = Async.getTextDecoration();
}
render() {
return (React.createElement("li", { className: "async", ref: rootEl => (this.rootEl = rootEl) },
React.createElement("span", { style: { textDecoration: Async.getTextDecoration() } }, "Async"),
React.createElement("ul", null, super.render())));
}
}