@xeplr/ui-utils 1.0.1 → 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.
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export var TEXT_FIELD_RULES = [
2
+ { selector: 'input[type="text"], input[type="search"], textarea, .xeplr-textfield-input', label: 'Text input element' }
3
+ ];