Repository navigation
Expand file tree
/
Copy pathtags.jsx
More file actions
139 lines (139 loc) · 5.87 KB
/
Copy pathtags.jsx
File metadata and controls
139 lines (139 loc) · 5.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* Linked React components for building forms implementing React 0.14 valueLink semantic.
*
* WTFPL License, (c) 2016 Vlad Balin, Volicon.
*/
import * as React from 'react';
const setValue = (x, e) => e.target.value;
const setBoolValue = (x, e) => Boolean(e.target.checked);
/**
* Wrapper for standard <input/> to be compliant with React 0.14 valueLink semantic.
* Simple supports for link validation - adds 'invalid' class if link has an error.
*
* <input type="checkbox" checkedLink={ linkToBool } />
* <input type="radio" valueLink={ linkToSelectedValue } value="option1value" />
* <input type="text" valueLink={ linkToString } />
*/
function validationClasses(props, value, error) {
let classNames = props.className ? [props.className] : [];
if (error) {
classNames.push(props.invalidClass || 'invalid');
if (value === '') {
classNames.push(props.requiredClass || 'required');
}
}
return classNames.join(' ');
}
export function Input(props) {
const { valueLink, checkedLink, ...rest } = props, type = props.type, link = valueLink || checkedLink;
switch (type) {
case 'checkbox':
return <input {...rest} checked={Boolean(link.value)} onChange={link.action(setBoolValue)}/>;
case 'radio':
return <input {...rest} checked={link.value === props.value} onChange={e => { e.target.checked && link.set(props.value); }}/>;
default:
return <input {...rest} className={validationClasses(rest, valueLink.value, valueLink.error)} value={String(valueLink.value)} onChange={valueLink.action(setValue)}/>;
}
}
;
export const isRequired = x => x != null && x !== '';
isRequired.error = 'Required';
const emailPattern = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*@([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
export const isEmail = x => Boolean(x.match(emailPattern));
isEmail.error = 'Should be valid email';
export class NumberInput extends React.Component {
constructor() {
super(...arguments);
this.onKeyPress = e => {
const { charCode } = e, { integer, positive } = this.props, allowed = (positive ? [] : [45]).concat(integer ? [] : [46]);
if (e.ctrlKey)
return;
if (charCode && // allow control characters
(charCode < 48 || charCode > 57) && // char is number
allowed.indexOf(charCode) < 0) {
e.preventDefault();
}
};
this.onChange = e => {
// Update local state...
const { value } = e.target;
this.setValue(value);
const asNumber = Number(value);
if (value && !isNaN(asNumber)) {
this.props.valueLink.update(x => {
// Update link if value is changed
if (asNumber !== Number(x)) {
return asNumber;
}
});
}
};
}
componentWillMount() {
// Initialize component state
this.setAndConvert(this.props.valueLink.value);
}
setValue(x) {
// We're not using native state in order to avoid race condition.
this.value = String(x);
this.error = this.value === '' || isNaN(Number(x));
this.forceUpdate();
}
setAndConvert(x) {
let value = Number(x);
if (this.props.positive) {
value = Math.abs(x);
}
if (this.props.integer) {
value = Math.round(value);
}
this.setValue(value);
}
componentWillReceiveProps(nextProps) {
const { valueLink: next } = nextProps;
if (Number(next.value) !== Number(this.value)) {
this.setAndConvert(next.value); // keep state being synced
}
}
render() {
const { valueLink, positive, integer, ...props } = this.props, error = valueLink.error || this.error;
return <input {...props} type="text" className={validationClasses(props, this.value, error)} value={this.value} onKeyPress={this.onKeyPress} onChange={this.onChange}/>;
}
}
/**
* Wrapper for standard <textarea/> to be compliant with React 0.14 valueLink semantic.
* Simple supports for link validation - adds 'invalid' class if link has an error.
*
* <TextArea valueLink={ linkToText } />
*/
export const TextArea = ({ valueLink, ...props }) => (<textarea {...props} className={validationClasses(props, valueLink.value, valueLink.error)} value={valueLink.value} onChange={valueLink.action(setValue)}/>);
/**
* Wrapper for standard <select/> to be compliant with React 0.14 valueLink semantic.
* Regular <option/> tags must be used:
*
* <Select valueLink={ linkToSelectedValue }>
* <option value="a">A</option>
* <option value="b">B</option>
* </Select>
*/
export const Select = ({ valueLink, children, ...props }) => (<select {...props} value={valueLink.value} onChange={valueLink.action(setValue)}>
{children}
</select>);
/**
* Simple custom <Radio/> tag implementation. Can be easily styled.
* Intended to be used with offhand bool link:
*
* <Radio checkedLink={ linkToValue.equals( optionValue ) />
*/
export const Radio = ({ className = 'radio', checkedLink, children }) => (<div className={className + (checkedLink.value ? ' selected' : '')} onClick={checkedLink.action(() => true)}>
{children}
</div>);
/**
* Simple custom <Checkbox /> tag implementation.
* Takes any type of boolean link. Can be easily styled.
*
* <Checkbox checkedLink={ boolLink } />
*/
export const Checkbox = ({ className = 'checkbox', checkedLink, children }) => (<div className={className + (checkedLink.value ? ' selected' : '')} onClick={checkedLink.action(x => !x)}>
{children}
</div>);