react-store-input 0.2.6 → 0.5.0
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/CHANGELOG.md +136 -0
- package/README.md +211 -199
- package/dist/index.d.mts +127 -11
- package/dist/index.d.ts +127 -11
- package/dist/index.js +542 -280
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +529 -272
- package/dist/index.mjs.map +1 -0
- package/dist/text-editor.d.mts +11 -0
- package/dist/text-editor.d.ts +11 -0
- package/dist/text-editor.js +203 -0
- package/dist/text-editor.js.map +1 -0
- package/dist/text-editor.mjs +178 -0
- package/dist/text-editor.mjs.map +1 -0
- package/package.json +78 -18
- package/dist/create_render.d.mts +0 -9
- package/dist/create_render.d.ts +0 -9
- package/dist/create_render.js +0 -51
- package/dist/create_render.mjs +0 -25
- package/dist/text_editor.d.mts +0 -13
- package/dist/text_editor.d.ts +0 -13
- package/dist/text_editor.js +0 -135
- package/dist/text_editor.mjs +0 -112
- package/dist/use_form_store.d.mts +0 -16
- package/dist/use_form_store.d.ts +0 -16
- package/dist/use_form_store.js +0 -365
- package/dist/use_form_store.mjs +0 -340
- package/dist/use_store_component.d.mts +0 -23
- package/dist/use_store_component.d.ts +0 -23
- package/dist/use_store_component.js +0 -337
- package/dist/use_store_component.mjs +0 -311
- package/dist/use_store_controller.d.mts +0 -11
- package/dist/use_store_controller.d.ts +0 -11
- package/dist/use_store_controller.js +0 -57
- package/dist/use_store_controller.mjs +0 -32
- package/dist/use_store_input.d.mts +0 -22
- package/dist/use_store_input.d.ts +0 -22
- package/dist/use_store_input.js +0 -151
- package/dist/use_store_input.mjs +0 -126
- package/dist/use_store_input_with_name.d.mts +0 -17
- package/dist/use_store_input_with_name.d.ts +0 -17
- package/dist/use_store_input_with_name.js +0 -177
- package/dist/use_store_input_with_name.mjs +0 -150
- package/tsup.config.ts +0 -9
package/dist/index.mjs
CHANGED
|
@@ -1,347 +1,604 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
// src/store/create_render.tsx
|
|
2
|
+
import { useSelector } from "gw-store";
|
|
3
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
+
function createRender(store, selector, compare) {
|
|
5
|
+
function Component() {
|
|
6
|
+
const result = useSelector(store, (state) => state, compare);
|
|
7
|
+
return /* @__PURE__ */ jsx(Fragment, { children: selector(result) });
|
|
8
|
+
}
|
|
9
|
+
return /* @__PURE__ */ jsx(Component, {});
|
|
10
|
+
}
|
|
11
|
+
function createRenderWithStore(store) {
|
|
12
|
+
return function createRender2(selector, compare) {
|
|
13
|
+
function Component() {
|
|
14
|
+
const result = useSelector(store, (state) => state, compare);
|
|
15
|
+
return /* @__PURE__ */ jsx(Fragment, { children: selector(result) });
|
|
16
|
+
}
|
|
17
|
+
return /* @__PURE__ */ jsx(Component, {});
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/binding/lens.ts
|
|
22
|
+
function defineLens(lens) {
|
|
23
|
+
return lens;
|
|
24
|
+
}
|
|
25
|
+
function readPath(value, path) {
|
|
26
|
+
return path.reduce((current, key) => current[key], value);
|
|
27
|
+
}
|
|
28
|
+
function writePath(state, path, value) {
|
|
29
|
+
const parent = path.slice(0, -1).reduce((current, key) => current[key], state);
|
|
30
|
+
parent[path.at(-1)] = value;
|
|
31
|
+
}
|
|
32
|
+
function createPathLens(path) {
|
|
33
|
+
return {
|
|
34
|
+
get: (state) => readPath(state, path),
|
|
35
|
+
set: (state, value) => writePath(state, path, value),
|
|
36
|
+
prop: (key) => createPathLens([...path, key])
|
|
25
37
|
};
|
|
38
|
+
}
|
|
39
|
+
function stateLens() {
|
|
26
40
|
return {
|
|
27
|
-
|
|
41
|
+
prop: (key) => createPathLens([key])
|
|
28
42
|
};
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
// src/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
// src/binding/codec.ts
|
|
46
|
+
function defineCodec(codec) {
|
|
47
|
+
return codec;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/binding/binding.ts
|
|
51
|
+
function defineBinding(binding) {
|
|
52
|
+
return binding;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/binding/laws.ts
|
|
56
|
+
import { produce } from "immer";
|
|
57
|
+
function structuralEqual(previous, next) {
|
|
58
|
+
if (Object.is(previous, next)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (previous instanceof Date && next instanceof Date) {
|
|
62
|
+
return previous.getTime() === next.getTime();
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(previous) && Array.isArray(next)) {
|
|
65
|
+
return previous.length === next.length && previous.every((value, index) => structuralEqual(value, next[index]));
|
|
66
|
+
}
|
|
67
|
+
if (previous !== null && next !== null && typeof previous === "object" && typeof next === "object") {
|
|
68
|
+
const previousRecord = previous;
|
|
69
|
+
const nextRecord = next;
|
|
70
|
+
const previousKeys = Reflect.ownKeys(previousRecord);
|
|
71
|
+
const nextKeys = Reflect.ownKeys(nextRecord);
|
|
72
|
+
return previousKeys.length === nextKeys.length && previousKeys.every(
|
|
73
|
+
(key) => Object.prototype.hasOwnProperty.call(nextRecord, key) && structuralEqual(previousRecord[key], nextRecord[key])
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
function assertLensLaws(lens, options) {
|
|
79
|
+
const equalsState = options.equalsState ?? structuralEqual;
|
|
80
|
+
const equalsValue = options.equalsValue ?? structuralEqual;
|
|
81
|
+
const set = (state, value) => produce(state, (draft) => lens.set(draft, value));
|
|
82
|
+
if (!equalsState(set(options.state, lens.get(options.state)), options.state)) {
|
|
83
|
+
throw new Error("Lens law failed: setting the current value changed state.");
|
|
84
|
+
}
|
|
85
|
+
for (const value of options.values) {
|
|
86
|
+
const updated = set(options.state, value);
|
|
87
|
+
if (!equalsValue(lens.get(updated), value)) {
|
|
88
|
+
throw new Error("Lens law failed: get(set(state, value)) !== value.");
|
|
37
89
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
90
|
+
}
|
|
91
|
+
for (const previous of options.values) {
|
|
92
|
+
for (const next of options.values) {
|
|
93
|
+
if (!equalsState(set(set(options.state, previous), next), set(options.state, next))) {
|
|
94
|
+
throw new Error("Lens law failed: the last set did not win.");
|
|
42
95
|
}
|
|
43
96
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return toInputValue(props.getter(store.state));
|
|
54
|
-
};
|
|
55
|
-
const toInputChecked = (value) => {
|
|
56
|
-
if (props.type === "radio") {
|
|
57
|
-
return value !== void 0 && value === props.value;
|
|
58
|
-
}
|
|
59
|
-
return Boolean(value);
|
|
60
|
-
};
|
|
61
|
-
const getDefaultChecked = () => {
|
|
62
|
-
if (props.defaultChecked) {
|
|
63
|
-
return props.defaultChecked;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function assertCodecLaws(codec, options) {
|
|
100
|
+
const equals = options.equals ?? codec.equals ?? structuralEqual;
|
|
101
|
+
const equalsInput = options.equalsInput ?? structuralEqual;
|
|
102
|
+
for (const value of options.values) {
|
|
103
|
+
const result = codec.parse(codec.format(value));
|
|
104
|
+
if (result.isErr) {
|
|
105
|
+
throw new Error("Codec law failed: parse(format(value)) returned Err.");
|
|
64
106
|
}
|
|
65
|
-
if (
|
|
66
|
-
|
|
107
|
+
if (!equals(result.value, value)) {
|
|
108
|
+
throw new Error("Codec law failed: parse(format(value)) !== value.");
|
|
67
109
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
110
|
+
}
|
|
111
|
+
for (const input of options.inputs ?? []) {
|
|
112
|
+
const result = codec.parse(input);
|
|
113
|
+
if (result.isOk && !equalsInput(codec.format(result.value), input)) {
|
|
114
|
+
throw new Error("Codec law failed: format(parse(input).value) !== input.");
|
|
73
115
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/binding/use_store_binding.tsx
|
|
120
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
121
|
+
function equalStoreValue(previous, next) {
|
|
122
|
+
if (Object.is(previous, next)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (previous instanceof Date && next instanceof Date) {
|
|
126
|
+
return Object.is(previous.getTime(), next.getTime());
|
|
127
|
+
}
|
|
128
|
+
if (Array.isArray(previous) && Array.isArray(next)) {
|
|
129
|
+
return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function useStoreBinding(store, binding, options = {}) {
|
|
134
|
+
const dispatchKey = useId();
|
|
135
|
+
const [initialValue] = useState(() => binding.codec.format(binding.lens.get(store.state)));
|
|
136
|
+
const [meta, setMeta] = useState({ valid: true });
|
|
137
|
+
const metaRef = useRef(meta);
|
|
138
|
+
const optionsRef = useRef(options);
|
|
139
|
+
const subscribedStoreRef = useRef(null);
|
|
140
|
+
const subscribedBindingRef = useRef(binding);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
optionsRef.current = options;
|
|
143
|
+
}, [options]);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const isReplacement = subscribedStoreRef.current !== null && (subscribedStoreRef.current !== store || subscribedBindingRef.current !== binding);
|
|
146
|
+
subscribedStoreRef.current = store;
|
|
147
|
+
subscribedBindingRef.current = binding;
|
|
148
|
+
const sync = (state) => {
|
|
149
|
+
if (!metaRef.current.valid) {
|
|
83
150
|
return;
|
|
84
151
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
const value = toInputValue(props.getter(state));
|
|
93
|
-
if (element.value === value) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
element.value = value;
|
|
152
|
+
const value = binding.lens.get(state);
|
|
153
|
+
optionsRef.current.onStoreChange?.(binding.codec.format(value), value);
|
|
154
|
+
};
|
|
155
|
+
const unsubscribe = store.subscribe((state, key) => {
|
|
156
|
+
if (key !== dispatchKey) {
|
|
157
|
+
sync(state);
|
|
97
158
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
159
|
+
});
|
|
160
|
+
if (isReplacement) {
|
|
161
|
+
sync(store.state);
|
|
162
|
+
}
|
|
163
|
+
return unsubscribe;
|
|
164
|
+
}, [binding, dispatchKey, store]);
|
|
165
|
+
const setValue = useCallback(
|
|
166
|
+
(value) => {
|
|
167
|
+
const currentValue = binding.lens.get(store.state);
|
|
168
|
+
const equals = binding.codec.equals ?? equalStoreValue;
|
|
169
|
+
if (equals(currentValue, value)) {
|
|
104
170
|
return;
|
|
105
171
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
172
|
+
store.dispatch((state) => binding.lens.set(state, value), { key: dispatchKey });
|
|
173
|
+
},
|
|
174
|
+
[binding, dispatchKey, store]
|
|
175
|
+
);
|
|
176
|
+
const commit = useCallback(
|
|
177
|
+
(input) => {
|
|
178
|
+
const result = binding.codec.parse(input);
|
|
179
|
+
if (result.isErr) {
|
|
180
|
+
const invalidMeta = {
|
|
181
|
+
valid: false,
|
|
182
|
+
error: result.error
|
|
183
|
+
};
|
|
184
|
+
metaRef.current = invalidMeta;
|
|
185
|
+
setMeta(invalidMeta);
|
|
186
|
+
return result;
|
|
110
187
|
}
|
|
111
|
-
|
|
112
|
-
|
|
188
|
+
const validMeta = { valid: true };
|
|
189
|
+
metaRef.current = validMeta;
|
|
190
|
+
setMeta((current) => current.valid ? current : validMeta);
|
|
191
|
+
setValue(result.value);
|
|
192
|
+
return result;
|
|
193
|
+
},
|
|
194
|
+
[binding, setValue]
|
|
195
|
+
);
|
|
196
|
+
const reset = useCallback(
|
|
197
|
+
(value) => {
|
|
198
|
+
const validMeta = { valid: true };
|
|
199
|
+
metaRef.current = validMeta;
|
|
200
|
+
setMeta((current) => current.valid ? current : validMeta);
|
|
201
|
+
setValue(value);
|
|
202
|
+
},
|
|
203
|
+
[setValue]
|
|
204
|
+
);
|
|
113
205
|
return {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
props.onChange?.(event);
|
|
119
|
-
}
|
|
206
|
+
initialValue,
|
|
207
|
+
meta,
|
|
208
|
+
commit,
|
|
209
|
+
reset
|
|
120
210
|
};
|
|
121
211
|
}
|
|
122
212
|
|
|
123
|
-
// src/
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
213
|
+
// src/html_element/use_store_html_element.tsx
|
|
214
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
215
|
+
|
|
216
|
+
// src/html_element/dom_value.ts
|
|
217
|
+
function pad(value) {
|
|
218
|
+
return String(value).padStart(2, "0");
|
|
219
|
+
}
|
|
220
|
+
function formatDateTimeLocal(value) {
|
|
221
|
+
const date = new Date(value);
|
|
222
|
+
if (Number.isNaN(date.getTime())) {
|
|
223
|
+
return "";
|
|
224
|
+
}
|
|
225
|
+
return [
|
|
226
|
+
date.getFullYear(),
|
|
227
|
+
"-",
|
|
228
|
+
pad(date.getMonth() + 1),
|
|
229
|
+
"-",
|
|
230
|
+
pad(date.getDate()),
|
|
231
|
+
"T",
|
|
232
|
+
pad(date.getHours()),
|
|
233
|
+
":",
|
|
234
|
+
pad(date.getMinutes()),
|
|
235
|
+
":",
|
|
236
|
+
pad(date.getSeconds())
|
|
237
|
+
].join("");
|
|
238
|
+
}
|
|
239
|
+
function readElementValue(element) {
|
|
240
|
+
if ("options" in element && element.multiple) {
|
|
241
|
+
return Array.from(element.selectedOptions, (option) => option.value);
|
|
242
|
+
}
|
|
243
|
+
return element.value;
|
|
244
|
+
}
|
|
245
|
+
function writeElementValue(element, value) {
|
|
246
|
+
if ("files" in element && element.type === "file") {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if ("options" in element && element.multiple) {
|
|
250
|
+
const selectedValues = new Set((Array.isArray(value) ? value : [value]).map(String));
|
|
251
|
+
for (const option of element.options) {
|
|
252
|
+
const selected = selectedValues.has(option.value);
|
|
253
|
+
if (option.selected !== selected) {
|
|
254
|
+
option.selected = selected;
|
|
137
255
|
}
|
|
138
|
-
state[props.name] = value;
|
|
139
256
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const nextValue = String(value);
|
|
260
|
+
if (element.value !== nextValue) {
|
|
261
|
+
element.value = nextValue;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/html_element/reset_coordinator.ts
|
|
266
|
+
var resettingForms = /* @__PURE__ */ new WeakSet();
|
|
267
|
+
var resetCoordinators = /* @__PURE__ */ new WeakMap();
|
|
268
|
+
function isFormResetting(form) {
|
|
269
|
+
return resettingForms.has(form);
|
|
270
|
+
}
|
|
271
|
+
function registerResetBinding(form, batch, reset) {
|
|
272
|
+
let coordinator = resetCoordinators.get(form);
|
|
273
|
+
if (!coordinator) {
|
|
274
|
+
const groups = /* @__PURE__ */ new Map();
|
|
275
|
+
const handleReset = () => {
|
|
276
|
+
resettingForms.add(form);
|
|
277
|
+
const groupSnapshots = Array.from(groups, ([groupBatch, bindings2]) => [groupBatch, [...bindings2]]);
|
|
278
|
+
setTimeout(() => {
|
|
279
|
+
try {
|
|
280
|
+
for (const [groupBatch, bindings2] of groupSnapshots) {
|
|
281
|
+
groupBatch(() => {
|
|
282
|
+
for (const binding of bindings2) {
|
|
283
|
+
binding();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
} finally {
|
|
288
|
+
setTimeout(() => resettingForms.delete(form), 0);
|
|
289
|
+
}
|
|
290
|
+
}, 0);
|
|
291
|
+
};
|
|
292
|
+
coordinator = { groups, handleReset };
|
|
293
|
+
resetCoordinators.set(form, coordinator);
|
|
294
|
+
form.addEventListener("reset", handleReset);
|
|
295
|
+
}
|
|
296
|
+
let bindings = coordinator.groups.get(batch);
|
|
297
|
+
if (!bindings) {
|
|
298
|
+
bindings = /* @__PURE__ */ new Set();
|
|
299
|
+
coordinator.groups.set(batch, bindings);
|
|
300
|
+
}
|
|
301
|
+
bindings.add(reset);
|
|
302
|
+
return () => {
|
|
303
|
+
const currentCoordinator = resetCoordinators.get(form);
|
|
304
|
+
if (!currentCoordinator) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const currentBindings = currentCoordinator.groups.get(batch);
|
|
308
|
+
currentBindings?.delete(reset);
|
|
309
|
+
if (currentBindings?.size === 0) {
|
|
310
|
+
currentCoordinator.groups.delete(batch);
|
|
311
|
+
}
|
|
312
|
+
if (currentCoordinator.groups.size === 0) {
|
|
313
|
+
form.removeEventListener("reset", currentCoordinator.handleReset);
|
|
314
|
+
resetCoordinators.delete(form);
|
|
315
|
+
}
|
|
144
316
|
};
|
|
145
317
|
}
|
|
146
318
|
|
|
147
|
-
// src/
|
|
148
|
-
|
|
319
|
+
// src/html_element/value_conversion.ts
|
|
320
|
+
function convertToInputValue(value, type) {
|
|
321
|
+
if (value === void 0 || value === null) {
|
|
322
|
+
return "";
|
|
323
|
+
}
|
|
324
|
+
if (type === "datetime-local") {
|
|
325
|
+
return formatDateTimeLocal(value);
|
|
326
|
+
}
|
|
327
|
+
return Array.isArray(value) ? value.map(String) : String(value);
|
|
328
|
+
}
|
|
329
|
+
function convertToStateValue(value, type) {
|
|
330
|
+
if (Array.isArray(value)) {
|
|
331
|
+
return value;
|
|
332
|
+
}
|
|
333
|
+
if (type === "number" || type === "range") {
|
|
334
|
+
return value === "" ? void 0 : Number(value);
|
|
335
|
+
}
|
|
336
|
+
if (type === "datetime-local") {
|
|
337
|
+
if (value === "") {
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
const date = new Date(value);
|
|
341
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
342
|
+
}
|
|
343
|
+
return value;
|
|
344
|
+
}
|
|
345
|
+
function resolveDefaultValue(props, stateValue, format) {
|
|
346
|
+
if (props.type !== "radio" && props.value !== void 0) {
|
|
347
|
+
return void 0;
|
|
348
|
+
}
|
|
349
|
+
if (props.defaultValue !== void 0) {
|
|
350
|
+
return props.defaultValue;
|
|
351
|
+
}
|
|
352
|
+
if (props.type === "checkbox" || props.type === "radio" || props.type === "file") {
|
|
353
|
+
return void 0;
|
|
354
|
+
}
|
|
355
|
+
return format(stateValue);
|
|
356
|
+
}
|
|
357
|
+
function resolveDefaultChecked(props, stateValue, toChecked) {
|
|
358
|
+
if (props.checked !== void 0) {
|
|
359
|
+
return void 0;
|
|
360
|
+
}
|
|
361
|
+
if (props.defaultChecked !== void 0) {
|
|
362
|
+
return props.defaultChecked;
|
|
363
|
+
}
|
|
364
|
+
return props.type === "checkbox" || props.type === "radio" ? toChecked(stateValue) : void 0;
|
|
365
|
+
}
|
|
366
|
+
function resolveResetStateValue(props, stateValue, parse) {
|
|
367
|
+
if (props.type === "checkbox" && props.defaultChecked !== void 0) {
|
|
368
|
+
return props.defaultChecked;
|
|
369
|
+
}
|
|
370
|
+
if (props.type === "radio" && props.defaultChecked) {
|
|
371
|
+
if (props.value !== void 0) {
|
|
372
|
+
return props.value;
|
|
373
|
+
}
|
|
374
|
+
return convertToStateValue(String(props.defaultValue ?? ""), props.type);
|
|
375
|
+
}
|
|
376
|
+
if (props.type !== "file" && props.type !== "checkbox" && props.type !== "radio" && props.defaultValue !== void 0) {
|
|
377
|
+
const defaultValue = Array.isArray(props.defaultValue) ? props.defaultValue.map(String) : String(props.defaultValue);
|
|
378
|
+
return parse(defaultValue);
|
|
379
|
+
}
|
|
380
|
+
return stateValue;
|
|
381
|
+
}
|
|
149
382
|
|
|
150
|
-
// src/
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
383
|
+
// src/html_element/use_store_html_element.tsx
|
|
384
|
+
function isDisplayValue(value) {
|
|
385
|
+
return typeof value === "string" || typeof value === "number" || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
386
|
+
}
|
|
387
|
+
function readControlValue(element, type, radioValue) {
|
|
388
|
+
if ("files" in element && type === "file") {
|
|
389
|
+
return element.files?.length ? element.files : null;
|
|
390
|
+
}
|
|
391
|
+
if ("checked" in element && type === "checkbox") {
|
|
392
|
+
return element.checked;
|
|
393
|
+
}
|
|
394
|
+
if ("checked" in element && type === "radio") {
|
|
395
|
+
return element.checked ? radioValue ?? element.value : void 0;
|
|
396
|
+
}
|
|
397
|
+
return readElementValue(element);
|
|
398
|
+
}
|
|
399
|
+
function useStoreHTMLElement(ref, store, binding, options = {}) {
|
|
400
|
+
const props = options;
|
|
401
|
+
const getStateValue = binding.lens.get;
|
|
402
|
+
const formatValue = useCallback2((value) => binding.codec.format(value), [binding]);
|
|
403
|
+
const toChecked = useCallback2(
|
|
404
|
+
(value) => props.type === "radio" ? Object.is(formatValue(value), props.value) : Boolean(formatValue(value)),
|
|
405
|
+
[formatValue, props.type, props.value]
|
|
406
|
+
);
|
|
407
|
+
const initialStateValue = getStateValue(store.state);
|
|
408
|
+
const resetValueRef = useRef2(
|
|
409
|
+
Object.prototype.hasOwnProperty.call(options, "resetValue") ? options.resetValue : initialStateValue
|
|
170
410
|
);
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
411
|
+
const writeValue = useCallback2(
|
|
412
|
+
(element, value) => {
|
|
413
|
+
if ("checked" in element && (props.type === "checkbox" || props.type === "radio")) {
|
|
414
|
+
element.checked = toChecked(value);
|
|
175
415
|
return;
|
|
176
416
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
return "";
|
|
185
|
-
};
|
|
186
|
-
const result = getResult();
|
|
187
|
-
if (controller.value !== result) {
|
|
188
|
-
controller.value = result;
|
|
417
|
+
if ("files" in element && props.type === "file") {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const formatted = formatValue(value);
|
|
421
|
+
if (isDisplayValue(formatted)) {
|
|
422
|
+
writeElementValue(element, formatted);
|
|
189
423
|
}
|
|
190
424
|
},
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
425
|
+
[formatValue, props.type, toChecked]
|
|
426
|
+
);
|
|
427
|
+
const { commit, meta, reset } = useStoreBinding(store, binding, {
|
|
428
|
+
onStoreChange: (_input, value) => {
|
|
429
|
+
const element = ref.current;
|
|
430
|
+
if (!element || element.form && isFormResetting(element.form) || props.type !== "radio" && props.value !== void 0) {
|
|
194
431
|
return;
|
|
195
432
|
}
|
|
196
|
-
if (
|
|
197
|
-
setter(state, controller.value);
|
|
433
|
+
if ("checked" in element && (props.type === "checkbox" || props.type === "radio") && props.checked !== void 0) {
|
|
198
434
|
return;
|
|
199
435
|
}
|
|
200
|
-
|
|
201
|
-
state[name] = controller.value;
|
|
202
|
-
}
|
|
436
|
+
writeValue(element, value);
|
|
203
437
|
}
|
|
204
438
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
439
|
+
useEffect2(() => {
|
|
440
|
+
const element = ref.current;
|
|
441
|
+
const form = element?.form;
|
|
442
|
+
if (!element || !form) {
|
|
443
|
+
return;
|
|
208
444
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
445
|
+
return registerResetBinding(form, store.batch, () => {
|
|
446
|
+
if (!element.isConnected) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const resetValue = resetValueRef.current;
|
|
450
|
+
const writeResetValue = () => writeValue(element, resetValue);
|
|
451
|
+
writeResetValue();
|
|
452
|
+
reset(resetValue);
|
|
453
|
+
setTimeout(() => {
|
|
454
|
+
if (element.isConnected) {
|
|
455
|
+
writeResetValue();
|
|
456
|
+
}
|
|
457
|
+
}, 0);
|
|
458
|
+
});
|
|
459
|
+
}, [ref, reset, store.batch, writeValue]);
|
|
460
|
+
const inputProps = {
|
|
461
|
+
defaultValue: resolveDefaultValue(props, initialStateValue, (value) => {
|
|
462
|
+
const formatted = formatValue(value);
|
|
463
|
+
return isDisplayValue(formatted) ? formatted : "";
|
|
464
|
+
}),
|
|
465
|
+
defaultChecked: resolveDefaultChecked(props, initialStateValue, toChecked),
|
|
466
|
+
onChange: (event) => {
|
|
467
|
+
const element = ref.current;
|
|
468
|
+
if (element) {
|
|
469
|
+
const controlValue = readControlValue(element, props.type, props.value);
|
|
470
|
+
if (controlValue !== void 0) {
|
|
471
|
+
commit(controlValue);
|
|
472
|
+
}
|
|
223
473
|
}
|
|
474
|
+
props.onChange?.(event);
|
|
224
475
|
}
|
|
225
|
-
|
|
476
|
+
};
|
|
477
|
+
return { inputProps, meta };
|
|
226
478
|
}
|
|
227
479
|
|
|
228
|
-
// src/
|
|
229
|
-
import {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
480
|
+
// src/html_element/use_store_input_with_name.tsx
|
|
481
|
+
import { useMemo } from "react";
|
|
482
|
+
import { ok } from "gw-result";
|
|
483
|
+
function formatControlValue(value, type) {
|
|
484
|
+
if (type === "checkbox") {
|
|
485
|
+
return Boolean(value);
|
|
486
|
+
}
|
|
487
|
+
if (type === "radio" || type === "file") {
|
|
488
|
+
return value;
|
|
489
|
+
}
|
|
490
|
+
return convertToInputValue(value, type);
|
|
491
|
+
}
|
|
492
|
+
function parseControlValue(value, type) {
|
|
493
|
+
if (typeof value === "string" || Array.isArray(value)) {
|
|
494
|
+
const stateInputValue = Array.isArray(value) ? [...value] : value;
|
|
495
|
+
return convertToStateValue(stateInputValue, type);
|
|
496
|
+
}
|
|
497
|
+
return value;
|
|
498
|
+
}
|
|
499
|
+
function useStoreInputWithName(ref, store, props) {
|
|
500
|
+
const binding = useMemo(
|
|
501
|
+
() => ({
|
|
502
|
+
lens: {
|
|
503
|
+
get: (state) => state[props.name],
|
|
504
|
+
set: (state, value) => {
|
|
505
|
+
state[props.name] = value;
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
codec: {
|
|
509
|
+
format: (value) => formatControlValue(value, props.type),
|
|
510
|
+
parse: (value) => ok(parseControlValue(value, props.type))
|
|
511
|
+
}
|
|
512
|
+
}),
|
|
513
|
+
[props.name, props.type]
|
|
514
|
+
);
|
|
515
|
+
const initialStateValue = binding.lens.get(store.state);
|
|
516
|
+
const resetValue = resolveResetStateValue(
|
|
517
|
+
props,
|
|
518
|
+
initialStateValue,
|
|
519
|
+
(value) => parseControlValue(value, props.type)
|
|
520
|
+
);
|
|
521
|
+
const { inputProps } = useStoreHTMLElement(ref, store, binding, {
|
|
522
|
+
...props,
|
|
523
|
+
resetValue
|
|
524
|
+
});
|
|
243
525
|
return {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
textarea,
|
|
247
|
-
textEditor
|
|
526
|
+
...inputProps,
|
|
527
|
+
name: String(props.name)
|
|
248
528
|
};
|
|
249
529
|
}
|
|
530
|
+
|
|
531
|
+
// src/input/store_input.tsx
|
|
532
|
+
import {
|
|
533
|
+
useCallback as useCallback3,
|
|
534
|
+
useRef as useRef3
|
|
535
|
+
} from "react";
|
|
536
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
250
537
|
function Input({
|
|
251
538
|
store,
|
|
252
|
-
getter,
|
|
253
|
-
setter,
|
|
254
|
-
toInputValue,
|
|
255
|
-
toStateValue,
|
|
256
539
|
...props
|
|
257
540
|
}) {
|
|
258
|
-
const ref =
|
|
259
|
-
const storeProps = useStoreInputWithName(ref, store,
|
|
260
|
-
...props,
|
|
261
|
-
getter,
|
|
262
|
-
setter,
|
|
263
|
-
toInputValue,
|
|
264
|
-
toStateValue
|
|
265
|
-
});
|
|
541
|
+
const ref = useRef3(null);
|
|
542
|
+
const storeProps = useStoreInputWithName(ref, store, props);
|
|
266
543
|
return /* @__PURE__ */ jsx2("input", { ref, ...props, ...storeProps });
|
|
267
544
|
}
|
|
268
545
|
function Select({
|
|
269
546
|
store,
|
|
270
|
-
getter,
|
|
271
|
-
setter,
|
|
272
|
-
toInputValue,
|
|
273
|
-
toStateValue,
|
|
274
547
|
...props
|
|
275
548
|
}) {
|
|
276
|
-
const ref =
|
|
277
|
-
const storeProps = useStoreInputWithName(ref, store,
|
|
278
|
-
...props,
|
|
279
|
-
getter,
|
|
280
|
-
setter,
|
|
281
|
-
toInputValue,
|
|
282
|
-
toStateValue
|
|
283
|
-
});
|
|
549
|
+
const ref = useRef3(null);
|
|
550
|
+
const storeProps = useStoreInputWithName(ref, store, props);
|
|
284
551
|
return /* @__PURE__ */ jsx2("select", { ref, ...props, ...storeProps });
|
|
285
552
|
}
|
|
286
553
|
function Textarea({
|
|
287
554
|
store,
|
|
288
|
-
getter,
|
|
289
|
-
setter,
|
|
290
|
-
toInputValue,
|
|
291
|
-
toStateValue,
|
|
292
555
|
...props
|
|
293
556
|
}) {
|
|
294
|
-
const ref =
|
|
295
|
-
const storeProps = useStoreInputWithName(ref, store,
|
|
296
|
-
...props,
|
|
297
|
-
getter,
|
|
298
|
-
setter,
|
|
299
|
-
toInputValue,
|
|
300
|
-
toStateValue
|
|
301
|
-
});
|
|
557
|
+
const ref = useRef3(null);
|
|
558
|
+
const storeProps = useStoreInputWithName(ref, store, props);
|
|
302
559
|
return /* @__PURE__ */ jsx2("textarea", { ref, ...props, ...storeProps });
|
|
303
560
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
function createRenderWithStore(store) {
|
|
309
|
-
return function createRender(selector, compare) {
|
|
310
|
-
function Component() {
|
|
311
|
-
const result = useSelector(store, (state) => state, compare);
|
|
312
|
-
const content = selector(result);
|
|
313
|
-
return /* @__PURE__ */ jsx3(Fragment, { children: content });
|
|
314
|
-
}
|
|
315
|
-
return /* @__PURE__ */ jsx3(Component, {});
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// src/use_form_store.tsx
|
|
320
|
-
import { useStore } from "gw-store";
|
|
321
|
-
function useFormStore(initialState) {
|
|
322
|
-
const store = useStore(initialState);
|
|
323
|
-
const storeComponent = useStoreComponent(store);
|
|
324
|
-
return {
|
|
325
|
-
get state() {
|
|
326
|
-
return store.state;
|
|
561
|
+
function useStoreInput(store) {
|
|
562
|
+
const input = useCallback3(
|
|
563
|
+
function Component(props) {
|
|
564
|
+
return /* @__PURE__ */ jsx2(Input, { store, ...props });
|
|
327
565
|
},
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
566
|
+
[store]
|
|
567
|
+
);
|
|
568
|
+
const select = useCallback3(
|
|
569
|
+
function Component(props) {
|
|
570
|
+
return /* @__PURE__ */ jsx2(Select, { store, ...props });
|
|
571
|
+
},
|
|
572
|
+
[store]
|
|
573
|
+
);
|
|
574
|
+
const textarea = useCallback3(
|
|
575
|
+
function Component(props) {
|
|
576
|
+
return /* @__PURE__ */ jsx2(Textarea, { store, ...props });
|
|
577
|
+
},
|
|
578
|
+
[store]
|
|
579
|
+
);
|
|
580
|
+
return { input, select, textarea };
|
|
333
581
|
}
|
|
334
582
|
|
|
335
583
|
// src/index.tsx
|
|
336
|
-
|
|
584
|
+
import { err, ok as ok2 } from "gw-result";
|
|
337
585
|
export {
|
|
338
586
|
Input,
|
|
339
587
|
Select,
|
|
340
|
-
TextEditor,
|
|
341
588
|
Textarea,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
589
|
+
assertCodecLaws,
|
|
590
|
+
assertLensLaws,
|
|
591
|
+
createRender,
|
|
592
|
+
createRenderWithStore,
|
|
593
|
+
defineBinding,
|
|
594
|
+
defineCodec,
|
|
595
|
+
defineLens,
|
|
596
|
+
err,
|
|
597
|
+
ok2 as ok,
|
|
598
|
+
stateLens,
|
|
599
|
+
useStoreBinding,
|
|
600
|
+
useStoreHTMLElement,
|
|
345
601
|
useStoreInput,
|
|
346
602
|
useStoreInputWithName
|
|
347
603
|
};
|
|
604
|
+
//# sourceMappingURL=index.mjs.map
|