react-luna-form 0.0.85 → 0.0.87

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.
Files changed (70) hide show
  1. package/README.md +15 -10
  2. package/dist/client/cjs/index.js +2 -1
  3. package/dist/client/esm/index.js +2 -1
  4. package/dist/config/cjs/index.js +1 -1
  5. package/dist/config/esm/index.js +1 -1
  6. package/dist/schema/cjs/index.js +1 -1
  7. package/dist/schema/esm/index.js +1 -1
  8. package/dist/server/cjs/index.js +1 -1
  9. package/dist/server/esm/index.js +1 -1
  10. package/dist/types/luna-core/type.d.ts +30 -16
  11. package/dist/types/luna-core/util/build.d.ts +3 -1
  12. package/dist/types/luna-core/util/extract.d.ts +22 -0
  13. package/dist/types/luna-core/util/is-input.d.ts +13 -0
  14. package/dist/types/luna-core/util/prepare.d.ts +1 -1
  15. package/dist/types/luna-core/util/schema.d.ts +5 -5
  16. package/dist/types/luna-react/src/client/component/field/field-preview.d.ts +4 -2
  17. package/dist/types/luna-react/src/{component → client/component}/field/field-set-advanced.d.ts +1 -1
  18. package/dist/types/luna-react/src/client/component/field/field-set.d.ts +11 -0
  19. package/dist/types/luna-react/src/client/component/field/field.d.ts +37 -9
  20. package/dist/types/luna-react/src/client/component/field-description.d.ts +2 -0
  21. package/dist/types/luna-react/src/{component → client/component}/formatted-description.d.ts +2 -2
  22. package/dist/types/luna-react/src/client/component/input-strategies.d.ts +1 -1
  23. package/dist/types/luna-react/src/client/context/failed-submit-context.d.ts +3 -0
  24. package/dist/types/luna-react/src/client/context/reveal-context.d.ts +1 -0
  25. package/dist/types/luna-react/src/client/hook/use-disclosure.d.ts +1 -0
  26. package/dist/types/luna-react/src/client/hook/use-entry-atom.d.ts +18 -0
  27. package/dist/types/luna-react/src/client/hook/use-form-action.d.ts +2 -1
  28. package/dist/types/luna-react/src/client/hook/use-host-entry.d.ts +17 -0
  29. package/dist/types/luna-react/src/client/hook/use-input-core.d.ts +6 -8
  30. package/dist/types/luna-react/src/client/hook/use-input.d.ts +1 -1
  31. package/dist/types/luna-react/src/client/hook/use-latest.d.ts +22 -0
  32. package/dist/types/luna-react/src/client/hook/use-live-item-value.d.ts +17 -3
  33. package/dist/types/luna-react/src/client/hook/use-schema.d.ts +4 -2
  34. package/dist/types/luna-react/src/client/hook/use-value.d.ts +3 -3
  35. package/dist/types/luna-react/src/client/lib/compose-list-value.d.ts +29 -2
  36. package/dist/types/luna-react/src/client/lib/error-store.d.ts +3 -1
  37. package/dist/types/luna-react/src/client/lib/host-value-store.d.ts +28 -0
  38. package/dist/types/luna-react/src/client/lib/list-store.d.ts +6 -4
  39. package/dist/types/luna-react/src/client/lib/resolve-value.d.ts +0 -5
  40. package/dist/types/luna-react/src/client/lib/source-store.d.ts +7 -1
  41. package/dist/types/luna-react/src/client/lib/state-store.d.ts +0 -1
  42. package/dist/types/luna-react/src/client/lib/store-helper.d.ts +5 -7
  43. package/dist/types/luna-react/src/client/lib/value-store.d.ts +0 -1
  44. package/dist/types/luna-react/src/component/collapsible.d.ts +2 -1
  45. package/dist/types/luna-react/src/component/column.d.ts +2 -0
  46. package/dist/types/luna-react/src/component/description.d.ts +9 -0
  47. package/dist/types/luna-react/src/component/field/{field-set.d.ts → field-set-static.d.ts} +1 -1
  48. package/dist/types/luna-react/src/component/field/field.d.ts +1 -1
  49. package/dist/types/luna-react/src/component/field-description.d.ts +3 -2
  50. package/dist/types/luna-react/src/component/form.d.ts +11 -0
  51. package/dist/types/luna-react/src/component/input/input-base.d.ts +1 -0
  52. package/dist/types/luna-react/src/component/input-group.d.ts +2 -0
  53. package/dist/types/luna-react/src/component/input-label.d.ts +2 -0
  54. package/dist/types/luna-react/src/component/slot/slot-base.d.ts +2 -0
  55. package/dist/types/luna-react/src/lib/visibility.d.ts +3 -0
  56. package/dist/types/luna-react/src/server/component/field.d.ts +2 -0
  57. package/dist/types/luna-react/src/server/component/list-slot.d.ts +2 -0
  58. package/dist/types/luna-react/src/server/component/slot.d.ts +15 -0
  59. package/dist/types/luna-react/src/type.d.ts +1 -0
  60. package/docs/events/change.md +21 -7
  61. package/docs/fields/custom-inputs.md +4 -0
  62. package/docs/fields/input.md +9 -3
  63. package/docs/fields/list.md +2 -0
  64. package/docs/fields/specialized-selectors.md +4 -0
  65. package/docs/forms/submit.md +14 -8
  66. package/docs/setup.md +39 -19
  67. package/docs/structure/sections.md +3 -1
  68. package/docs/validation/overview.md +10 -1
  69. package/package.json +7 -7
  70. package/dist/types/luna-react/src/client/component/field/field-with-state.d.ts +0 -13
