@nsail/stack 0.1.975 → 0.1.981
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/dist/form.d.ts +5 -1
- package/dist/form.js +20 -3
- package/package.json +1 -1
package/dist/form.d.ts
CHANGED
|
@@ -9,7 +9,11 @@ export interface Form<T> {
|
|
|
9
9
|
/** What the server last refused, kept until the field it is about changes. */
|
|
10
10
|
readonly problem: Problem | null;
|
|
11
11
|
set<K extends keyof T>(name: K, value: T[K]): void;
|
|
12
|
-
|
|
12
|
+
/** Replaces the value with another record or with what the server holds; `saved` is cleared unless
|
|
13
|
+
* `keepSaved` says the value is the re-read of what was just sent. */
|
|
14
|
+
reset(value: T, options?: {
|
|
15
|
+
keepSaved?: boolean;
|
|
16
|
+
}): void;
|
|
13
17
|
issues(name: keyof T): Issue[];
|
|
14
18
|
submit(action: (value: T) => Promise<void>): Promise<boolean>;
|
|
15
19
|
}
|
package/dist/form.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Copyright (c) 2026 Leonardo Porro and Emmanuel Arias. https://github.com/nsail-ar/nsail-stack
|
|
3
|
-
import { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { memberOf } from "./descriptors";
|
|
5
5
|
import { validate, validateField } from "./validation";
|
|
6
6
|
import { problemOf } from "./load";
|
|
@@ -12,17 +12,25 @@ export function useForm(message, initial) {
|
|
|
12
12
|
const [problem, setProblem] = useState(null);
|
|
13
13
|
const [submitting, setSubmitting] = useState(false);
|
|
14
14
|
const [saved, setSaved] = useState(false);
|
|
15
|
+
const sending = useRef(false);
|
|
16
|
+
// Bumped in the handlers below, never inside a state updater, which StrictMode runs twice.
|
|
17
|
+
const edits = useRef(0);
|
|
15
18
|
const set = useCallback((name, next) => {
|
|
16
19
|
setValue((current) => ({ ...current, [name]: next }));
|
|
17
20
|
setTouched((current) => new Set(current).add(name));
|
|
18
21
|
setSaved(false);
|
|
22
|
+
edits.current++;
|
|
19
23
|
const member = memberOf(message.fields[name]);
|
|
20
24
|
setProblem((current) => (current ? { ...current, issues: current.issues.filter((issue) => issue.source !== member) } : current));
|
|
21
25
|
}, [message]);
|
|
22
|
-
const reset = useCallback((next) => {
|
|
26
|
+
const reset = useCallback((next, options) => {
|
|
23
27
|
setValue(next);
|
|
24
28
|
setTouched(new Set());
|
|
25
29
|
setProblem(null);
|
|
30
|
+
if (!options?.keepSaved) {
|
|
31
|
+
setSaved(false);
|
|
32
|
+
}
|
|
33
|
+
edits.current++;
|
|
26
34
|
}, []);
|
|
27
35
|
const issues = useCallback((name) => {
|
|
28
36
|
const field = message.fields[name];
|
|
@@ -32,18 +40,26 @@ export function useForm(message, initial) {
|
|
|
32
40
|
return [...local, ...remote.filter((r) => !local.some((l) => l.code === r.code))];
|
|
33
41
|
}, [message, touched, value, problem]);
|
|
34
42
|
const submit = useCallback(async (action) => {
|
|
43
|
+
// A ref, not `submitting`: two calls in one tick both see the state unchanged.
|
|
44
|
+
if (sending.current) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
35
47
|
const refused = validate(message, value);
|
|
36
48
|
if (refused) {
|
|
37
49
|
setTouched(new Set(Object.keys(message.fields)));
|
|
38
50
|
setProblem(refused);
|
|
39
51
|
return false;
|
|
40
52
|
}
|
|
53
|
+
sending.current = true;
|
|
54
|
+
const sentAt = edits.current;
|
|
41
55
|
setSubmitting(true);
|
|
42
56
|
setSaved(false);
|
|
43
57
|
setProblem(null);
|
|
44
58
|
try {
|
|
45
59
|
await action(value);
|
|
46
|
-
|
|
60
|
+
if (edits.current === sentAt) {
|
|
61
|
+
setSaved(true);
|
|
62
|
+
}
|
|
47
63
|
return true;
|
|
48
64
|
}
|
|
49
65
|
catch (error) {
|
|
@@ -51,6 +67,7 @@ export function useForm(message, initial) {
|
|
|
51
67
|
return false;
|
|
52
68
|
}
|
|
53
69
|
finally {
|
|
70
|
+
sending.current = false;
|
|
54
71
|
setSubmitting(false);
|
|
55
72
|
}
|
|
56
73
|
}, [message, value]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nsail/stack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.981",
|
|
4
4
|
"description": "NSail's vendor-free client half: sends messages from the descriptors an Sdk generates, speaks the catalog, and holds the hooks a page reads with.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|