vultus-core 0.1.1 → 0.2.1
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 +35 -1
- package/dist/MessageSink.d.ts +8 -5
- package/dist/MessageSink.d.ts.map +1 -1
- package/dist/MessageSink.js.map +1 -1
- package/dist/action-core.d.ts +3 -3
- package/dist/action-core.d.ts.map +1 -1
- package/dist/action-core.js +24 -6
- package/dist/action-core.js.map +1 -1
- package/dist/decision.d.ts +2 -0
- package/dist/decision.d.ts.map +1 -1
- package/dist/decision.js +2 -0
- package/dist/decision.js.map +1 -1
- package/dist/field.d.ts +97 -0
- package/dist/field.d.ts.map +1 -0
- package/dist/field.js +95 -0
- package/dist/field.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +34 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/useAction.d.ts.map +1 -1
- package/dist/useAction.js +4 -4
- package/dist/useAction.js.map +1 -1
- package/dist/useActionList.d.ts +2 -2
- package/dist/useActionList.d.ts.map +1 -1
- package/dist/useActionList.js +6 -6
- package/dist/useActionList.js.map +1 -1
- package/dist/useBoolField.d.ts +17 -0
- package/dist/useBoolField.d.ts.map +1 -0
- package/dist/useBoolField.js +12 -0
- package/dist/useBoolField.js.map +1 -0
- package/dist/useMixedBoolField.d.ts +24 -0
- package/dist/useMixedBoolField.d.ts.map +1 -0
- package/dist/useMixedBoolField.js +25 -0
- package/dist/useMixedBoolField.js.map +1 -0
- package/package.json +1 -1
- package/src/MessageSink.tsx +8 -5
- package/src/action-core.ts +27 -7
- package/src/decision.ts +4 -0
- package/src/field.ts +197 -0
- package/src/index.ts +8 -0
- package/src/types.ts +43 -2
- package/src/useAction.ts +5 -5
- package/src/useActionList.ts +7 -7
- package/src/useBoolField.ts +30 -0
- package/src/useMixedBoolField.ts +43 -0
package/src/useActionList.ts
CHANGED
|
@@ -3,14 +3,14 @@ import { useTranslation } from 'react-i18next';
|
|
|
3
3
|
import { useActionErrorCtx } from './useActionErrorCtx.js';
|
|
4
4
|
import { useMessageSink } from './MessageSink.js';
|
|
5
5
|
import { resolveActionFields, makeFirePromise, assembleStatus } from './action-core.js';
|
|
6
|
-
import type { ActionOptions, ActionStatus, ErrorRoutesRegistry } from './types.js';
|
|
6
|
+
import type { ActionOptions, ActionStatus, ErrorRoutesRegistry, Message } from './types.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Build a DYNAMIC list of actions from a spec array — the list counterpart to
|
|
10
10
|
* useAction. The hook count is fixed regardless of list length (one shared
|
|
11
|
-
* error context, one pending/
|
|
11
|
+
* error context, one pending/messages map, one memo), so it is usable where
|
|
12
12
|
* useAction — a per-call hook — cannot be looped (rules of hooks). Pending and
|
|
13
|
-
*
|
|
13
|
+
* messages are tracked per action id in the shared maps; each action gets real
|
|
14
14
|
* per-item pending. The firing machinery + ActionStatus shape are the exact
|
|
15
15
|
* same shared helpers useAction uses (see action-core.ts) — nothing is
|
|
16
16
|
* reimplemented.
|
|
@@ -26,7 +26,7 @@ export function useActionList<TArgs = void, TResult = void, RouteId extends stri
|
|
|
26
26
|
const messageSink = useMessageSink();
|
|
27
27
|
|
|
28
28
|
const [pendingMap, setPendingMap] = useState<Record<string, boolean>>({});
|
|
29
|
-
const [
|
|
29
|
+
const [messagesMap, setMessagesMap] = useState<Record<string, Message[]>>({});
|
|
30
30
|
const pendingRef = useRef(pendingMap);
|
|
31
31
|
pendingRef.current = pendingMap;
|
|
32
32
|
|
|
@@ -39,7 +39,7 @@ export function useActionList<TArgs = void, TResult = void, RouteId extends stri
|
|
|
39
39
|
reason: fields.reason,
|
|
40
40
|
getPending: () => !!pendingRef.current[opts.id],
|
|
41
41
|
setPending: (value) => setPendingMap((m) => ({ ...m, [opts.id]: value })),
|
|
42
|
-
|
|
42
|
+
setMessages: (msgs) => setMessagesMap((m) => ({ ...m, [opts.id]: msgs })),
|
|
43
43
|
errCtxRef,
|
|
44
44
|
t,
|
|
45
45
|
registry,
|
|
@@ -49,10 +49,10 @@ export function useActionList<TArgs = void, TResult = void, RouteId extends stri
|
|
|
49
49
|
opts,
|
|
50
50
|
fields,
|
|
51
51
|
!!pendingMap[opts.id],
|
|
52
|
-
|
|
52
|
+
messagesMap[opts.id] ?? [],
|
|
53
53
|
firePromise,
|
|
54
54
|
);
|
|
55
55
|
}),
|
|
56
|
-
[specs, pendingMap,
|
|
56
|
+
[specs, pendingMap, messagesMap, registry, t, messageSink],
|
|
57
57
|
);
|
|
58
58
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useFieldCore, type FieldControls, type FieldOptions } from './field.js';
|
|
2
|
+
import type { ErrorRoutesRegistry } from './types.js';
|
|
3
|
+
|
|
4
|
+
export type BoolFieldOptions<RouteId extends string = string> = FieldOptions<boolean, boolean, RouteId>;
|
|
5
|
+
|
|
6
|
+
export interface BoolFieldControls extends FieldControls<boolean, boolean> {
|
|
7
|
+
/**
|
|
8
|
+
* Request the opposite value. Programmatic use; widgets obtain the field's
|
|
9
|
+
* confirmation for `!value` first, then call `setValue`.
|
|
10
|
+
*/
|
|
11
|
+
toggle(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A boolean field (moxb's Bool, the sidedao boolean field). Rendered by
|
|
16
|
+
* vultus-antd's BoolCheckbox and BoolSwitch, which take the same controls.
|
|
17
|
+
* Inside the field the value starts at `initialValue` (default false).
|
|
18
|
+
*/
|
|
19
|
+
export function useBoolField<RouteId extends string = string>(
|
|
20
|
+
opts: BoolFieldOptions<RouteId>,
|
|
21
|
+
registry?: ErrorRoutesRegistry<RouteId>,
|
|
22
|
+
): BoolFieldControls {
|
|
23
|
+
const inside = !opts.onChange && !opts.commit;
|
|
24
|
+
const field = useFieldCore<boolean, boolean, RouteId>(
|
|
25
|
+
'boolean',
|
|
26
|
+
inside ? { ...opts, initialValue: opts.initialValue ?? false } : opts,
|
|
27
|
+
registry,
|
|
28
|
+
);
|
|
29
|
+
return { ...field, toggle: () => field.setValue(!field.value) };
|
|
30
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useFieldCore, type FieldControls, type FieldOptions } from './field.js';
|
|
2
|
+
import type { ErrorRoutesRegistry } from './types.js';
|
|
3
|
+
|
|
4
|
+
/** What a mixed boolean field shows: on, off, or some of its items on and some off. */
|
|
5
|
+
export type MixedBool = boolean | 'mixed';
|
|
6
|
+
|
|
7
|
+
export type MixedBoolFieldOptions<RouteId extends string = string> = FieldOptions<MixedBool, boolean, RouteId>;
|
|
8
|
+
|
|
9
|
+
export interface MixedBoolFieldControls extends FieldControls<MixedBool, boolean> {
|
|
10
|
+
/** Request the opposite: off when on, on when off or mixed. Programmatic use, as in BoolFieldControls. */
|
|
11
|
+
toggle(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A boolean field over an aggregate (moxb's indeterminate Bool, ARIA's
|
|
16
|
+
* "mixed"): it shows true, false or 'mixed', and can be asked for true or
|
|
17
|
+
* false only. Rendered by the same widgets as useBoolField: BoolSwitch puts
|
|
18
|
+
* its knob in the middle, BoolCheckbox is indeterminate. Derive the value with
|
|
19
|
+
* `aggregateBool`; with no items at all, disable the field with a reason.
|
|
20
|
+
*/
|
|
21
|
+
export function useMixedBoolField<RouteId extends string = string>(
|
|
22
|
+
opts: MixedBoolFieldOptions<RouteId>,
|
|
23
|
+
registry?: ErrorRoutesRegistry<RouteId>,
|
|
24
|
+
): MixedBoolFieldControls {
|
|
25
|
+
const inside = !opts.onChange && !opts.commit;
|
|
26
|
+
const field = useFieldCore<MixedBool, boolean, RouteId>(
|
|
27
|
+
'mixed-boolean',
|
|
28
|
+
inside ? { ...opts, initialValue: opts.initialValue ?? false } : opts,
|
|
29
|
+
registry,
|
|
30
|
+
);
|
|
31
|
+
return { ...field, toggle: () => field.setValue(field.value !== true) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The aggregate of several booleans: true when all are on, false when none is,
|
|
36
|
+
* 'mixed' otherwise; `undefined` when there are none (a case for disabling the
|
|
37
|
+
* field, not a value).
|
|
38
|
+
*/
|
|
39
|
+
export function aggregateBool(values: readonly boolean[]): MixedBool | undefined {
|
|
40
|
+
if (values.length === 0) return undefined;
|
|
41
|
+
const on = values.filter(Boolean).length;
|
|
42
|
+
return on === values.length ? true : on === 0 ? false : 'mixed';
|
|
43
|
+
}
|