@@ -0,0 +1,18 @@
1
+ import type { PrimitiveAtom } from 'jotai';
2
+ /**
3
+ * One entry of a record the whole form shares, as an atom of the component that
4
+ * reads it.
5
+ *
6
+ * A family did this before: one atom per name, kept in a module-level cache for
7
+ * the life of the page. That cache never let go on its own, so it had to be
8
+ * emptied by hand -- release atoms, a call in `useStore`, and a comment on the
9
+ * loop to avoid -- and two of the three families grew anyway, a key per row ever
10
+ * added and a key per name ever shown. Made here, the atom goes when the
11
+ * component does, and there is nothing left to release.
12
+ */
13
+ export declare function useEntryAtom<T>(base: PrimitiveAtom<Record<string, T>>, name: string): import("jotai").WritableAtom<NonNullable<T> | undefined, [next: T | undefined], void>;
14
+ /** The same for a record of contributions. See `createContributionAtom`. */
15
+ export declare function useContributionAtom<T>(base: PrimitiveAtom<Record<string, Record<string, T>>>, name: string, options: Readonly<{
16
+ merge: (values: T[]) => T | undefined;
17
+ validateTarget: (target: string) => boolean;
18
+ }>): import("jotai").WritableAtom<T | undefined, [target: string, value: T | undefined], void>;
@@ -1,3 +1,4 @@
1
+ import { type FormEvent } from 'react';
1
2
  import { type Field, type FormStateError, type Nullable, type Schemas, type ZodSchema } from '../../../../luna-core/index';
