@xeplr/ui-utils 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +25 -6
- package/src/confirm/confirm.js +175 -0
- package/src/dataImport/dataImport.js +32 -0
- package/src/dataImport/designs/DataImportSample.jsx +71 -0
- package/src/dataImport/designs/ImportHistorySample.jsx +130 -0
- package/src/dataImport/designs/dataImport.css +98 -0
- package/src/dataImport/designs/index.js +2 -0
- package/src/dataImport/pages.jsx +17 -0
- package/src/dataImport/useDataImportController.js +94 -0
- package/src/dataImport/useImportHistoryController.js +67 -0
- package/src/dataImport/validateDesign.js +68 -0
- package/src/dateField/dateField.js +71 -0
- package/src/dateField/designs/DateFieldSample.jsx +30 -0
- package/src/dateField/designs/dateField.css +60 -0
- package/src/dateField/designs/index.js +1 -0
- package/src/dateField/pages.jsx +10 -0
- package/src/dateField/useDateFieldController.js +77 -0
- package/src/dateField/validateDesign.js +3 -0
- package/src/dateRange/dateRange.js +273 -0
- package/src/dateRange/designs/DateRangeSample.jsx +138 -0
- package/src/dateRange/designs/dateRange.css +69 -0
- package/src/dateRange/designs/index.js +1 -0
- package/src/dateRange/pages.jsx +10 -0
- package/src/dateRange/useDateRangeController.js +118 -0
- package/src/dateRange/validateDesign.js +20 -0
- package/src/dropdown/designs/DropdownSample.jsx +151 -0
- package/src/dropdown/designs/dropdown.css +226 -0
- package/src/dropdown/designs/index.js +1 -0
- package/src/dropdown/dropdown.js +75 -0
- package/src/dropdown/pages.jsx +10 -0
- package/src/dropdown/useDropdownController.js +254 -0
- package/src/dropdown/validateDesign.js +4 -0
- package/src/fileUpload/validateDesign.js +27 -8
- package/src/gridDisplayer/designs/GridDisplayerSample.jsx +58 -0
- package/src/gridDisplayer/designs/gridDisplayer.css +89 -0
- package/src/gridDisplayer/designs/index.js +1 -0
- package/src/gridDisplayer/gridDisplayer.js +44 -0
- package/src/gridDisplayer/pages.jsx +10 -0
- package/src/gridDisplayer/useGridDisplayerController.js +59 -0
- package/src/gridDisplayer/validateDesign.js +4 -0
- package/src/index.js +177 -1
- package/src/numberField/designs/NumberFieldSample.jsx +52 -0
- package/src/numberField/designs/index.js +1 -0
- package/src/numberField/designs/numberField.css +66 -0
- package/src/numberField/numberField.js +105 -0
- package/src/numberField/pages.jsx +10 -0
- package/src/numberField/useNumberFieldController.js +116 -0
- package/src/numberField/validateDesign.js +3 -0
- package/src/progress/ProgressNotifier.jsx +118 -0
- package/src/progress/designs/progressNotifier.css +73 -0
- package/src/progress/progressSource.js +168 -0
- package/src/rangeField/designs/RangeFieldSample.jsx +25 -0
- package/src/rangeField/designs/index.js +1 -0
- package/src/rangeField/designs/rangeField.css +26 -0
- package/src/rangeField/pages.jsx +10 -0
- package/src/rangeField/rangeField.js +29 -0
- package/src/rangeField/useRangeFieldController.js +52 -0
- package/src/rangeField/validateDesign.js +3 -0
- package/src/snackbar/snackbar.js +6 -3
- package/src/textField/designs/TextFieldSample.jsx +75 -0
- package/src/textField/designs/index.js +1 -0
- package/src/textField/designs/textField.css +92 -0
- package/src/textField/pages.jsx +10 -0
- package/src/textField/textField.js +65 -0
- package/src/textField/useTextFieldController.js +101 -0
- package/src/textField/validateDesign.js +3 -0
- package/src/theme/default.theme.json +934 -0
- package/src/theme/index.js +155 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import './textField.css';
|
|
2
|
+
|
|
3
|
+
// The default LOOK. Presentation only — every value it renders comes in as a
|
|
4
|
+
// prop, so an app that wants a different one supplies its own design and this
|
|
5
|
+
// is never loaded.
|
|
6
|
+
//
|
|
7
|
+
// Nothing here is hard-coded to a colour or an edge: the CSS reads
|
|
8
|
+
// --xeplr-input-* custom properties, so "bottom rule only" is a theme setting
|
|
9
|
+
// rather than a fork of this file.
|
|
10
|
+
export default function TextFieldSample({
|
|
11
|
+
type, raw, error, remaining, placeholder, disabled, required, maxLength, rows,
|
|
12
|
+
label, help, handleChange, handleBlur, handleKeyDown, handleClear
|
|
13
|
+
}) {
|
|
14
|
+
var multiline = type === 'multiline';
|
|
15
|
+
var search = type === 'search';
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className={'xeplr-textfield' + (error ? ' xeplr-textfield-invalid' : '')}>
|
|
19
|
+
{label && (
|
|
20
|
+
<label className="xeplr-textfield-label">
|
|
21
|
+
{label}{required && <span className="xeplr-textfield-required" aria-hidden="true"> *</span>}
|
|
22
|
+
</label>
|
|
23
|
+
)}
|
|
24
|
+
|
|
25
|
+
<div className="xeplr-textfield-control">
|
|
26
|
+
{multiline ? (
|
|
27
|
+
<textarea
|
|
28
|
+
className="xeplr-textfield-input"
|
|
29
|
+
value={raw}
|
|
30
|
+
rows={rows || 4}
|
|
31
|
+
placeholder={placeholder}
|
|
32
|
+
disabled={disabled}
|
|
33
|
+
maxLength={maxLength}
|
|
34
|
+
aria-invalid={error ? 'true' : undefined}
|
|
35
|
+
onChange={handleChange}
|
|
36
|
+
onBlur={handleBlur}
|
|
37
|
+
/>
|
|
38
|
+
) : (
|
|
39
|
+
<input
|
|
40
|
+
className="xeplr-textfield-input"
|
|
41
|
+
type={search ? 'search' : 'text'}
|
|
42
|
+
value={raw}
|
|
43
|
+
placeholder={placeholder}
|
|
44
|
+
disabled={disabled}
|
|
45
|
+
maxLength={maxLength}
|
|
46
|
+
aria-invalid={error ? 'true' : undefined}
|
|
47
|
+
onChange={handleChange}
|
|
48
|
+
onBlur={handleBlur}
|
|
49
|
+
onKeyDown={handleKeyDown}
|
|
50
|
+
/>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{/* Only when there is something to clear — a permanently visible ✕ on
|
|
54
|
+
an empty box is a control that does nothing most of the time. */}
|
|
55
|
+
{search && raw && !disabled && (
|
|
56
|
+
<button type="button" className="xeplr-textfield-clear" onClick={handleClear} title="Clear">✕</button>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{/* The error REPLACES the help text rather than stacking under it: two
|
|
61
|
+
lines of guidance where one contradicts the other is worse than one. */}
|
|
62
|
+
{error ? (
|
|
63
|
+
<p className="xeplr-textfield-error">{error}</p>
|
|
64
|
+
) : help ? (
|
|
65
|
+
<p className="xeplr-textfield-help">{help}</p>
|
|
66
|
+
) : null}
|
|
67
|
+
|
|
68
|
+
{/* Shown near the limit, not always. A counter reading 486 of 500 from
|
|
69
|
+
the moment the field appears is noise. */}
|
|
70
|
+
{remaining != null && maxLength != null && remaining <= Math.max(10, maxLength * 0.1) && (
|
|
71
|
+
<p className="xeplr-textfield-count">{remaining} left</p>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as TextFieldSample } from './TextFieldSample.jsx';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/* Every value here comes from a --xeplr-input-* custom property, with the
|
|
2
|
+
shipped default as its fallback. That is what makes "border: bottom only"
|
|
3
|
+
a theme setting rather than a fork of this stylesheet — see the `input`
|
|
4
|
+
section of default.theme.json and inputStyleVars(). */
|
|
5
|
+
|
|
6
|
+
/* The gap is the control's OWN spacing when it stands alone in a form. Inside
|
|
7
|
+
a grid the row owns the spacing, so a host can zero it — see .dsh-prop. */
|
|
8
|
+
.xeplr-textfield { display: block; margin-bottom: var(--xeplr-input-text-gap, 12px); }
|
|
9
|
+
|
|
10
|
+
/* The label takes the FULL Font block, so anything the theme accepts for it
|
|
11
|
+
actually renders — underline included. A property the theme takes and the
|
|
12
|
+
stylesheet ignores is a setting that silently does nothing. */
|
|
13
|
+
.xeplr-textfield-label {
|
|
14
|
+
display: block;
|
|
15
|
+
font-family: var(--xeplr-input-text-label-family, inherit);
|
|
16
|
+
font-size: var(--xeplr-input-text-label-size, 11px);
|
|
17
|
+
font-weight: var(--xeplr-input-text-label-weight, 600);
|
|
18
|
+
font-style: var(--xeplr-input-text-label-style, normal);
|
|
19
|
+
font-variant: var(--xeplr-input-text-label-variant, normal);
|
|
20
|
+
color: var(--xeplr-input-text-label-color, #6b6a63);
|
|
21
|
+
line-height: var(--xeplr-input-text-label-line-height, 1.3);
|
|
22
|
+
letter-spacing: var(--xeplr-input-text-label-spacing, .03em);
|
|
23
|
+
text-align: var(--xeplr-input-text-label-align, left);
|
|
24
|
+
text-transform: var(--xeplr-input-text-label-transform, uppercase);
|
|
25
|
+
text-decoration: var(--xeplr-input-text-label-decoration, none);
|
|
26
|
+
opacity: var(--xeplr-input-text-label-opacity, 1);
|
|
27
|
+
margin-bottom: 5px;
|
|
28
|
+
}
|
|
29
|
+
.xeplr-textfield-required { color: var(--xeplr-input-text-invalid-color, #c2372e); }
|
|
30
|
+
|
|
31
|
+
.xeplr-textfield-control { position: relative; display: flex; }
|
|
32
|
+
|
|
33
|
+
.xeplr-textfield-input {
|
|
34
|
+
width: 100%;
|
|
35
|
+
font: inherit;
|
|
36
|
+
font-size: var(--xeplr-input-text-font-size, 12.5px);
|
|
37
|
+
color: var(--xeplr-input-text-color, #1a1a19);
|
|
38
|
+
background: var(--xeplr-input-text-bg, #fff);
|
|
39
|
+
padding: var(--xeplr-input-text-padding, 7px 9px);
|
|
40
|
+
/* Each edge on its own, so a theme can keep one and drop three. */
|
|
41
|
+
border-style: var(--xeplr-input-text-border-style, solid);
|
|
42
|
+
border-color: var(--xeplr-input-text-border-color, #e2e1d9);
|
|
43
|
+
border-top-width: var(--xeplr-input-text-border-top, 1px);
|
|
44
|
+
border-right-width: var(--xeplr-input-text-border-right, 1px);
|
|
45
|
+
border-bottom-width: var(--xeplr-input-text-border-bottom, 1px);
|
|
46
|
+
border-left-width: var(--xeplr-input-text-border-left, 1px);
|
|
47
|
+
border-radius: var(--xeplr-input-text-radius, 6px);
|
|
48
|
+
transition: border-color .12s ease, box-shadow .12s ease;
|
|
49
|
+
}
|
|
50
|
+
.xeplr-textfield-input:hover:not(:disabled) {
|
|
51
|
+
border-color: var(--xeplr-input-text-border-hover, #b6b4a8);
|
|
52
|
+
}
|
|
53
|
+
.xeplr-textfield-input:focus {
|
|
54
|
+
outline: none;
|
|
55
|
+
border-color: var(--xeplr-input-text-border-focus, #2563eb);
|
|
56
|
+
box-shadow: 0 0 0 var(--xeplr-input-text-ring, 3px) var(--xeplr-input-text-ring-color, rgba(37, 99, 235, .15));
|
|
57
|
+
}
|
|
58
|
+
.xeplr-textfield-input:disabled { opacity: .55; cursor: not-allowed; }
|
|
59
|
+
.xeplr-textfield-input::placeholder { color: var(--xeplr-input-text-placeholder, #9d9b92); }
|
|
60
|
+
|
|
61
|
+
textarea.xeplr-textfield-input { resize: vertical; min-height: 64px; }
|
|
62
|
+
|
|
63
|
+
.xeplr-textfield-invalid .xeplr-textfield-input {
|
|
64
|
+
border-color: var(--xeplr-input-text-invalid-color, #c2372e);
|
|
65
|
+
}
|
|
66
|
+
.xeplr-textfield-invalid .xeplr-textfield-input:focus {
|
|
67
|
+
box-shadow: 0 0 0 var(--xeplr-input-text-ring, 3px) var(--xeplr-input-text-invalid-ring, rgba(194, 55, 46, .15));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.xeplr-textfield-clear {
|
|
71
|
+
position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
|
|
72
|
+
border: none; background: none; cursor: pointer;
|
|
73
|
+
color: var(--xeplr-input-text-label-color, #6b6a63);
|
|
74
|
+
font-size: 11px; padding: 2px 4px; border-radius: 4px;
|
|
75
|
+
}
|
|
76
|
+
.xeplr-textfield-clear:hover { background: rgba(0, 0, 0, .06); }
|
|
77
|
+
|
|
78
|
+
.xeplr-textfield-help,
|
|
79
|
+
.xeplr-textfield-error,
|
|
80
|
+
.xeplr-textfield-count {
|
|
81
|
+
margin: 5px 0 0;
|
|
82
|
+
font-size: var(--xeplr-input-text-help-size, 11px);
|
|
83
|
+
line-height: 1.5;
|
|
84
|
+
}
|
|
85
|
+
.xeplr-textfield-help {
|
|
86
|
+
font-family: var(--xeplr-input-text-help-family, inherit);
|
|
87
|
+
font-style: var(--xeplr-input-text-help-style, normal);
|
|
88
|
+
text-decoration: var(--xeplr-input-text-help-decoration, none);
|
|
89
|
+
color: var(--xeplr-input-text-help-color, #6b6a63);
|
|
90
|
+
}
|
|
91
|
+
.xeplr-textfield-error { color: var(--xeplr-input-text-invalid-color, #c2372e); }
|
|
92
|
+
.xeplr-textfield-count { color: var(--xeplr-input-text-label-color, #6b6a63); text-align: right; }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useTextFieldController } from './useTextFieldController.js';
|
|
2
|
+
import TextFieldSample from './designs/TextFieldSample.jsx';
|
|
3
|
+
import { useDesignValidator } from '../fileUpload/validateDesign.js';
|
|
4
|
+
import { TEXT_FIELD_RULES } from './validateDesign.js';
|
|
5
|
+
|
|
6
|
+
export function TextFieldPage(props) {
|
|
7
|
+
var controller = useTextFieldController(props);
|
|
8
|
+
var ref = useDesignValidator('TextFieldPage', TEXT_FIELD_RULES);
|
|
9
|
+
return <div ref={ref}><TextFieldSample {...controller} /></div>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextField — pure model. No React.
|
|
3
|
+
*
|
|
4
|
+
* The plain text input, and the two shapes that are the same control with a
|
|
5
|
+
* different edge: a multi-line one and a search one. Kept together because
|
|
6
|
+
* they are one thing to style — an app that wants a bottom-rule-only textbox
|
|
7
|
+
* wants a bottom-rule-only search box too, and splitting them is how those
|
|
8
|
+
* drift apart.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export var TEXT_TYPES = ['text', 'multiline', 'search'];
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* What the control accepts as a value. Anything nullish is the empty string:
|
|
15
|
+
* a controlled input handed `undefined` switches to uncontrolled mid-life and
|
|
16
|
+
* React warns about it, which is a bug that only appears once somebody clears
|
|
17
|
+
* a field.
|
|
18
|
+
*/
|
|
19
|
+
export function toValue(raw) {
|
|
20
|
+
return raw === null || raw === undefined ? '' : String(raw);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Trimming is a CHOICE, not a default. " " is a legitimate value in a search
|
|
25
|
+
* box and a mistake in a name field, and only the caller knows which.
|
|
26
|
+
*/
|
|
27
|
+
export function normalise(raw, options) {
|
|
28
|
+
var value = toValue(raw);
|
|
29
|
+
var opts = options || {};
|
|
30
|
+
if (opts.trim) value = value.trim();
|
|
31
|
+
if (opts.maxLength != null && value.length > opts.maxLength) value = value.slice(0, opts.maxLength);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Whether the value satisfies the constraints, and what to say if not.
|
|
37
|
+
*
|
|
38
|
+
* Returns null when it is fine — so a caller can write `error = validate(...)`
|
|
39
|
+
* and render it directly, rather than unpacking a result object to find out
|
|
40
|
+
* there was nothing wrong.
|
|
41
|
+
*/
|
|
42
|
+
export function validate(raw, options) {
|
|
43
|
+
var opts = options || {};
|
|
44
|
+
var value = toValue(raw);
|
|
45
|
+
var trimmed = value.trim();
|
|
46
|
+
|
|
47
|
+
if (opts.required && !trimmed) return opts.requiredMessage || 'This is required';
|
|
48
|
+
if (opts.minLength != null && trimmed.length > 0 && trimmed.length < opts.minLength) {
|
|
49
|
+
return 'At least ' + opts.minLength + ' characters';
|
|
50
|
+
}
|
|
51
|
+
if (opts.maxLength != null && value.length > opts.maxLength) {
|
|
52
|
+
return 'At most ' + opts.maxLength + ' characters';
|
|
53
|
+
}
|
|
54
|
+
if (opts.pattern) {
|
|
55
|
+
var re = opts.pattern instanceof RegExp ? opts.pattern : new RegExp(opts.pattern);
|
|
56
|
+
if (trimmed && !re.test(trimmed)) return opts.patternMessage || 'That is not a valid value';
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** How many characters are left, or null when there is no limit to count to. */
|
|
62
|
+
export function remaining(raw, maxLength) {
|
|
63
|
+
if (maxLength == null) return null;
|
|
64
|
+
return maxLength - toValue(raw).length;
|
|
65
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import { toValue, normalise, validate, remaining, TEXT_TYPES } from './textField.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {object} props
|
|
6
|
+
* @param {'text'|'multiline'|'search'} [props.type='text']
|
|
7
|
+
* @param {string} [props.value] — controlled
|
|
8
|
+
* @param {string} [props.defaultValue] — uncontrolled initial
|
|
9
|
+
* @param {(value) => void} [props.onChange]
|
|
10
|
+
* @param {(value) => void} [props.onCommit] — on blur/Enter, after normalising
|
|
11
|
+
* @param {boolean} [props.required]
|
|
12
|
+
* @param {number} [props.minLength]
|
|
13
|
+
* @param {number} [props.maxLength]
|
|
14
|
+
* @param {RegExp|string} [props.pattern]
|
|
15
|
+
* @param {boolean} [props.trim] — normalise away surrounding space
|
|
16
|
+
* @param {string} [props.placeholder]
|
|
17
|
+
* @param {boolean} [props.disabled]
|
|
18
|
+
* @param {number} [props.rows] — multiline only
|
|
19
|
+
*/
|
|
20
|
+
export function useTextFieldController(props) {
|
|
21
|
+
props = props || {};
|
|
22
|
+
var type = props.type || 'text';
|
|
23
|
+
if (TEXT_TYPES.indexOf(type) === -1) {
|
|
24
|
+
throw new Error('[xeplr-ui-utils:TextField] Unknown type: ' + type + '. Expected one of ' + TEXT_TYPES.join(', '));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var controlled = props.value !== undefined;
|
|
28
|
+
var [inner, setInner] = useState(toValue(props.defaultValue));
|
|
29
|
+
var raw = controlled ? toValue(props.value) : inner;
|
|
30
|
+
|
|
31
|
+
// Errors appear on COMMIT, not on every keystroke. A required field that
|
|
32
|
+
// says "This is required" while you are still typing the first letter is
|
|
33
|
+
// shouting at somebody for not having finished.
|
|
34
|
+
var [touched, setTouched] = useState(false);
|
|
35
|
+
|
|
36
|
+
var options = useMemo(function() {
|
|
37
|
+
return {
|
|
38
|
+
required: props.required,
|
|
39
|
+
requiredMessage: props.requiredMessage,
|
|
40
|
+
minLength: props.minLength,
|
|
41
|
+
maxLength: props.maxLength,
|
|
42
|
+
pattern: props.pattern,
|
|
43
|
+
patternMessage: props.patternMessage,
|
|
44
|
+
trim: props.trim
|
|
45
|
+
};
|
|
46
|
+
}, [props.required, props.requiredMessage, props.minLength, props.maxLength,
|
|
47
|
+
props.pattern, props.patternMessage, props.trim]);
|
|
48
|
+
|
|
49
|
+
var handleChange = useCallback(function(e) {
|
|
50
|
+
var next = e && e.target ? e.target.value : e;
|
|
51
|
+
// Capped as you type, because a maxLength you can exceed and are told off
|
|
52
|
+
// for afterwards is a maxLength that does nothing.
|
|
53
|
+
if (props.maxLength != null) next = String(next).slice(0, props.maxLength);
|
|
54
|
+
if (!controlled) setInner(next);
|
|
55
|
+
if (props.onChange) props.onChange(next);
|
|
56
|
+
}, [controlled, props.maxLength, props.onChange]);
|
|
57
|
+
|
|
58
|
+
var commit = useCallback(function(value) {
|
|
59
|
+
var next = normalise(value, options);
|
|
60
|
+
setTouched(true);
|
|
61
|
+
if (!controlled && next !== value) setInner(next);
|
|
62
|
+
if (props.onCommit) props.onCommit(next);
|
|
63
|
+
else if (next !== value && props.onChange) props.onChange(next);
|
|
64
|
+
}, [controlled, options, props.onChange, props.onCommit]);
|
|
65
|
+
|
|
66
|
+
var handleBlur = useCallback(function(e) {
|
|
67
|
+
commit(e && e.target ? e.target.value : raw);
|
|
68
|
+
}, [commit, raw]);
|
|
69
|
+
|
|
70
|
+
var handleKeyDown = useCallback(function(e) {
|
|
71
|
+
// Enter commits a single-line field; in a multiline one it is a newline.
|
|
72
|
+
if (e.key === 'Enter' && type !== 'multiline') commit(e.target.value);
|
|
73
|
+
if (e.key === 'Escape' && props.onCancel) props.onCancel();
|
|
74
|
+
}, [commit, type, props.onCancel]);
|
|
75
|
+
|
|
76
|
+
var handleClear = useCallback(function() {
|
|
77
|
+
if (!controlled) setInner('');
|
|
78
|
+
if (props.onChange) props.onChange('');
|
|
79
|
+
if (props.onCommit) props.onCommit('');
|
|
80
|
+
}, [controlled, props.onChange, props.onCommit]);
|
|
81
|
+
|
|
82
|
+
var error = touched ? validate(raw, options) : null;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
type: type,
|
|
86
|
+
raw: raw,
|
|
87
|
+
error: error,
|
|
88
|
+
remaining: remaining(raw, props.maxLength),
|
|
89
|
+
placeholder: props.placeholder,
|
|
90
|
+
disabled: props.disabled,
|
|
91
|
+
required: props.required,
|
|
92
|
+
maxLength: props.maxLength,
|
|
93
|
+
rows: props.rows,
|
|
94
|
+
label: props.label,
|
|
95
|
+
help: props.help,
|
|
96
|
+
handleChange: handleChange,
|
|
97
|
+
handleBlur: handleBlur,
|
|
98
|
+
handleKeyDown: handleKeyDown,
|
|
99
|
+
handleClear: handleClear
|
|
100
|
+
};
|
|
101
|
+
}
|