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