2
3
  export type FormState<T> = {
3
4
  data: Nullable<T>;
@@ -10,4 +11,4 @@ export type FormActionOptions<T> = {
10
11
  translations?: Record<string, string>;
11
12
  validation?: boolean;
12
13
  };
13
- export declare function useFormState<T, F = Record<string, unknown>>(getSchema: () => readonly [Schemas, Field[]], action?: (formData: F, schema?: ZodSchema) => Promise<FormState<T>>, options?: FormActionOptions<T>): readonly [(payload: FormData) => void, FormState<T>, boolean];
14
+ export declare function useFormState<T, F = Record<string, unknown>>(getSchema: () => readonly [Schemas, Field[]], action?: (formData: F, schema?: ZodSchema) => Promise<FormState<T>>, options?: FormActionOptions<T>): readonly [(payload: FormData) => void, FormState<T>, boolean, (event: FormEvent<HTMLFormElement>) => void];
@@ -0,0 +1,17 @@
1
+ import { type HostEntry } from '../lib/host-value-store';
2
+ /**
3
+ * What the host says about one field. Re-renders this field when its own entry
4
+ * changes, and only then.
5
+ */
6
+ export declare function useHostEntry(name: string): HostEntry;
7
+ /**
8
+ * The same answer, read at the moment it is asked rather than at the moment the
9
+ * component rendered.
10
+ *
11
+ * For a reader that decides once and marks itself done. The store is written
12
+ * from the form's effect, and a parent's effects run after its children's, so a
13
+ * child asking during its own effect is always one commit behind. Asking from
14
+ * the microtask that follows the commit is not: every effect in that commit has
15
+ * run by then, the form's write included.
16
+ */
17
+ export declare function useHostEntryReader(name: string): () => HostEntry;
@@ -1,4 +1,4 @@
1
- import { type AriaAttributes, type CommonProps, type DataAttributes, type DataSource, type Field, type Nullable, type Schema, type Schemas, type Value } from '../../../../luna-core/index';
1
+ import { type AriaAttributes, type CommonProps, type DataAttributes, type DataSource, type Field, type Schema, type Value } from '../../../../luna-core/index';
2
2
  import type { Config, InputChange } from '../../type';
3
3
  export type InputCoreProps = Readonly<{
4
4
  ariaAttributes?: AriaAttributes;
@@ -7,23 +7,22 @@ export type InputCoreProps = Readonly<{
7
7
  context?: Record<string, unknown>;
8
8
  dataAttributes?: DataAttributes;
9
9
  field: Field;
10
- getSchema: () => readonly [Schemas, Field[]];
10
+ getField: (name: string) => Field | undefined;
11
11
  horizontal?: boolean;
12
- onMount: (name: string, schema: Schema, field: Field) => void;
12
+ onRegister: (name: string, schema: Schema, field: Field) => void;
13
13
  onUnmount: (name: string, options?: {
14
14
  keepValue?: boolean;
15
15
  }) => void;
16
16
  onValueChange?: (input: InputChange) => void;
17
+ readOnly?: boolean;
17
18
  translations?: Record<string, string>;
18
- value?: Nullable<Record<string, unknown>>;
19
19
  }>;
20
20
  export declare function useInputCore(props: InputCoreProps, deps: Readonly<{
21
21
  setSource: (target: string, source?: DataSource) => void;
22
22
  setValue: (value: unknown) => void;
23
- shouldSkipOnChange: () => boolean;
24
23
  value: unknown;
25
24
  }>): {
26
- readonly applyChangeEventsRef: import("react").RefObject<((selected: unknown) => void) | null>;
25
+ readonly applyChangeEventsRef: import("react").RefObject<(selected: unknown) => void>;
27
26
  readonly commonProps: {
28
27
  placeholder: string;
29
28
  disabled?: boolean;
@@ -34,9 +33,8 @@ export declare function useInputCore(props: InputCoreProps, deps: Readonly<{
34
33
  readonly entity: string | undefined;
35
34
  readonly hasClickable: boolean;
36
35
  readonly onBlur: (event: React.FocusEvent<HTMLInputElement>) => void;
37
- readonly onValueChangeRef: import("react").RefObject<((value: Value) => void) | null>;
36
+ readonly onValueChangeRef: import("react").RefObject<(value: Value) => void>;
38
37
  readonly setTimeoutRef: (callback: () => void, delay: number) => void;
39
- readonly shouldSkipOnChange: () => boolean;
40
38
  readonly validated: (value: Value) => void;
41
39
  readonly valueRef: import("react").RefObject<unknown>;
42
40
  };
@@ -1,4 +1,4 @@
1
1
  import type { Field, Schema } from '../../../../luna-core/index';
2
- export declare function useInput(field: Field, onMount: (name: string, schema: Schema, field: Field) => void, onUnmount: (name: string, options?: {
2
+ export declare function useInput(field: Field, onRegister: (name: string, schema: Schema, field: Field) => void, onUnmount: (name: string, options?: {
3
3
  keepValue?: boolean;
4
4
  }) => void, translations?: Record<string, string>): import("zod").ZodType<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The latest value a commit left, for the code that runs after it: an event
3
+ * handler, an effect, a callback whose identity has to stay steady across
4
+ * renders.
5
+ *
6
+ * Written from a layout effect and not from the render body, which is the whole
7
+ * point. A render is not a promise that anything reached the screen -- React
8
+ * throws one away when a transition is interrupted or a boundary retries -- and
9
+ * a ref assigned while rendering keeps whatever that discarded render held,
10
+ * which is props nobody ever saw. A commit is the moment a value becomes real,
11
+ * so that is when this writes it. It is also what the React Compiler asks of a
12
+ * component before it will optimize it.
13
+ *
14
+ * Layout and not `useEffect`: every effect of a commit runs after every layout
15
+ * effect of that same commit, so an effect reading this ref reads the value of
16
+ * the render it belongs to, whatever order the hooks were declared in.
17
+ *
18
+ * Reading it while rendering is still wrong, and still reported by
19
+ * `react-hooks/refs`: there the value is one commit old. What a render needs is
20
+ * the value itself.
21
+ */
22
+ export declare function useLatest<T>(value: T): import("react").RefObject<T>;
@@ -1,6 +1,20 @@
1
1
  /**
2
- * Returns the live value of a list item or field path, merging the current
3
- * Jotai form state on top of the initial value tree. Used by previews to
4
- * evaluate `when` conditions reactively against user edits.
2
+ * What a row holds right now: the form's own state over the initial value tree,
3
+ * for a `when` that has to follow what the user types.
4
+ *
5
+ * Subscribed to the row's keys and not to the record. Every field in the form
6
+ * writes into that one record, so a row that read it woke up on every keystroke
7
+ * anywhere and walked every key in the form to find its own. The selection is
8
+ * compared shallowly, so a keystroke outside the row recomputes the pick, finds
9
+ * the same keys, and renders nothing.
10
+ *
11
+ * Only a condition needs this, and only a condition asks for it: the components
12
+ * that own one render the child that calls this hook, so a row with no
13
+ * condition to answer never subscribes at all.
14
+ *
15
+ * The atom is made with the component that reads it and goes with it: jotai
16
+ * caches a selection under the selector function, `pickRow` makes a new one per
17
+ * row, and both are held weakly, so nothing is kept by name for the life of the
18
+ * page.
5
19
  */
6
20
  export declare function useLiveItemValue(name: string, initialValue?: Record<string, unknown> | unknown[] | null): unknown;
@@ -1,4 +1,6 @@
1
- import type { Field, Schema, Schemas } from '../../../../luna-core/index';
2
- export declare function useSchema(): readonly [() => readonly [Schemas, Field[]], (name: string, schema: Schema, field: Field) => void, (name: string, options?: {
1
+ import type { Field, Schema } from '../../../../luna-core/index';
2
+ export declare function useSchema(): readonly [() => readonly [{
3
+ [k: string]: Schema;
4
+ }, Field[]], (name: string) => Field | undefined, (name: string, schema: Schema, field: Field) => void, (name: string, options?: {
3
5
  keepValue?: boolean;
4
6
  }) => void];
@@ -1,6 +1,6 @@
1
- import { type Field, type Nullable } from '../../../../luna-core/index';
2
- export declare function useValue(field: Field, currentValue?: Nullable<Record<string, unknown>>): {
3
- readonly setValue: (newValue: unknown) => void;
1
+ import { type Field } from '../../../../luna-core/index';
2
+ export declare function useValue(field: Field): {
3
+ readonly setValue: (next: unknown) => void;
4
4
  readonly shouldSkipOnChange: () => boolean;
5
5
  readonly value: {} | undefined;
6
6
  };
@@ -1,5 +1,7 @@
1
+ import { type Nullable } from '../../../../luna-core/index';
1
2
  import type { MountedList } from './list-store';
2
- import type { Nullable } from '../../../../luna-core/index';
3
+ /** A list's rows as a consumer is given them, in order. */
4
+ export type ListRows = Array<Record<string, unknown>>;
3
5
  export declare function itemKey(listName: string, stableId: number, leafName: string): string;
4
6
  /**
5
7
  * The three places a leaf can be found, declared in the order they are tried.
@@ -38,4 +40,29 @@ export type ValueSources = {
38
40
  * @param listName - The list's name, already carrying its ancestors' stable
39
41
  * ids (`rules.0.rule`), which is what makes the keys unambiguous.
40
42
  */
41
- export declare function composeListValue(listName: string, { items, leafNames }: MountedList, sources: ValueSources): Array<Record<string, unknown>>;
43
+ export declare function composeListValue(listName: string, { items, leafNames }: MountedList, sources: ValueSources): ListRows;
44
+ /**
45
+ * Every positional name a change to a list's rows can have left out of date,
46
+ * each with what it holds now.
47
+ *
48
+ * A consumer that keeps each report in one record -- the way the docs suggest
49
+ * keeping values -- holds a row's leaves there twice: inside the list's array,
50
+ * and under their own positional names, `items.0.value`, from the reports the
51
+ * leaves made as they were typed in. Removing a row moves every row after it
52
+ * up one position; assigning rows rewrites what every position holds. The
53
+ * array is reported again either way, but the names would go on holding what
54
+ * the rows used to hold, and `resolveEntry` tries a flat name before the path
55
+ * into the array: the next row to read one of them gets a row that is gone.
56
+ *
57
+ * So every name from `from` to the end of the longer of the two lists comes
58
+ * back, with what its position holds now, or `undefined` where no row is left
59
+ * -- whether or not that differs from before, because what the consumer holds
60
+ * is not necessarily what the list last said.
61
+ *
62
+ * @param base - The list's positional name, as the consumer addresses it.
63
+ * @param from - The first position the change can have touched.
64
+ */
65
+ export declare function positionalEntries(base: string, from: number, previous: ListRows, next: ListRows): Array<{
66
+ name: string;
67
+ value: unknown;
68
+ }>;
@@ -1,7 +1,9 @@
1
+ export declare const inputErrorAtom: import("jotai").PrimitiveAtom<Record<string, string[]>> & {
2
+ init: Record<string, string[]>;
3
+ };
1
4
  export declare const clearInputErrorAtom: import("jotai").WritableAtom<null, [names: string[]], void> & {
2
5
  init: null;
3
6
  };
4
7
  export declare const reportErrorAtom: import("jotai").WritableAtom<null, [newValue: Record<string, string[]>], void> & {
5
8
  init: null;
6
9
  };
7
- export declare const reportInputErrorAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<string[] | undefined, [newValue: string[] | undefined], void>>;
@@ -0,0 +1,28 @@
1
+ import type { Nullable } from '../../../../luna-core/index';
2
+ export declare const hostValueAtom: import("jotai").PrimitiveAtom<Nullable<Record<string, unknown>>> & {
3
+ init: Nullable<Record<string, unknown>>;
4
+ };
5
+ /** What the host says about one field, with the sentinel already read. */
6
+ export type HostEntry = {
7
+ /** The host names this field. A field it does not name reads `undefined`. */
8
+ found: boolean;
9
+ value: unknown;
10
+ };
11
+ export declare function readHostEntry(entry: unknown): HostEntry;
12
+ /**
13
+ * One field's entry in that record.
14
+ *
15
+ * Keyed by the name the record actually uses -- the POSITIONAL one. A leaf
16
+ * inside a list is named after its row's stable id, and the two diverge the
17
+ * moment a non-tail row is removed, so the caller translates before it
18
+ * subscribes. That translation lives in a React context, which an atom cannot
19
+ * read; doing it at the call site is what keeps this correct where a memo
20
+ * comparator could not be. See `ListPathContext`.
21
+ *
22
+ * Made by whoever subscribes rather than cached by name, so it goes with the
23
+ * component that asked: a family kept one per name for the life of the page,
24
+ * and this one was never released at all -- 110 keys after ten definitions of
25
+ * ten fields. See `useEntryAtom` for the same move on the records a field
26
+ * writes into.
27
+ */
28
+ export declare function hostEntryAtom(name: string): import("jotai").Atom<unknown>;
@@ -1,7 +1,6 @@
1
1
  export declare const pendingListRowsAtom: import("jotai").PrimitiveAtom<Record<string, Record<string, unknown>[]>> & {
2
2
  init: Record<string, Record<string, unknown>[]>;
3
3
  };
4
- export declare const reportPendingListRowsAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<Record<string, unknown>[] | undefined, [newValue: Record<string, unknown>[] | undefined], void>>;
5
4
  /**
6
5
  * What a list on screen is holding: the stable ids of its live rows, in the
7
6
  * order they are shown, and the name of every leaf a row owns.
@@ -16,7 +15,7 @@ export type MountedList = {
16
15
  * The list's own `hidden`, as its definition declares it.
17
16
  *
18
17
  * Here rather than looked up, because there is nowhere to look it up: the
19
- * schema registry `getSchema` answers from is filled by `onMount` on the
18
+ * schema registry `getField` answers from is filled by `onRegister` on the
20
19
  * input path, and a container never goes through it. That gap is what let a
21
20
  * list keep its values through a hide -- the branch that clears a target
22
21
  * reverting to its static hidden asked only that registry, found nothing,
@@ -27,10 +26,13 @@ export type MountedList = {
27
26
  * rather than a gap: the only target worth clearing is one that was showing.
28
27
  */
29
28
  hidden?: boolean;
30
- /** The list's own `keepValue`, published for the same reason as `hidden`. */
29
+ /**
30
+ * The list's own `advanced.keepValue`, published for the same reason as
31
+ * `hidden` and flattened on the way in: this registry answers about a list
32
+ * that is on screen, not about the JSON that declared it.
33
+ */
31
34
  keepValue?: boolean;
32
35
  };
33
36
  export declare const mountedListsAtom: import("jotai").PrimitiveAtom<Record<string, MountedList>> & {
34
37
  init: Record<string, MountedList>;
35
38
  };
36
- export declare const reportMountedListAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<MountedList | undefined, [newValue: MountedList | undefined], void>>;
@@ -1,6 +1 @@
1
- export type Entry = {
2
- found: boolean;
3
- value: unknown;
4
- };
5
- export declare function resolveEntry(name: string, currentValue: Record<string, unknown> | unknown[]): Entry;
6
1
  export declare function resolveValue(name: string, currentValue: Record<string, unknown> | unknown[]): unknown;
@@ -1,5 +1,11 @@
1
1
  import { type DataSource } from '../../../../luna-core/index';
2
- export declare const reportSourceAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<DataSource | undefined, [target: string, value: DataSource | undefined], void>>;
2
+ export declare const sourceAtom: import("jotai").PrimitiveAtom<Record<string, Record<string, DataSource>>> & {
3
+ init: Record<string, Record<string, DataSource>>;
4
+ };
5
+ export declare const SOURCE_OPTIONS: {
6
+ readonly merge: (values: DataSource[]) => DataSource | undefined;
7
+ readonly validateTarget: (target: string) => boolean;
8
+ };
3
9
  export declare const clearInputSourceAtom: import("jotai").WritableAtom<null, [contributorNames: string[]], void> & {
4
10
  init: null;
5
11
  };
@@ -2,4 +2,3 @@ import type { FieldState } from '../../../../luna-core/index';
2
2
  export declare const fieldStateAtom: import("jotai").PrimitiveAtom<Record<string, FieldState>> & {
3
3
  init: Record<string, FieldState>;
4
4
  };
5
- export declare const reportFieldStateAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<FieldState | undefined, [newValue: FieldState | undefined], void>>;
@@ -1,10 +1,10 @@
1
1
  import { type PrimitiveAtom } from 'jotai';
2
- type NestedAtomFamilyOptions<TInner> = {
3
- merge?: (values: TInner[]) => TInner | undefined;
4
- validateTarget?: (target: string) => boolean;
5
- };
6
2
  export declare function omitKey<T extends Record<string, unknown>>(obj: T, key: string): T;
7
- export declare function createNestedRecordAtomFamily<TInner, TRecord extends Record<string, Record<string, TInner>> = Record<string, Record<string, TInner>>>(baseAtom: PrimitiveAtom<TRecord>, options?: NestedAtomFamilyOptions<TInner>): import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<TInner | undefined, [target: string, value: TInner | undefined], void>>;
3
+ export declare function createEntryAtom<T>(base: PrimitiveAtom<Record<string, T>>, name: string): import("jotai").WritableAtom<NonNullable<T> | undefined, [next: T | undefined], void>;
4
+ export declare function createContributionAtom<T>(base: PrimitiveAtom<Record<string, Record<string, T>>>, name: string, options: Readonly<{
5
+ merge: (values: T[]) => T | undefined;
6
+ validateTarget: (target: string) => boolean;
7
+ }>): import("jotai").WritableAtom<T | undefined, [target: string, value: T | undefined], void>;
8
8
  export declare function createNestedClearAtom<TInner>(baseAtom: PrimitiveAtom<Record<string, Record<string, TInner>>>): import("jotai").WritableAtom<null, [contributorNames: string[]], void> & {
9
9
  init: null;
10
10
  };
@@ -21,6 +21,4 @@ export declare function createAtomStore<T>(initialValue?: Record<string, T>): {
21
21
  bulkReport: import("jotai").WritableAtom<null, [newValue: Record<string, T>], void> & {
22
22
  init: null;
23
23
  };
24
- report: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<NonNullable<T> | undefined, [newValue: T | undefined], void>>;
25
24
  };
26
- export {};
@@ -7,7 +7,6 @@ export declare const clearAllValueAtom: import("jotai").WritableAtom<null, [], v
7
7
  export declare const clearInputValueAtom: import("jotai").WritableAtom<null, [names: string[]], void> & {
8
8
  init: null;
9
9
  };
10
- export declare const reportValueAtom: import("jotai-family").AtomFamily<string, import("jotai").WritableAtom<{} | undefined, [newValue: unknown], void>>;
11
10
  export declare const appliedAutoFillAtom: import("jotai").PrimitiveAtom<Record<string, unknown>> & {
12
11
  init: Record<string, unknown>;
13
12
  };
@@ -1,4 +1,5 @@
1
- export declare function Collapsible({ children, visible, }: Readonly<{
1
+ export declare function Collapsible({ children, onReveal, visible, }: Readonly<{
2
2
  children: React.ReactNode;
3
+ onReveal?: () => void;
3
4
  visible?: boolean;
4
5
  }>): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { type DescriptionTextProps } from './description';
1
2
  import { type Column } from '../../../luna-core/index';
2
3
  import type { Config } from '../type';
3
4
  export declare function Column(props: Readonly<{
@@ -5,5 +6,6 @@ export declare function Column(props: Readonly<{
5
6
  column?: Column;
6
7
  config?: Config;
7
8
  context?: Record<string, unknown>;
9
+ description?: React.ComponentType<DescriptionTextProps>;
8
10
  translations?: Record<string, string>;
9
11
  }>): import("react").JSX.Element;
@@ -1,4 +1,13 @@
1
+ import { type Description as DescriptionType } from '../../../luna-core/index';
2
+ import type { Config } from '../type';
1
3
  export declare function Description(props: Readonly<{
2
4
  children: string | React.ReactNode;
3
5
  title?: string;
4
6
  }>): import("react").JSX.Element;
7
+ export type DescriptionTextProps = Readonly<{
8
+ config?: Config;
9
+ context?: Record<string, unknown>;
10
+ text?: DescriptionType;
11
+ translations?: Record<string, string>;
12
+ }>;
13
+ export declare function DescriptionText(props: DescriptionTextProps): import("react").JSX.Element | null;
@@ -1,5 +1,5 @@
1
1
  import { type Section, type Style } from '../../../../luna-core/index';
2
- export declare function FieldSet(props: Readonly<{
2
+ export declare function StaticFieldSet(props: Readonly<{
3
3
  advanced?: {
4
4
  step?: boolean;
5
5
  };
@@ -3,7 +3,7 @@ import type { Children } from '../../type';
3
3
  export type FieldProps = Readonly<{
4
4
  children: Children;
5
5
  disabled?: boolean;
6
- errors?: Record<string, string[]>;
6
+ errors?: string[];
7
7
  field: Field;
8
8
  lang?: string;
9
9
  style?: Style;
@@ -1,8 +1,9 @@
1
1
  import type { Config } from '../type';
2
2
  import type { Field } from '../../../luna-core/index';
3
- export declare function FieldDescription(props: Readonly<{
3
+ export type DescriptionProps = Readonly<{
4
4
  config?: Config;
5
5
  context?: Record<string, unknown>;
6
6
  field: Field;
7
7
  translations?: Record<string, string>;
8
- }>): import("react").JSX.Element;
8
+ }>;
9
+ export declare function StaticDescription(props: DescriptionProps): import("react").JSX.Element;
@@ -1,5 +1,13 @@
1
+ import { StaticFieldSet } from './field/field-set-static';
1
2
  import { type Definition, type Sections } from '../../../luna-core/index';
2
3
  import type { Config, Control, Slot } from '../type';
4
+ import type { Fields } from '../../../luna-core/index';
5
+ import type { FormEvent } from 'react';
6
+ export type GuardProps = Readonly<{
7
+ children: React.ReactNode;
8
+ fields: Fields;
9
+ }>;
10
+ export declare function StaticGuard(props: GuardProps): import("react").ReactNode;
3
11
  export declare function Form(props: Readonly<{
4
12
  advanced?: {
5
13
  step?: boolean;
@@ -9,8 +17,11 @@ export declare function Form(props: Readonly<{
9
17
  config: Config;
10
18
  control?: Control;
11
19
  definition?: Definition;
20
+ fieldSet?: React.ComponentType<React.ComponentProps<typeof StaticFieldSet>>;
21
+ guard?: React.ComponentType<GuardProps>;
12
22
  isPending?: boolean;
13
23
  noValidate?: boolean;
24
+ onSubmit?: (event: FormEvent<HTMLFormElement>) => void;
14
25
  readOnly?: boolean;
15
26
  sections: Sections;
16
27
  translations?: Record<string, string>;
@@ -7,5 +7,6 @@ export declare function InputBase(props: Readonly<{
7
7
  field: Field;
8
8
  horizontal?: boolean;
9
9
  lang?: string;
10
+ readOnly?: boolean;
10
11
  translations?: Record<string, string>;
11
12
  }>): import("react").ReactNode;
@@ -1,9 +1,11 @@
1
+ import { type DescriptionProps } from './field-description';
1
2
  import type { Config } from '../type';
2
3
  import type { Field } from '../../../luna-core/index';
3
4
  export declare function InputGroup(props: Readonly<{
4
5
  children: React.ReactNode;
5
6
  config?: Config;
6
7
  context?: Record<string, unknown>;
8
+ description?: React.ComponentType<DescriptionProps>;
7
9
  field: Field;
8
10
  horizontal?: boolean;
9
11
  translations?: Record<string, string>;
@@ -1,8 +1,10 @@
1
+ import { type DescriptionProps } from './field-description';
1
2
  import { type Field } from '../../../luna-core/index';
2
3
  import type { Config } from '../type';
3
4
  export declare function InputLabel(props: Readonly<{
4
5
  config?: Config;
5
6
  context?: Record<string, unknown>;
7
+ description?: React.ComponentType<DescriptionProps>;
6
8
  field: Field;
7
9
  horizontal?: boolean;
8
10
  translations?: Record<string, string>;
@@ -1,8 +1,10 @@
1
1
  import { type Fields, type Nullable, type Style } from '../../../../luna-core/index';
2
+ import type { DescriptionTextProps } from '../description';
2
3
  import type { Children, Config } from '../../type';
3
4
  import type { FieldProps } from '../field/field';
4
5
  import type { ListProps } from '../field/field-list';
5
6
  export type SlotComponents = {
7
+ description?: React.ComponentType<DescriptionTextProps>;
6
8
  field: React.ComponentType<FieldProps>;
7
9
  list: React.ComponentType<ListProps>;
8
10
  };
@@ -0,0 +1,3 @@
1
+ import type { Field, FieldState, Fields, List } from '../../../luna-core/index';
2
+ export declare function isEntryHidden(entry: Fields[number], states: Record<string, FieldState>): boolean;
3
+ export declare function isGuardHidden(states: Record<string, FieldState>, fields: Fields, container?: Field | List): boolean;
@@ -0,0 +1,2 @@
1
+ import type { FieldProps } from '../../component/field/field';
2
+ export declare function Field(props: FieldProps): import("react").JSX.Element;
@@ -0,0 +1,2 @@
1
+ import type { ListProps } from '../../component/field/field-list';
2
+ export declare function ListSlot(props: ListProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ export declare const Slot: (props: Readonly<{
2
+ children: import("../../type").Children;
3
+ config: import("../../type").Config;
4
+ context?: Record<string, unknown>;
5
+ disabled?: boolean;
6
+ fields?: import('../../../../luna-core/index').Fields;
7
+ lang?: string;
8
+ onValueChange?: (input: {
9
+ name: string;
10
+ value: unknown;
11
+ }) => void;
12
+ style?: import('../../../../luna-core/index').Style;
13
+ translations?: Record<string, string>;
14
+ value?: import('../../../../luna-core/index').Nullable<Record<string, unknown>>;
15
+ }>) => import("react").JSX.Element;
@@ -12,6 +12,7 @@ export type Children = (props: {
12
12
  dataAttributes?: DataAttributes;
13
13
  field: Field;
14
14
  horizontal?: boolean;
15
+ readOnly?: boolean;
15
16
  }) => React.ReactNode;
16
17
  export type InputConfig<T extends React.ElementType> = {
17
18
  types: string | string[];
@@ -159,7 +159,7 @@ A row's key can name a field that is itself a `list`, in which case its value is
159
159
  Every rule above holds one level down as well: an inner list assigns rather than adds, honours its own `advanced.length`, and keeps the identity of the rows that survive. Two more only come up here:
160
160
 
161
161
  - **A row that names an inner list without giving it rows empties it.** `"checks": []`, a `checks` key holding something that is not rows, and no `checks` key at all all mean the same thing — none — rather than leaving behind what the row in that position had.
162
- - **The consumer is told once for the whole tree**, under the outer list's name, with the inner rows already in it. The inner lists then report their own paths (`groups.0.checks`) as they take their rows, the same way they do when a row is added or removed by hand.
162
+ - **The consumer is told the whole tree at once**, under the outer list's name, with the inner rows already in it, and then every positional name the assignment rewrote (`groups.0.label`, `groups.1.checks.0.v`) with what that position holds now, or nothing where no row is left. A consumer that keeps each report in one record would otherwise go on holding the values of the rows that went away under those names. The inner lists then report their own paths (`groups.0.checks`) as they take their rows, the same way they do when a row is added or removed by hand.
163
163
 
164
164
  This is what the relative syntax cannot do. `"groups/checks"` reaches the inner list of the row the sender is sitting in, and `"groups.0.checks"` reaches the first one; neither can say "every group", so a control that appends a group has to assign the outer list and carry the inner rows along with it.
165
165
 
@@ -237,11 +237,19 @@ It does not matter how the form phrases the hiding. A rule whose state is
237
237
  so the field falls back to its own `hidden`, are the same act and clear the
238
238
  same values -- containers included.
239
239
 
240
+ What the cleared target decided goes with it. A target with `state` rules of its
241
+ own asserted them on the strength of its value, and that value is gone, so its
242
+ rules are undone as if it held nothing: a field it revealed is hidden again --
243
+ and cleared, if it too falls back to `hidden` -- and so on down the chain.
244
+ Showing the first target again brings it back empty, and nothing it had revealed
245
+ comes back with it. A target that keeps its value through the hide keeps what
246
+ its rules decided as well; see below.
247
+
240
248
  #### Keeping a value through a hide
241
249
 
242
- A field that declares `keepValue: true` is put away rather than dropped: hiding
243
- it leaves what it held, showing it again gives it back, and the consumer is
244
- told nothing because nothing was taken.
250
+ A field that declares `advanced.keepValue: true` is put away rather than
251
+ dropped: hiding it leaves what it held, showing it again gives it back, and the
252
+ consumer is told nothing because nothing was taken.
245
253
 
246
254
  ```json
247
255
  {
@@ -249,15 +257,21 @@ told nothing because nothing was taken.
249
257
  "name": "raw",
250
258
  "type": "textarea",
251
259
  "hidden": true,
252
- "keepValue": true
260
+ "advanced": { "keepValue": true }
253
261
  }
254
262
  ```
255
263
 
264
+ It sits in `advanced` beside [`transient`](#transient-controls), which is the
265
+ other key that decides what ends up stored, rather than beside `hidden`: the
266
+ two read as a pair, but `hidden` is what the form does to the field and this is
267
+ what the field is.
268
+
256
269
  It is declared on the FIELD and not on the rule, for the same reason the clear
257
270
  does not care how the hiding was phrased: a field either survives being put
258
271
  away or it does not, and which of several rules happened to hide it this time
259
- says nothing about that. A `list` may declare it too, and then it covers every
260
- value its rows hold.
272
+ says nothing about that. A
273
+ [`list`](../fields/list.md#advanced-configuration-advanced-object) may declare
274
+ it too, and then it covers every value its rows hold.
261
275
 
262
276
  Reach for it where two fields are two views of the same thing and the form
263
277
  swaps between them -- there, hiding one is a change of scenery, not a change of
@@ -60,6 +60,10 @@ its own handlers must call the ones it received, or the form never learns the
60
60
  value changed: validation will not run, and neither will any `change` event the
61
61
  field declares.
62
62
 
63
+ A `readonly` field arrives `disabled` and without a `name`. Its value is still
64
+ submitted, but by the form, in hidden inputs of its own: nothing your component
65
+ renders for it is sent, so it is sent once, whatever your component renders.
66
+
63
67
  ## Forwarding `id` is what gives the field its name
64
68
 
65
69
  The label is rendered by this library, outside your component, as