@mpen/react-basic-inputs 0.1.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/README.md +37 -0
- package/dist/bundle.cjs +212 -0
- package/dist/bundle.d.ts +3 -0
- package/dist/bundle.mjs +206 -0
- package/dist/components/Input.d.ts +25 -0
- package/dist/components/Select.d.ts +33 -0
- package/dist/components/TextInput.d.ts +3 -0
- package/dist/hooks/useEvent.d.ts +6 -0
- package/dist/types/utility.d.ts +30 -0
- package/dist/util/constants.d.ts +5 -0
- package/dist/util/format.d.ts +9 -0
- package/dist/util/resolvable.d.ts +4 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# react-basic-inputs
|
|
2
|
+
|
|
3
|
+
Thin wrappers around native input elements to make them behave better.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
yarn add @mnpenner/react-basic-inputs
|
|
9
|
+
# or
|
|
10
|
+
npm install @mnpenner/react-basic-inputs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Links
|
|
14
|
+
|
|
15
|
+
- Docs: https://mnpenner.github.io/react-basic-inputs
|
|
16
|
+
- Repo: https://github.com/mnpenner/react-basic-inputs
|
|
17
|
+
- Code: https://github.com/mnpenner/react-basic-inputs/tree/default/src
|
|
18
|
+
- Issues: https://github.com/mnpenner/react-basic-inputs/issues
|
|
19
|
+
- Npm: https://www.npmjs.com/package/@mnpenner/react-basic-inputs
|
|
20
|
+
- Yarn: https://yarnpkg.com/package/@mnpenner/react-basic-inputs
|
|
21
|
+
- Bundlephobia: https://bundlephobia.com/package/@mnpenner/react-basic-inputs
|
|
22
|
+
- Unpkg: https://unpkg.com/@mnpenner/react-basic-inputs/dist/bundle.mjs
|
|
23
|
+
- jsDelivr: https://cdn.jsdelivr.net/npm/@mnpenner/react-basic-inputs/dist/bundle.mjs
|
|
24
|
+
|
|
25
|
+
## Components
|
|
26
|
+
|
|
27
|
+
### Select
|
|
28
|
+
|
|
29
|
+
Like `<select>` but takes an `options` prop instead of `children`.
|
|
30
|
+
|
|
31
|
+
- The values are typed; they can be anything, not just `string`
|
|
32
|
+
- Duplicate values are OK. e.g. if you want to put "United States" at the top of your country list and then again in
|
|
33
|
+
alphabetical order, it will just work
|
|
34
|
+
- Each `<option>` will automatically be assigned a unique React `key`
|
|
35
|
+
- If the current `value` cannot be found in the list of `options` it will be appended to the end
|
|
36
|
+
- If you want to override this behavior, set `invalidValueOption`
|
|
37
|
+
- `placeholder` prop
|
package/dist/bundle.cjs
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
|
|
5
|
+
var React = require("react");
|
|
6
|
+
|
|
7
|
+
const NOOP = Object.freeze((() => {}));
|
|
8
|
+
|
|
9
|
+
function identity(x) {
|
|
10
|
+
return x;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let useEvent;
|
|
14
|
+
|
|
15
|
+
if (typeof window !== "undefined") {
|
|
16
|
+
useEvent = handler => {
|
|
17
|
+
React.useDebugValue(handler);
|
|
18
|
+
const handlerRef = React.useRef(handler);
|
|
19
|
+
React.useLayoutEffect((() => {
|
|
20
|
+
handlerRef.current = handler;
|
|
21
|
+
}), [ handler ]);
|
|
22
|
+
return React.useCallback((ev => {
|
|
23
|
+
handlerRef.current(ev);
|
|
24
|
+
}), []);
|
|
25
|
+
};
|
|
26
|
+
} else {
|
|
27
|
+
useEvent = NOOP;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var useEvent$1 = useEvent;
|
|
31
|
+
|
|
32
|
+
function resolveValue(val, ...args) {
|
|
33
|
+
return typeof val === "function" ? val(...args) : val;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function useFirstMountState() {
|
|
37
|
+
var isFirst = React.useRef(true);
|
|
38
|
+
if (isFirst.current) {
|
|
39
|
+
isFirst.current = false;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return isFirst.current;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var useUpdateEffect = function(effect, deps) {
|
|
46
|
+
var isFirstMount = useFirstMountState();
|
|
47
|
+
React.useEffect((function() {
|
|
48
|
+
if (!isFirstMount) {
|
|
49
|
+
return effect();
|
|
50
|
+
}
|
|
51
|
+
}), deps);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
var useUpdateEffect$1 = useUpdateEffect;
|
|
55
|
+
|
|
56
|
+
const defaultMakeInvalidValueOption = value => ({
|
|
57
|
+
value,
|
|
58
|
+
text: String(value),
|
|
59
|
+
disabled: true,
|
|
60
|
+
key: INVALID_OPTION_KEY
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
function defaultMakeKey(opt, idx) {
|
|
64
|
+
if (opt.key != null) {
|
|
65
|
+
return resolveValue(opt.key, opt, idx);
|
|
66
|
+
} else if (typeof opt.value === "string" || typeof opt.value === "number") {
|
|
67
|
+
return opt.value;
|
|
68
|
+
}
|
|
69
|
+
return idx;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const PLACEHOLDER_KEY = "3c9369b7-0a5e-46ea-93c2-e8b9fec67fdb";
|
|
73
|
+
|
|
74
|
+
const INVALID_OPTION_KEY = "1a53f789-77f5-4ce6-a829-b00e563f1ee8";
|
|
75
|
+
|
|
76
|
+
function Select({options, value, invalidValueOption = defaultMakeInvalidValueOption, onChange, placeholder, ...selectAttrs}) {
|
|
77
|
+
const isNotSelected = value == null;
|
|
78
|
+
const isValid = React.useMemo((() => value != null && options.some((o => o.value == value))), [ options, value ]);
|
|
79
|
+
const extraOption = React.useMemo((() => {
|
|
80
|
+
if (value == null || !invalidValueOption) return null;
|
|
81
|
+
return invalidValueOption(value);
|
|
82
|
+
}), [ invalidValueOption, value ]);
|
|
83
|
+
const fixedOptions = React.useMemo((() => {
|
|
84
|
+
if (isValid) return options;
|
|
85
|
+
const fixedOptions = [ ...options ];
|
|
86
|
+
if (isNotSelected) {
|
|
87
|
+
if (placeholder != null) {
|
|
88
|
+
fixedOptions.unshift({
|
|
89
|
+
text: placeholder,
|
|
90
|
+
hidden: true,
|
|
91
|
+
value: null,
|
|
92
|
+
key: PLACEHOLDER_KEY
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
} else if (extraOption) {
|
|
96
|
+
fixedOptions.push(extraOption);
|
|
97
|
+
}
|
|
98
|
+
return fixedOptions;
|
|
99
|
+
}), [ isValid, options, isNotSelected, extraOption, placeholder ]);
|
|
100
|
+
const handleChange = useEvent$1((ev => {
|
|
101
|
+
const idx = ev.target.selectedIndex;
|
|
102
|
+
const opt = fixedOptions[idx];
|
|
103
|
+
onChange?.({
|
|
104
|
+
value: opt.value,
|
|
105
|
+
index: idx,
|
|
106
|
+
type: "change",
|
|
107
|
+
timeStamp: ev.timeStamp,
|
|
108
|
+
target: ev.target
|
|
109
|
+
});
|
|
110
|
+
}));
|
|
111
|
+
const ref = React.useRef(null);
|
|
112
|
+
const refreshSelectedIndex = React.useCallback((() => {
|
|
113
|
+
if (!ref.current) return;
|
|
114
|
+
if (ref.current.selectedIndex < 0 || fixedOptions[ref.current.selectedIndex].value != value) {
|
|
115
|
+
ref.current.selectedIndex = fixedOptions.findIndex((opt => opt.value == value));
|
|
116
|
+
}
|
|
117
|
+
}), [ fixedOptions, value ]);
|
|
118
|
+
const setRef = el => {
|
|
119
|
+
ref.current = el;
|
|
120
|
+
refreshSelectedIndex();
|
|
121
|
+
};
|
|
122
|
+
useUpdateEffect$1((() => {
|
|
123
|
+
refreshSelectedIndex();
|
|
124
|
+
}), [ refreshSelectedIndex ]);
|
|
125
|
+
const usedKeys = new Map;
|
|
126
|
+
return jsxRuntime.jsx("select", {
|
|
127
|
+
...selectAttrs,
|
|
128
|
+
onChange: handleChange,
|
|
129
|
+
ref: setRef,
|
|
130
|
+
children: fixedOptions.map(((opt, idx) => {
|
|
131
|
+
const {value, text, key, ...optAttrs} = opt;
|
|
132
|
+
let fixedKey = defaultMakeKey(opt, idx);
|
|
133
|
+
for (;;) {
|
|
134
|
+
let suffix = usedKeys.get(fixedKey);
|
|
135
|
+
if (suffix === undefined) {
|
|
136
|
+
usedKeys.set(fixedKey, 1);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
usedKeys.set(fixedKey, ++suffix);
|
|
140
|
+
fixedKey = `${fixedKey}(${suffix})`;
|
|
141
|
+
}
|
|
142
|
+
return React.createElement("option", {
|
|
143
|
+
...optAttrs,
|
|
144
|
+
key: fixedKey
|
|
145
|
+
}, opt.text);
|
|
146
|
+
}))
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function collapseWhitespace(str) {
|
|
151
|
+
if (!str) return "";
|
|
152
|
+
return String(str).replace(/\s+/gu, " ").trim();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const Input = React.forwardRef((function Input({value = "", onChange, onInput, onBlur, formatOnChange = identity, ...otherProps}, ref) {
|
|
156
|
+
const [currentValue, setCurrentValue] = React.useState(value);
|
|
157
|
+
const lastValue = React.useRef(value);
|
|
158
|
+
const modified = React.useRef(false);
|
|
159
|
+
useUpdateEffect$1((() => {
|
|
160
|
+
setCurrentValue(value);
|
|
161
|
+
modified.current = false;
|
|
162
|
+
lastValue.current = value;
|
|
163
|
+
}), [ value ]);
|
|
164
|
+
const props = {
|
|
165
|
+
type: "text",
|
|
166
|
+
...otherProps,
|
|
167
|
+
value: currentValue,
|
|
168
|
+
onChange: ev => {
|
|
169
|
+
setCurrentValue(ev.target.value);
|
|
170
|
+
},
|
|
171
|
+
onInput: ev => {
|
|
172
|
+
modified.current = true;
|
|
173
|
+
onInput?.(ev);
|
|
174
|
+
},
|
|
175
|
+
onBlur: ev => {
|
|
176
|
+
const formattedValue = formatOnChange(currentValue);
|
|
177
|
+
if (modified.current) {
|
|
178
|
+
if (formattedValue !== lastValue.current) {
|
|
179
|
+
onChange?.({
|
|
180
|
+
type: "change",
|
|
181
|
+
value: formattedValue,
|
|
182
|
+
timeStamp: ev.timeStamp,
|
|
183
|
+
target: ev.target
|
|
184
|
+
});
|
|
185
|
+
lastValue.current = formattedValue;
|
|
186
|
+
}
|
|
187
|
+
if (formattedValue !== ev.target.value) {
|
|
188
|
+
setCurrentValue(formattedValue);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
onBlur?.(ev);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
return jsxRuntime.jsx("input", {
|
|
195
|
+
...props,
|
|
196
|
+
ref
|
|
197
|
+
});
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
function TextInput({formatOnChange = collapseWhitespace, ...otherProps}) {
|
|
201
|
+
return jsxRuntime.jsx(Input, {
|
|
202
|
+
formatOnChange,
|
|
203
|
+
...otherProps,
|
|
204
|
+
type: "text"
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
exports.Input = Input;
|
|
209
|
+
|
|
210
|
+
exports.Select = Select;
|
|
211
|
+
|
|
212
|
+
exports.TextInput = TextInput;
|
package/dist/bundle.d.ts
ADDED
package/dist/bundle.mjs
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
import { useDebugValue, useRef, useLayoutEffect, useCallback, useEffect, useMemo, createElement, forwardRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
const NOOP = Object.freeze((() => {}));
|
|
6
|
+
|
|
7
|
+
function identity(x) {
|
|
8
|
+
return x;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let useEvent;
|
|
12
|
+
|
|
13
|
+
if (typeof window !== "undefined") {
|
|
14
|
+
useEvent = handler => {
|
|
15
|
+
useDebugValue(handler);
|
|
16
|
+
const handlerRef = useRef(handler);
|
|
17
|
+
useLayoutEffect((() => {
|
|
18
|
+
handlerRef.current = handler;
|
|
19
|
+
}), [ handler ]);
|
|
20
|
+
return useCallback((ev => {
|
|
21
|
+
handlerRef.current(ev);
|
|
22
|
+
}), []);
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
useEvent = NOOP;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var useEvent$1 = useEvent;
|
|
29
|
+
|
|
30
|
+
function resolveValue(val, ...args) {
|
|
31
|
+
return typeof val === "function" ? val(...args) : val;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function useFirstMountState() {
|
|
35
|
+
var isFirst = useRef(true);
|
|
36
|
+
if (isFirst.current) {
|
|
37
|
+
isFirst.current = false;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return isFirst.current;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
var useUpdateEffect = function(effect, deps) {
|
|
44
|
+
var isFirstMount = useFirstMountState();
|
|
45
|
+
useEffect((function() {
|
|
46
|
+
if (!isFirstMount) {
|
|
47
|
+
return effect();
|
|
48
|
+
}
|
|
49
|
+
}), deps);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
var useUpdateEffect$1 = useUpdateEffect;
|
|
53
|
+
|
|
54
|
+
const defaultMakeInvalidValueOption = value => ({
|
|
55
|
+
value,
|
|
56
|
+
text: String(value),
|
|
57
|
+
disabled: true,
|
|
58
|
+
key: INVALID_OPTION_KEY
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function defaultMakeKey(opt, idx) {
|
|
62
|
+
if (opt.key != null) {
|
|
63
|
+
return resolveValue(opt.key, opt, idx);
|
|
64
|
+
} else if (typeof opt.value === "string" || typeof opt.value === "number") {
|
|
65
|
+
return opt.value;
|
|
66
|
+
}
|
|
67
|
+
return idx;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const PLACEHOLDER_KEY = "3c9369b7-0a5e-46ea-93c2-e8b9fec67fdb";
|
|
71
|
+
|
|
72
|
+
const INVALID_OPTION_KEY = "1a53f789-77f5-4ce6-a829-b00e563f1ee8";
|
|
73
|
+
|
|
74
|
+
function Select({options, value, invalidValueOption = defaultMakeInvalidValueOption, onChange, placeholder, ...selectAttrs}) {
|
|
75
|
+
const isNotSelected = value == null;
|
|
76
|
+
const isValid = useMemo((() => value != null && options.some((o => o.value == value))), [ options, value ]);
|
|
77
|
+
const extraOption = useMemo((() => {
|
|
78
|
+
if (value == null || !invalidValueOption) return null;
|
|
79
|
+
return invalidValueOption(value);
|
|
80
|
+
}), [ invalidValueOption, value ]);
|
|
81
|
+
const fixedOptions = useMemo((() => {
|
|
82
|
+
if (isValid) return options;
|
|
83
|
+
const fixedOptions = [ ...options ];
|
|
84
|
+
if (isNotSelected) {
|
|
85
|
+
if (placeholder != null) {
|
|
86
|
+
fixedOptions.unshift({
|
|
87
|
+
text: placeholder,
|
|
88
|
+
hidden: true,
|
|
89
|
+
value: null,
|
|
90
|
+
key: PLACEHOLDER_KEY
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else if (extraOption) {
|
|
94
|
+
fixedOptions.push(extraOption);
|
|
95
|
+
}
|
|
96
|
+
return fixedOptions;
|
|
97
|
+
}), [ isValid, options, isNotSelected, extraOption, placeholder ]);
|
|
98
|
+
const handleChange = useEvent$1((ev => {
|
|
99
|
+
const idx = ev.target.selectedIndex;
|
|
100
|
+
const opt = fixedOptions[idx];
|
|
101
|
+
onChange?.({
|
|
102
|
+
value: opt.value,
|
|
103
|
+
index: idx,
|
|
104
|
+
type: "change",
|
|
105
|
+
timeStamp: ev.timeStamp,
|
|
106
|
+
target: ev.target
|
|
107
|
+
});
|
|
108
|
+
}));
|
|
109
|
+
const ref = useRef(null);
|
|
110
|
+
const refreshSelectedIndex = useCallback((() => {
|
|
111
|
+
if (!ref.current) return;
|
|
112
|
+
if (ref.current.selectedIndex < 0 || fixedOptions[ref.current.selectedIndex].value != value) {
|
|
113
|
+
ref.current.selectedIndex = fixedOptions.findIndex((opt => opt.value == value));
|
|
114
|
+
}
|
|
115
|
+
}), [ fixedOptions, value ]);
|
|
116
|
+
const setRef = el => {
|
|
117
|
+
ref.current = el;
|
|
118
|
+
refreshSelectedIndex();
|
|
119
|
+
};
|
|
120
|
+
useUpdateEffect$1((() => {
|
|
121
|
+
refreshSelectedIndex();
|
|
122
|
+
}), [ refreshSelectedIndex ]);
|
|
123
|
+
const usedKeys = new Map;
|
|
124
|
+
return jsx("select", {
|
|
125
|
+
...selectAttrs,
|
|
126
|
+
onChange: handleChange,
|
|
127
|
+
ref: setRef,
|
|
128
|
+
children: fixedOptions.map(((opt, idx) => {
|
|
129
|
+
const {value, text, key, ...optAttrs} = opt;
|
|
130
|
+
let fixedKey = defaultMakeKey(opt, idx);
|
|
131
|
+
for (;;) {
|
|
132
|
+
let suffix = usedKeys.get(fixedKey);
|
|
133
|
+
if (suffix === undefined) {
|
|
134
|
+
usedKeys.set(fixedKey, 1);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
usedKeys.set(fixedKey, ++suffix);
|
|
138
|
+
fixedKey = `${fixedKey}(${suffix})`;
|
|
139
|
+
}
|
|
140
|
+
return createElement("option", {
|
|
141
|
+
...optAttrs,
|
|
142
|
+
key: fixedKey
|
|
143
|
+
}, opt.text);
|
|
144
|
+
}))
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function collapseWhitespace(str) {
|
|
149
|
+
if (!str) return "";
|
|
150
|
+
return String(str).replace(/\s+/gu, " ").trim();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const Input = forwardRef((function Input({value = "", onChange, onInput, onBlur, formatOnChange = identity, ...otherProps}, ref) {
|
|
154
|
+
const [currentValue, setCurrentValue] = useState(value);
|
|
155
|
+
const lastValue = useRef(value);
|
|
156
|
+
const modified = useRef(false);
|
|
157
|
+
useUpdateEffect$1((() => {
|
|
158
|
+
setCurrentValue(value);
|
|
159
|
+
modified.current = false;
|
|
160
|
+
lastValue.current = value;
|
|
161
|
+
}), [ value ]);
|
|
162
|
+
const props = {
|
|
163
|
+
type: "text",
|
|
164
|
+
...otherProps,
|
|
165
|
+
value: currentValue,
|
|
166
|
+
onChange: ev => {
|
|
167
|
+
setCurrentValue(ev.target.value);
|
|
168
|
+
},
|
|
169
|
+
onInput: ev => {
|
|
170
|
+
modified.current = true;
|
|
171
|
+
onInput?.(ev);
|
|
172
|
+
},
|
|
173
|
+
onBlur: ev => {
|
|
174
|
+
const formattedValue = formatOnChange(currentValue);
|
|
175
|
+
if (modified.current) {
|
|
176
|
+
if (formattedValue !== lastValue.current) {
|
|
177
|
+
onChange?.({
|
|
178
|
+
type: "change",
|
|
179
|
+
value: formattedValue,
|
|
180
|
+
timeStamp: ev.timeStamp,
|
|
181
|
+
target: ev.target
|
|
182
|
+
});
|
|
183
|
+
lastValue.current = formattedValue;
|
|
184
|
+
}
|
|
185
|
+
if (formattedValue !== ev.target.value) {
|
|
186
|
+
setCurrentValue(formattedValue);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
onBlur?.(ev);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
return jsx("input", {
|
|
193
|
+
...props,
|
|
194
|
+
ref
|
|
195
|
+
});
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
function TextInput({formatOnChange = collapseWhitespace, ...otherProps}) {
|
|
199
|
+
return jsx(Input, {
|
|
200
|
+
formatOnChange,
|
|
201
|
+
...otherProps,
|
|
202
|
+
type: "text"
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export { Input, Select, TextInput };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { EventCallback, HtmlInputElement, OverrideProps } from "../types/utility";
|
|
3
|
+
export type InputChangeEvent = {
|
|
4
|
+
value: string;
|
|
5
|
+
type: 'change';
|
|
6
|
+
timeStamp: number;
|
|
7
|
+
target: HtmlInputElement;
|
|
8
|
+
};
|
|
9
|
+
export type InputChangeEventHandler = EventCallback<InputChangeEvent>;
|
|
10
|
+
export type InputProps = OverrideProps<'input', {
|
|
11
|
+
onChange?: InputChangeEventHandler;
|
|
12
|
+
value?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Function used to format value on blur.
|
|
15
|
+
*/
|
|
16
|
+
formatOnChange?: (value: string) => string;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "value" | "onChange" | "formatOnChange"> & {
|
|
19
|
+
onChange?: InputChangeEventHandler;
|
|
20
|
+
value?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Function used to format value on blur.
|
|
23
|
+
*/
|
|
24
|
+
formatOnChange?: (value: string) => string;
|
|
25
|
+
} & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Key, ReactNode } from 'react';
|
|
2
|
+
import { Resolvable } from '../util/resolvable';
|
|
3
|
+
import { EventCallback, HtmlSelectElement, NonNil, OverrideProps } from "../types/utility";
|
|
4
|
+
export type SelectOption<T> = OverrideProps<'option', {
|
|
5
|
+
value: T;
|
|
6
|
+
text: ReactNode;
|
|
7
|
+
key?: Resolvable<Key, [SelectOption<T>, number]>;
|
|
8
|
+
}, 'children' | 'selected'>;
|
|
9
|
+
export interface SelectChangeEvent<T> {
|
|
10
|
+
value: T;
|
|
11
|
+
index: number;
|
|
12
|
+
type: 'change';
|
|
13
|
+
timeStamp: number;
|
|
14
|
+
target: HtmlSelectElement;
|
|
15
|
+
}
|
|
16
|
+
export type SelectChangeEventHandler<T> = EventCallback<SelectChangeEvent<T>>;
|
|
17
|
+
export type InvalidValueToOption<T> = (value: T) => SelectOption<T>;
|
|
18
|
+
export type SelectProps<T extends NonNil> = OverrideProps<'select', {
|
|
19
|
+
options: SelectOption<T>[];
|
|
20
|
+
value?: T | null;
|
|
21
|
+
onChange?: SelectChangeEventHandler<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Function used to create an <option> when `value` cannot be found in the list of `options`.
|
|
24
|
+
* Set to `null` to disable this behavior.
|
|
25
|
+
* By default, stringifies `value`.
|
|
26
|
+
*/
|
|
27
|
+
invalidValueOption?: InvalidValueToOption<T> | null;
|
|
28
|
+
/**
|
|
29
|
+
* Text to display when `value` is nullish.
|
|
30
|
+
*/
|
|
31
|
+
placeholder?: ReactNode;
|
|
32
|
+
}, 'children' | 'defaultValue'>;
|
|
33
|
+
export declare function Select<T extends NonNil>({ options, value, invalidValueOption, onChange, placeholder, ...selectAttrs }: SelectProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type Fn<TArgs extends ReadonlyArray<unknown> = unknown[], TRet = unknown> = (...args: TArgs[]) => TRet;
|
|
2
|
+
export type AnyFn = (...args: any[]) => any;
|
|
3
|
+
export type EventCallback<T = never> = (ev: T) => void;
|
|
4
|
+
export type VoidFn = () => void;
|
|
5
|
+
export type EmptyObject = Record<PropertyKey, never>;
|
|
6
|
+
export type UnknownObject = Record<PropertyKey, unknown>;
|
|
7
|
+
export type Override<Base, Extension, DeleteKeys extends PropertyKey = never> = Omit<Base, keyof Extension | DeleteKeys> & Extension;
|
|
8
|
+
export type PartiallyRequired<Type, Key extends keyof Type> = Omit<Type, Key> & Required<Pick<Type, Key>>;
|
|
9
|
+
export type Optionalize<T1 extends T2, T2> = Omit<T1, keyof T2> & Partial<Pick<T1, keyof T2>>;
|
|
10
|
+
/**
|
|
11
|
+
* Nullish. Either `null` or `undefined`.
|
|
12
|
+
*/
|
|
13
|
+
export type nil = null | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Not null or undefined.
|
|
16
|
+
*/
|
|
17
|
+
export type NonNil = {};
|
|
18
|
+
export type OverrideProps<Base extends import('react').ElementType, Extension, DeleteKeys extends PropertyKey = never> = Override<import('react').ComponentPropsWithoutRef<Base>, Extension, DeleteKeys>;
|
|
19
|
+
export type OmitProps<Base extends import('react').ElementType, DeleteKeys extends PropertyKey> = Omit<import('react').ComponentPropsWithoutRef<Base>, DeleteKeys>;
|
|
20
|
+
export type MapKeyType<M> = M extends Map<infer K, any> ? K : never;
|
|
21
|
+
export type MapValueType<M> = M extends Map<any, infer V> ? V : never;
|
|
22
|
+
/** Hack to de-conflict React's HTMLInputElement vs the standard dom lib */
|
|
23
|
+
export type HtmlInputElement = HTMLElementTagNameMap['input'];
|
|
24
|
+
export type HtmlSelectElement = HTMLElementTagNameMap['select'];
|
|
25
|
+
export type HtmlInputChangeEvent = import('react').ChangeEvent<HtmlInputElement>;
|
|
26
|
+
export type ArrayType<T extends any[]> = T[number];
|
|
27
|
+
type _UnionKeys<T> = T extends T ? keyof T : never;
|
|
28
|
+
type _StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<_UnionKeys<TAll>, keyof T>, never>> : never;
|
|
29
|
+
export type XOR<T> = _StrictUnionHelper<T, T>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { nil } from "../types/utility";
|
|
2
|
+
export declare function collapseWhitespace(str: string | nil): string;
|
|
3
|
+
export declare function formatEmail(str: string | nil): string;
|
|
4
|
+
export declare function formatUrl(str: string | nil): string;
|
|
5
|
+
export declare function fullWide(n: number): string;
|
|
6
|
+
export declare function stringToNumber(n: string): number;
|
|
7
|
+
export declare function numberToString(f: number): string;
|
|
8
|
+
export declare function formatStrNumber(n: string): string;
|
|
9
|
+
export declare function formatUsername(str: string | nil): string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Fn } from "../types/utility";
|
|
2
|
+
export type Resolvable<TValue = unknown, TArgs extends ReadonlyArray<unknown> = []> = TValue extends any ? TValue | ((...args: TArgs) => TValue) : never;
|
|
3
|
+
export type Resolved<T> = T extends Fn ? ReturnType<T> : T;
|
|
4
|
+
export declare function resolveValue<TValue, TArgs extends ReadonlyArray<any>>(val: Resolvable<TValue, TArgs>, ...args: TArgs): TValue;
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mpen/react-basic-inputs",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"packageManager": "yarn@3.5.0",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/bundle.mjs",
|
|
8
|
+
"require": "./dist/bundle.cjs",
|
|
9
|
+
"types": "./dist/bundle.d.ts"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"./dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev:build": "rollup -cw",
|
|
18
|
+
"dev:serve": "serve -l tcp://0.0.0.0:8080",
|
|
19
|
+
"dev": "run-p \"dev:**\"",
|
|
20
|
+
"serve": "bun run --hot dev/serve.ts",
|
|
21
|
+
"bundle:clean": "rimraf -g \"dist/*\"",
|
|
22
|
+
"bundle:build": "rollup -c",
|
|
23
|
+
"bundle": "run-s bundle:clean bundle:build",
|
|
24
|
+
"patch": "npm version patch && VER=$(jq -r '.version' package.json) && hg ci -m \"Publish v$VER\" && hg tag \"v$VER\"",
|
|
25
|
+
"release": "run-s bundle patch && npm publish --access=public && hg push"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@rollup/plugin-commonjs": "^25.0.4",
|
|
29
|
+
"@rollup/plugin-node-resolve": "^15.2.1",
|
|
30
|
+
"@rollup/plugin-replace": "^5.0.2",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.3",
|
|
32
|
+
"@rollup/plugin-typescript": "^11.1.3",
|
|
33
|
+
"@types/eslint": "^8",
|
|
34
|
+
"@types/jest": "^29.5.0",
|
|
35
|
+
"@types/react": "^18.2.21",
|
|
36
|
+
"@types/react-dom": "^18.2.7",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
|
38
|
+
"@typescript-eslint/parser": "^5.57.1",
|
|
39
|
+
"eslint": "^8.38.0",
|
|
40
|
+
"eslint-plugin-react": "^7.32.2",
|
|
41
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
42
|
+
"jest": "^29.5.0",
|
|
43
|
+
"npm-run-all": "^4.1.5",
|
|
44
|
+
"react": ">=17 <19",
|
|
45
|
+
"react-dom": ">=17 <19",
|
|
46
|
+
"react-use": "^17.4.0",
|
|
47
|
+
"rimraf": "^5.0.0",
|
|
48
|
+
"rollup": "^3.20.2",
|
|
49
|
+
"serve": "^14.2.0",
|
|
50
|
+
"ts-jest": "^29.1.0",
|
|
51
|
+
"tslib": "^2.5.0",
|
|
52
|
+
"typescript": "^5.2.2"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"react": ">=17 <19",
|
|
56
|
+
"react-dom": ">=17 <19"
|
|
57
|
+
}
|
|
58
|
+
}
|