vultus-core 0.5.2 → 0.5.4

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 (57) hide show
  1. package/README.md +176 -4
  2. package/dist/FieldContext.d.ts +37 -1
  3. package/dist/FieldContext.d.ts.map +1 -1
  4. package/dist/FieldContext.js +68 -11
  5. package/dist/FieldContext.js.map +1 -1
  6. package/dist/field.d.ts +12 -1
  7. package/dist/field.d.ts.map +1 -1
  8. package/dist/field.js +6 -1
  9. package/dist/field.js.map +1 -1
  10. package/dist/fieldGroupValues.d.ts +63 -0
  11. package/dist/fieldGroupValues.d.ts.map +1 -0
  12. package/dist/fieldGroupValues.js +119 -0
  13. package/dist/fieldGroupValues.js.map +1 -0
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +3 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/kindHooks.d.ts +23 -0
  19. package/dist/kindHooks.d.ts.map +1 -0
  20. package/dist/kindHooks.js +38 -0
  21. package/dist/kindHooks.js.map +1 -0
  22. package/dist/kinds.d.ts +19 -14
  23. package/dist/kinds.d.ts.map +1 -1
  24. package/dist/kinds.js +40 -14
  25. package/dist/kinds.js.map +1 -1
  26. package/dist/tree.d.ts +17 -0
  27. package/dist/tree.d.ts.map +1 -1
  28. package/dist/tree.js +27 -0
  29. package/dist/tree.js.map +1 -1
  30. package/dist/useArg.d.ts +6 -0
  31. package/dist/useArg.d.ts.map +1 -1
  32. package/dist/useArg.js +6 -2
  33. package/dist/useArg.js.map +1 -1
  34. package/dist/useFieldGroup.d.ts +80 -0
  35. package/dist/useFieldGroup.d.ts.map +1 -0
  36. package/dist/useFieldGroup.js +266 -0
  37. package/dist/useFieldGroup.js.map +1 -0
  38. package/dist/useOneOfTreeField.d.ts +79 -0
  39. package/dist/useOneOfTreeField.d.ts.map +1 -0
  40. package/dist/useOneOfTreeField.js +117 -0
  41. package/dist/useOneOfTreeField.js.map +1 -0
  42. package/dist/useStoredField.d.ts +9 -0
  43. package/dist/useStoredField.d.ts.map +1 -1
  44. package/dist/useStoredField.js +7 -5
  45. package/dist/useStoredField.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/FieldContext.tsx +73 -11
  48. package/src/field.ts +25 -5
  49. package/src/fieldGroupValues.ts +169 -0
  50. package/src/index.ts +7 -1
  51. package/src/kindHooks.ts +52 -0
  52. package/src/kinds.ts +38 -14
  53. package/src/tree.ts +34 -0
  54. package/src/useArg.ts +6 -2
  55. package/src/useFieldGroup.ts +346 -0
  56. package/src/useOneOfTreeField.ts +182 -0
  57. package/src/useStoredField.ts +10 -6
package/README.md CHANGED
@@ -31,7 +31,8 @@ npm install vultus-core
31
31
  `initialValueOf`, `valueTypeOf`, `sanitizeStored`); the argument layer
32
32
  (the value types, `memoryStore`, `localStorageStore`, `ArgStoreProvider` /
33
33
  `useArgStore`, `useArg`, `useArgWriter`, `setArg` / `resetArg`, `argOf`,
34
- `useStoredField` / `useStoredFields`); `InternalLinkContext` /
34
+ `useStoredField` / `useStoredFields`); field groups (`useFieldGroup` and
35
+ its `FieldGroup` type); `InternalLinkContext` /
35
36
  `useInternalLink` / `InternalLinkComponent`, `MessageSinkContext` /
36
37
  `useMessageSink` / `MessageSink`, and `VultusComponentsContext` /
37
38
  `useVultusComponents` / `SpinnerScope` / `useSpinnerKey` /
@@ -257,6 +258,12 @@ its descendants matches (with only the children that stay), and the rest
257
258
  is trimmed, leaving the nodes given unchanged. Implicit initial value: the
258
259
  empty list.
259
260
 
261
+ A `TreeNode`'s `choosable` (default `true`; a folder when `false`) is for the one-of tree, below;
262
+ this hook ignores it, so one node list can feed both kinds. Its helpers: `canChooseNode(node,
263
+ choose)` (choosable, enabled, and, under `choose: 'leaves'`, no children), `choosableValues(nodes,
264
+ choose)` (the values it accepts, in pre-order) and `treePath(nodes, value)` (the values from the
265
+ root down to `value`, inclusive; `[]` when no node has it).
266
+
260
267
  ```ts
261
268
  const field = useManyOfTreeField({
262
269
  id: 'categories',
@@ -265,6 +272,39 @@ const field = useManyOfTreeField({
265
272
  field.toggle('a'); // field.value === ['a'] — 'topmost' collapses the subtree to the parent
266
273
  ```
267
274
 
275
+ ### one-of tree (`useOneOfTreeField`)
276
+
277
+ The value is a single node id (`nodes`, the many-of tree's `TreeNode`), or `''` meaning nothing
278
+ chosen (Praeceptor's category picker, a table of contents). A node is choosable when its own
279
+ `choosable` is not `false` (a folder — a part heading with no page, a category group — is
280
+ `choosable: false`; the many-of tree ignores it, so one node list can feed both kinds), it is
281
+ enabled, and, under `choose: 'leaves'` (default `'any'`), has no children (`canChooseNode`,
282
+ `choosableValues`, `treePath` — see many-of tree, above). `setValue` refuses, with a warning (as
283
+ `useOneOfField` refuses a disabled choice): a value naming no node, a node that cannot be
284
+ chosen, and `''` unless `optional` (default `false`, as `oneOf`'s value is never empty); `clear()`
285
+ requests `''`. Controls add `nodes` (each with `choosable`, `current` — it is the value — and
286
+ `onPath` — an ancestor of it) and `byValue`, as the many-of tree's; `current` (the chosen node, or
287
+ undefined when the value is empty or names no node), `path` (the nodes from the root down to
288
+ `current`, inclusive; empty when there is none), `previous` / `next` (the choosable nodes just
289
+ before and after `current`'s own position in tree pre-order — so a `current` that cannot itself
290
+ be chosen, e.g. a value naming a folder or a node a context change disabled, still gets its true
291
+ neighbours; undefined at the ends, and with no `current`, `previous` undefined and `next` the
292
+ first choosable node), `optional`, `choose` and `expandedKeys`
293
+ (from `expand`, default `'selected'` — the value's ancestors, so a table of contents opens where
294
+ the reader is; `'top'`, `'none'` and `'all'` as the many-of tree's). The field's current state is
295
+ the chosen node's description, unless `valueDescriptions` words it differently (as `oneOf`).
296
+ Implicit initial value: `initialValue`, else `''` when `optional`, else the first node that can be
297
+ chosen in tree pre-order, else `''` (no node can be chosen; as `oneOf`'s undefined choice).
298
+
299
+ ```ts
300
+ const field = useOneOfTreeField({
301
+ id: 'category',
302
+ nodes: [{ value: 'geo', choosable: false, children: [{ value: 'sik' }, { value: 'ter' }] }],
303
+ });
304
+ field.value; // 'sik' — the first node that can be chosen ('geo' is a folder)
305
+ field.next?.value; // 'ter'
306
+ ```
307
+
268
308
  ### text (`useTextField`)
269
309
 
270
310
  The value is a string. `control: 'input' | 'textarea'` (default `'input'`)
@@ -285,7 +325,7 @@ const field = useTextField({ id: 'search', control: 'input' });
285
325
  A `FieldDefinition` is a field's options in their "inside" form (no `value`
286
326
  plus `onChange` or `commit`), tagged with a required `kind` — one of
287
327
  `'boolean'`, `'mixed-boolean'`, `'oneOf'`, `'manyOf'`, `'numeric'`,
288
- `'range'`, `'manyOfTree'`, `'text'` (the same strings `FieldControls.type` already
328
+ `'range'`, `'manyOfTree'`, `'oneOfTree'`, `'text'` (the same strings `FieldControls.type` already
289
329
  carries). `kind` stays optional on each hook's own options — a hook knows
290
330
  its own kind, and throws if a definition names a different one — but is
291
331
  required once a field becomes plain data: a module declares its filters as a
@@ -333,7 +373,7 @@ b)`) say how a value is kept as a string and compared; equality is the
333
373
  type's own meaning (a list as a set, a range by both ends), used everywhere
334
374
  two values of that type are compared — including whether a value is the
335
375
  default, so it's left out of the store. `valueTypeOf(kind)` gives each field
336
- kind's: `booleanType` (`'1'` / `'0'`), `stringType` (one-of, text),
376
+ kind's: `booleanType` (`'1'` / `'0'`), `stringType` (one-of, one-of tree, text),
337
377
  `stringSetType` (many-of, many-of tree — a sorted comma list; a *present but empty*
338
378
  value reads as the empty list, which differs from the key being absent
339
379
  altogether), `numberType`, `rangeType` (`from..to`). A mixed-boolean field
@@ -381,7 +421,9 @@ is the definition's `id`, its value type comes from the kind, its default
381
421
  is `initialValueOf`. It returns `{ options, isDefault, reset }` — `options`
382
422
  is the definition in its outside form (`value` plus `onChange`, ready for
383
423
  the kind's hook), with the stored raw value made to fit the definition as
384
- it is now (`sanitizeStored`: unknown choices or nodes dropped, numbers and
424
+ it is now (`sanitizeStored`: unknown choices or nodes dropped; a one-of
425
+ tree value naming no node, a folder or a node it cannot choose, or `''`
426
+ when the field is not optional, replaced by the initial value; numbers and
385
427
  ranges clamped, and rounded when `integer`). A value that needed fixing up
386
428
  warns once per field and per raw value — not again on every render, and not
387
429
  at all when the key is simply absent (then the default applies, and an
@@ -392,6 +434,21 @@ definitions), `changedCount`, and `resetAll(method)` (one write for every
392
434
  field). Being a summary, it never warns — only `useStoredField` on the
393
435
  field itself does.
394
436
 
437
+ **Links.** Every kind's hook gives its controls `hrefFor(value)`: the link that requesting `value`
438
+ amounts to, or `undefined` where nothing gives one. With `value` + `onChange` or `value` +
439
+ `commit`, the caller passes it; inside the field it is always `undefined` (an `hrefFor` passed
440
+ anyway is ignored). `FieldControls` has it optional (`hrefFor?(value)`), so controls built by
441
+ hand need none; a widget calls `field.hrefFor?.(value)`. `useStoredField` wires its own from the
442
+ store (`useArg`'s `hrefFor`, itself from the store's), so a field stored over a URL store
443
+ (vultus-react-router's) links each value it could be set to, e.g.
444
+ `field.hrefFor?.(field.choices[0].value)` next to the field's own widget. A link built from
445
+ `hrefFor` is as fresh as the render that built it: a URL that changes without re-rendering its
446
+ component (another key of the query, the path) leaves it stale. Render an app's own links (a
447
+ previous / next pair) in a component that re-renders on location changes (with react-router,
448
+ one that calls `useLocation`), or compute them at interaction time (vultus-antd's
449
+ `OneOfTreeList` asks again as the pointer enters a link, on its focus, a button press, its
450
+ context menu, a modified click).
451
+
395
452
  ```tsx
396
453
  const store = memoryStore();
397
454
 
@@ -410,6 +467,121 @@ function Category() {
410
467
  }
411
468
  ```
412
469
 
470
+ ## Field groups
471
+
472
+ A field's options sometimes depend on another field's value: a print dialog offers some paper
473
+ sizes only for one layout, a filter shows a sub-filter only when another is on. With hooks, an
474
+ option takes a value computed from the other field's controls. Definitions as data say it
475
+ through the field context: `useFieldGroup(definitions)` runs a list of definitions together, and
476
+ every option function of each one receives `ctx.values`, the group's values by field id.
477
+
478
+ ```tsx
479
+ const printOptions: FieldDefinition[] = [
480
+ { kind: 'oneOf', id: 'layout', choices: [{ value: 'rendered' }, { value: 'original' }] },
481
+ { kind: 'oneOf', id: 'paper', choices: (ctx) => (ctx.values.layout === 'rendered' ? A_SIZES : ALL_SIZES) },
482
+ { kind: 'numeric', id: 'copies', min: 1, visible: (ctx) => ctx.values.layout === 'original' },
483
+ ];
484
+
485
+ function PrintDialog() {
486
+ const group = useFieldGroup(printOptions);
487
+ return <>{group.fields.map((f) => <FieldWidget key={f.id} field={f} item />)}</>;
488
+ }
489
+ ```
490
+
491
+ It returns `fields`, each field's controls exactly as its kind's hook returns them, in definition
492
+ order; `byId`, the same controls by id; `values`, the shown values by id as of this render (a
493
+ plain object, which keeps its identity until a shown value changes, so an effect keyed on it runs
494
+ only then); `isDefault(id)` (an id not in the group throws), `changedCount` and `resetAll()`
495
+ ("more filters (3)", "clear all"). The controls are typed `FieldControls`, which every widget
496
+ takes; for a kind's own controls (a tree's `nodes`, numeric's `format`), cast by kind:
497
+ `group.byId.toc as OneOfTreeFieldControls<string>`.
498
+
499
+ **References.** Every option function of a group's definition receives `ctx.values` (`label`,
500
+ `description`, `placeholder`, `visible` / `hidden`, `enabled` / `disabled`, `choices`, a tree's
501
+ `nodes`, `min` / `max`, numeric's `format(n, ctx)`, ...), beside the keys of the app's own field
502
+ context (the nearest `FieldContextProvider`), which keep working. A function the controls keep
503
+ and call later (`format`, through `field.format(n)`) sees the group's values too. `initialValue`
504
+ cannot read them. In a plain `FieldDefinition` the context is untyped (`any`); an app wanting
505
+ `ctx.values` typed declares `values` in its context type (`FieldDefinition<Ctx>`,
506
+ `createFieldContext<Ctx>()`).
507
+
508
+ **The fallback rule.** A field's shown value is its kept value checked as a stored value is
509
+ (`sanitizeStored`), with its options evaluated in the group's context. A value that another
510
+ field's change made invalid (a paper size the layout no longer offers) shows the field's
511
+ fallback, and the other fields see that fallback. Nothing is rewritten: the kept value shows
512
+ again once it is valid again (the layout back). A hidden field keeps its value, and the others
513
+ still see it.
514
+
515
+ **Defaults follow the options.** A field's default is its `initialValue`, else its kind's
516
+ implicit initial value from its options as they are now (the first enabled choice of its current
517
+ `choices`, a tree's first choosable node, `min`, ...), so it moves with the fields those options
518
+ read. A field never set, or set to its current default, keeps nothing and shows its current
519
+ default as it moves. `isDefault(id)` is useStoredFields' rule: nothing kept, or the kept value or
520
+ the value shown equals the current default.
521
+
522
+ **Errors.** These fail loudly, naming the field and the option where there is one:
523
+
524
+ - an option reading an id not in the group: `Field 'paper': option 'choices': no field 'layot'
525
+ in the group (fields: layout, paper, copies)`;
526
+ - reading `ctx.values` outside a group, with a `FieldContextProvider` above: `... ctx.values is
527
+ only available inside a field group`; with none, a function declaring `ctx` gets the existing
528
+ `... is a function of the field context, but no FieldContextProvider is above it`;
529
+ - `initialValue` reading `ctx.values`: `... initialValue cannot read other fields' values`;
530
+ - fields whose values depend on each other (A's choices read B, and B's read A): `Fields 'a' ->
531
+ 'b' -> 'a' depend on each other's values`. Options that never affect a value (`visible`,
532
+ `enabled`, `label`, `unit`, `format`, ...) cannot form a cycle;
533
+ - two definitions with the same id; a mixed-boolean definition (it shows an aggregate of other
534
+ data, not a value of its own); an app field context that already has `values` (the group
535
+ would shadow it);
536
+ - a list of another shape than the first render's. The hooks run per field, so the ids and kinds
537
+ stay the same, in the same order: to swap lists, remount the component running the group with
538
+ a React `key`. The definitions' contents may change, and new objects of the same shape on
539
+ every render (inline literals) are fine.
540
+
541
+ **Storage**, for the whole group, is the runner's choice. By default the group keeps the values
542
+ in its own state, as a store keeps them: a field without a kept value shows its current default,
543
+ and a value set equal to it is not kept. A fallback is silent here, as it is the designed
544
+ behaviour, not bad input. `resetAll()` clears every kept value.
545
+
546
+ With `useFieldGroup(definitions, { stored: true })` the values live in the nearest `ArgStore`
547
+ (the URL through vultus-react-router's `<ReactRouterArgStore>`, or localStorage) under each
548
+ field's id, as `useStoredField` keeps a single field there, all read in one store subscription:
549
+
550
+ ```tsx
551
+ const filters: FieldDefinition[] = [
552
+ { kind: 'text', id: 'search' },
553
+ { kind: 'oneOfTree', id: 'toc', optional: true, nodes: CHAPTERS, widget: 'list', visible: (ctx) => ctx.values.search === '' },
554
+ ];
555
+
556
+ function Filters() {
557
+ const group = useFieldGroup(filters, { stored: true }); // ?search=...&toc=...
558
+ return <>{group.fields.map((f) => <FieldWidget key={f.id} field={f} />)}</>;
559
+ }
560
+ ```
561
+
562
+ An absent key reads as the field's current default, and a value equal to it is written as an
563
+ absent key (with `'replace'`). The default compared with is the one under the store's values at
564
+ the moment of the write (a store reads its own writes back at once), so, as in the group's own
565
+ state, `layout.setValue('original'); paper.setValue('a4')` in one handler keeps `a4` even where
566
+ `a4` was paper's default under the old layout. Each field's `hrefFor` links a value through the
567
+ store, as rendered (`undefined` where the store has no links). The fallback rule is the same,
568
+ and the stored value is never rewritten (the search above hides `toc`; `?toc=` stays). A stored
569
+ raw value that does not parse, or does not fit its field, warns once, in the render that first
570
+ reads it, as `useStoredField`'s does; one that does not parse reads as the current default. A
571
+ value that another field's change makes fall back later does not warn: that is how a group
572
+ behaves, not bad input. Nor does a raw value the group itself wrote:
573
+ `layout.setValue('rendered'); paper.setValue('letter')` in one handler (Letter offered under the
574
+ original layout) stores `letter`, which falls back at once; the warnings are for raw values from
575
+ elsewhere (the URL, another tab). `resetAll(method = 'replace')` removes every field's key in one store
576
+ write (one history entry). Switching between the two storages is a change of shape: it throws
577
+ (remount with a React `key`).
578
+
579
+ **Rendering.** vultus-antd's `<FieldWidget>` renders the controls (by the definition's `widget`
580
+ hint; `item` for a labelled form item), in order as above or by id
581
+ (`<FieldWidget field={group.byId.paper} />`). A request goes through the kind's own checks (a
582
+ paper size the layout does not offer is refused with a warning, as the kind's hook alone refuses
583
+ it). The group then keeps the value and re-renders, so every option is computed again.
584
+
413
585
  ## Components
414
586
 
415
587
  `VultusComponentsContext` (default `{}`) / `useVultusComponents()` is how an app lends its own
@@ -6,6 +6,22 @@ import { type JSX, type ReactNode } from 'react';
6
6
  export declare const NO_FIELD_CONTEXT: unique symbol;
7
7
  /** The field context as the hooks hold it: the guarded value, or the mark that there is none. */
8
8
  export type FieldCtx = object | typeof NO_FIELD_CONTEXT;
9
+ /**
10
+ * What the guard throws for a key no provider gave, so readingContext can tell it from the
11
+ * function's own errors. Exported for the field group (fieldGroupValues), whose own reads that
12
+ * fail (an id not in the group, `values` read from `initialValue`) get the field and option in
13
+ * front the same way; not part of the package's API.
14
+ */
15
+ export declare class MissingKeyError extends Error {
16
+ }
17
+ /**
18
+ * `raw` guarded against reading a key it lacks: such a read throws a MissingKeyError with
19
+ * `missing(key)` as its message, which readingContext puts the field and the option in front
20
+ * of. `has` says whether `raw` has the key; by default inherited members (`toString`) count,
21
+ * and `Object.hasOwn` makes them missing too. Probes and symbol keys read as usual. Exported for
22
+ * the field group's `values` (an id not in the group); not part of the package's API.
23
+ */
24
+ export declare function guardKeys(raw: object, missing: (key: string) => string, has?: (target: object, key: string) => boolean): object;
9
25
  /**
10
26
  * Runs `read`, a call of a definition's function with the field context. A key it reads that
11
27
  * no provider gave throws with `where` (`Field '<id>': option '<name>'`) in front of the
@@ -33,6 +49,26 @@ export declare function createFieldContext<Ctx extends object>(): {
33
49
  children?: ReactNode;
34
50
  }) => JSX.Element;
35
51
  };
36
- /** The field context the hooks resolve functions with: the guarded value, or NO_FIELD_CONTEXT. */
52
+ /**
53
+ * A field context with `base`'s keys (none for NO_FIELD_CONTEXT) and `extra`'s, guarded as a
54
+ * provider's value is: how a field group gives its fields' functions `values` beside the app's
55
+ * own keys. `extra`'s properties are copied as defined, so a getter among them runs when its
56
+ * key is read, not here. A key of `extra` that `base` already has throws, `where` in front,
57
+ * instead of shadowing the app's value; every key the copy takes is checked (non-enumerable
58
+ * and symbol keys too).
59
+ */
60
+ export declare function extendFieldContext(base: FieldCtx, extra: Record<string, unknown>, where: string): object;
61
+ /**
62
+ * Runs `run` with `ctx` as the field context: a hook it calls gets `ctx` from useFieldContext
63
+ * instead of the providers' value. It is for a hook that calls other hooks in its own render,
64
+ * each with its own context (a field group runs each field's kind hook with the group's); the
65
+ * components rendered below do not see it. `run` must be synchronous: the previous context
66
+ * comes back when it returns or throws. Calls nest.
67
+ */
68
+ export declare function withFieldContext<T>(ctx: FieldCtx, run: () => T): T;
69
+ /**
70
+ * The field context the hooks resolve functions with: the one withFieldContext gives while it
71
+ * runs, else the providers' guarded value, or NO_FIELD_CONTEXT.
72
+ */
37
73
  export declare function useFieldContext(): FieldCtx;
38
74
  //# sourceMappingURL=FieldContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FieldContext.d.ts","sourceRoot":"","sources":["../src/FieldContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAAO,MAAwC,CAAC;AAE/E,iGAAiG;AACjG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,gBAAgB,CAAC;AA2CxD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAOjE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,eAShG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,SAAS,MAAM,KAAK;IACxD,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC;CACjF,CAEA;AAED,kGAAkG;AAClG,wBAAgB,eAAe,IAAI,QAAQ,CAE1C"}
1
+ {"version":3,"file":"FieldContext.d.ts","sourceRoot":"","sources":["../src/FieldContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAErF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAAO,MAAwC,CAAC;AAE/E,iGAAiG;AACjG,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,gBAAgB,CAAC;AAwBxD;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EAChC,GAAG,GAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAwC,GAC7E,MAAM,CAOR;AAcD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAOjE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,eAShG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,SAAS,MAAM,KAAK;IACxD,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;KAAE,KAAK,GAAG,CAAC,OAAO,CAAC;CACjF,CAEA;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMxG;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAQlE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAI1C"}
@@ -18,24 +18,41 @@ const PROBED_KEYS = new Set([
18
18
  function isProbe(key) {
19
19
  return PROBED_KEYS.has(key) || key.startsWith('@@');
20
20
  }
21
- /** What the guard throws for a key no provider gave, so readingContext can tell it from the function's own errors. */
22
- class MissingKeyError extends Error {
21
+ /**
22
+ * What the guard throws for a key no provider gave, so readingContext can tell it from the
23
+ * function's own errors. Exported for the field group (fieldGroupValues), whose own reads that
24
+ * fail (an id not in the group, `values` read from `initialValue`) get the field and option in
25
+ * front the same way; not part of the package's API.
26
+ */
27
+ export class MissingKeyError extends Error {
23
28
  }
24
29
  /**
25
- * The context value as the definitions' functions receive it: reading a key that no provider
26
- * gave throws, naming it and the keys present, so a forgotten value fails loudly instead of
27
- * passing `undefined` along. A key provided with the value `undefined` is fine.
30
+ * `raw` guarded against reading a key it lacks: such a read throws a MissingKeyError with
31
+ * `missing(key)` as its message, which readingContext puts the field and the option in front
32
+ * of. `has` says whether `raw` has the key; by default inherited members (`toString`) count,
33
+ * and `Object.hasOwn` makes them missing too. Probes and symbol keys read as usual. Exported for
34
+ * the field group's `values` (an id not in the group); not part of the package's API.
28
35
  */
29
- function guard(raw) {
36
+ export function guardKeys(raw, missing, has = (target, key) => key in target) {
30
37
  return new Proxy(raw, {
31
38
  get(target, key, receiver) {
32
- if (typeof key === 'string' && !isProbe(key) && !(key in target)) {
33
- throw new MissingKeyError(`Field context: '${key}' was never provided (provided: ${Object.keys(target).join(', ')})`);
34
- }
39
+ if (typeof key === 'string' && !isProbe(key) && !has(target, key))
40
+ throw new MissingKeyError(missing(key));
35
41
  return Reflect.get(target, key, receiver);
36
42
  },
37
43
  });
38
44
  }
45
+ /**
46
+ * The context value as the definitions' functions receive it: reading a key that no provider
47
+ * gave throws, naming it and the keys present, so a forgotten value fails loudly instead of
48
+ * passing `undefined` along. A key provided with the value `undefined` is fine. `values` is the
49
+ * key a field group adds (extendFieldContext), so reading it from any other context says so.
50
+ */
51
+ function guard(raw) {
52
+ return guardKeys(raw, (key) => (key === 'values'
53
+ ? 'ctx.values is only available inside a field group'
54
+ : `Field context: '${key}' was never provided (provided: ${Object.keys(raw).join(', ')})`));
55
+ }
39
56
  /**
40
57
  * Runs `read`, a call of a definition's function with the field context. A key it reads that
41
58
  * no provider gave throws with `where` (`Field '<id>': option '<name>'`) in front of the
@@ -75,8 +92,48 @@ export function FieldContextProvider({ value, children }) {
75
92
  export function createFieldContext() {
76
93
  return { Provider: FieldContextProvider };
77
94
  }
78
- /** The field context the hooks resolve functions with: the guarded value, or NO_FIELD_CONTEXT. */
95
+ /**
96
+ * A field context with `base`'s keys (none for NO_FIELD_CONTEXT) and `extra`'s, guarded as a
97
+ * provider's value is: how a field group gives its fields' functions `values` beside the app's
98
+ * own keys. `extra`'s properties are copied as defined, so a getter among them runs when its
99
+ * key is read, not here. A key of `extra` that `base` already has throws, `where` in front,
100
+ * instead of shadowing the app's value; every key the copy takes is checked (non-enumerable
101
+ * and symbol keys too).
102
+ */
103
+ export function extendFieldContext(base, extra, where) {
104
+ const raw = base === NO_FIELD_CONTEXT ? {} : { ...base };
105
+ for (const key of Reflect.ownKeys(extra)) {
106
+ if (Object.hasOwn(raw, key))
107
+ throw new Error(`${where}: the field context already has '${String(key)}'; a field group provides it`);
108
+ }
109
+ return guard(Object.defineProperties(raw, Object.getOwnPropertyDescriptors(extra)));
110
+ }
111
+ /** The context withFieldContext gives the hooks it is running, if any. */
112
+ let override;
113
+ /**
114
+ * Runs `run` with `ctx` as the field context: a hook it calls gets `ctx` from useFieldContext
115
+ * instead of the providers' value. It is for a hook that calls other hooks in its own render,
116
+ * each with its own context (a field group runs each field's kind hook with the group's); the
117
+ * components rendered below do not see it. `run` must be synchronous: the previous context
118
+ * comes back when it returns or throws. Calls nest.
119
+ */
120
+ export function withFieldContext(ctx, run) {
121
+ const previous = override;
122
+ override = ctx;
123
+ try {
124
+ return run();
125
+ }
126
+ finally {
127
+ override = previous;
128
+ }
129
+ }
130
+ /**
131
+ * The field context the hooks resolve functions with: the one withFieldContext gives while it
132
+ * runs, else the providers' guarded value, or NO_FIELD_CONTEXT.
133
+ */
79
134
  export function useFieldContext() {
80
- return useContext(FieldContext)?.guarded ?? NO_FIELD_CONTEXT;
135
+ // Called under an override too, so that the hooks' order never depends on one.
136
+ const provided = useContext(FieldContext);
137
+ return override ?? provided?.guarded ?? NO_FIELD_CONTEXT;
81
138
  }
82
139
  //# sourceMappingURL=FieldContext.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FieldContext.js","sourceRoot":"","sources":["../src/FieldContext.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAA4B,MAAM,OAAO,CAAC;AAErF;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAkB,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAW/E,MAAM,YAAY,GAAG,aAAa,CAAuB,SAAS,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB;CAC1G,CAAC,CAAC;AAEH,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,sHAAsH;AACtH,MAAM,eAAgB,SAAQ,KAAK;CAAG;AAEtC;;;;GAIG;AACH,SAAS,KAAK,CAAC,GAAW;IACxB,OAAO,IAAI,KAAK,CAAC,GAAG,EAAE;QACpB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ;YACvB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,eAAe,CAAC,mBAAmB,GAAG,mCAAmC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxH,CAAC;YACD,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAI,KAAa,EAAE,IAAa;IAC5D,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,eAAe;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QACtG,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAA2C;IAC/F,MAAM,KAAK,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IACvC,4FAA4F;IAC5F,8FAA8F;IAC9F,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5B,MAAM,GAAG,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QACxC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACtC,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACnB,OAAO,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,QAAQ,YAAG,QAAQ,GAAyB,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB;IAGhC,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC;AAC5C,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,eAAe;IAC7B,OAAO,UAAU,CAAC,YAAY,CAAC,EAAE,OAAO,IAAI,gBAAgB,CAAC;AAC/D,CAAC"}
1
+ {"version":3,"file":"FieldContext.js","sourceRoot":"","sources":["../src/FieldContext.tsx"],"names":[],"mappings":"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"}
package/dist/field.d.ts CHANGED
@@ -9,7 +9,7 @@ import type { ConfirmationSpec, Decision, ErrorRoutesRegistry, ExecutionContext,
9
9
  * Design notes: unitas docs/2026-10-05-vultus-field-model-design.md.
10
10
  */
11
11
  /** The field kinds (FieldControls.type and a FieldDefinition's `kind`). */
12
- export type FieldKind = 'boolean' | 'mixed-boolean' | 'oneOf' | 'manyOf' | 'numeric' | 'range' | 'manyOfTree' | 'text';
12
+ export type FieldKind = 'boolean' | 'mixed-boolean' | 'oneOf' | 'manyOf' | 'numeric' | 'range' | 'manyOfTree' | 'oneOfTree' | 'text';
13
13
  /** The widget to render a field with: a name, or a name with display options overriding its defaults. */
14
14
  export type WidgetHint = string | {
15
15
  name: string;
@@ -67,6 +67,7 @@ export type FieldStorage<TValue, TRequest = TValue, RouteId extends string = str
67
67
  value?: never;
68
68
  onChange?: never;
69
69
  commit?: never;
70
+ hrefFor?: never;
70
71
  }
71
72
  /** Outside, changed locally: the caller owns the value and applies `onChange`. */
72
73
  | {
@@ -74,6 +75,8 @@ export type FieldStorage<TValue, TRequest = TValue, RouteId extends string = str
74
75
  onChange: (value: TRequest) => void;
75
76
  initialValue?: never;
76
77
  commit?: never;
78
+ /** The link that requesting `value` amounts to, where the caller has one (e.g. a URL store). */
79
+ hrefFor?: (value: TRequest) => string | undefined;
77
80
  }
78
81
  /** Outside, committed immediately: `commit.fire` stores the change (pending, messages, confirmation). */
79
82
  | {
@@ -81,6 +84,7 @@ export type FieldStorage<TValue, TRequest = TValue, RouteId extends string = str
81
84
  commit: FieldCommit<TRequest, RouteId>;
82
85
  initialValue?: never;
83
86
  onChange?: never;
87
+ hrefFor?: (value: TRequest) => string | undefined;
84
88
  };
85
89
  export type FieldOptions<TValue, TRequest = TValue, RouteId extends string = string, Ctx = any> = FieldBaseOptions<TValue, Ctx> & FieldStorage<TValue, TRequest, RouteId, Ctx>;
86
90
  /** What a field widget receives: everything it shows, and the one way to change the value. */
@@ -106,6 +110,13 @@ export interface FieldControls<TValue, TRequest = TValue> {
106
110
  messages: Message[];
107
111
  /** The confirmation a widget must obtain before requesting `next`, if any. */
108
112
  confirmationFor(next: TRequest): ConfirmationSpec | undefined;
113
+ /**
114
+ * The link that requesting `value` amounts to, where whoever keeps the value gave one (a URL
115
+ * store does); `undefined` otherwise, and always inside the field. Every kind's hook provides
116
+ * it; optional here so that controls built by hand (as before vultus-core 0.5.3) need none, so
117
+ * a widget calls it as `field.hrefFor?.(value)`.
118
+ */
119
+ hrefFor?(value: TRequest): string | undefined;
109
120
  widget: WidgetHint | undefined;
110
121
  }
111
122
  export type FieldStorageMode = 'inside' | 'local' | 'commit';
@@ -1 +1 @@
1
- {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
package/dist/field.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import { useCallback, useState } from 'react';
2
2
  import { getReason, getVerdict, invertDecision } from './decision.js';
3
3
  import { useFieldContext } from './FieldContext.js';
4
4
  import { resolveIn } from './resolve.js';
@@ -86,6 +86,10 @@ export function useFieldCore(type, opts, registry) {
86
86
  const spec = opts.commit?.confirmation;
87
87
  return typeof spec === 'function' ? spec(next) : spec;
88
88
  };
89
+ // Inside the field nothing outside keeps the value, so nothing gives a link: an `hrefFor` that
90
+ // came anyway (untyped options; the types forbid it) is ignored.
91
+ const hrefForOpt = mode === 'inside' ? undefined : opts.hrefFor;
92
+ const hrefFor = useCallback((next) => hrefForOpt?.(next), [hrefForOpt]);
89
93
  return {
90
94
  type,
91
95
  id: opts.id,
@@ -102,6 +106,7 @@ export function useFieldCore(type, opts, registry) {
102
106
  commits: mode === 'commit',
103
107
  messages: mode === 'commit' ? (setter.messages ?? []) : [],
104
108
  confirmationFor,
109
+ hrefFor,
105
110
  widget: opts.widget,
106
111
  };
107
112
  }
package/dist/field.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field.js","sourceRoot":"","sources":["../src/field.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import { type FieldCtx } from './FieldContext.js';
2
+ import { type FieldDefinition } from './kinds.js';
3
+ /**
4
+ * One render's values of a field group (see useFieldGroup): each field's shown value and
5
+ * default, computed on demand by id from the kept values, with the definitions' options
6
+ * evaluated in the group's context, and each computed once.
7
+ */
8
+ export interface GroupValues {
9
+ /**
10
+ * The field context the group's option functions receive: the app's own keys (guarded as a
11
+ * provider's value is) and `values`, the fields' shown values by id.
12
+ */
13
+ ctx: object;
14
+ /**
15
+ * A field's shown value: its kept value, else its current default, made fit for its options
16
+ * as they are now (sanitizeStored); one that no longer fits shows the field's fallback. The
17
+ * kept value itself is never touched, so it shows again once it fits again.
18
+ */
19
+ valueOf(id: string): unknown;
20
+ /**
21
+ * A field's default: its `initialValue` (which cannot read `values`), else its kind's
22
+ * implicit initial value from its options as they are now (the first enabled choice, `min`,
23
+ * ...), so it moves with the fields those options read.
24
+ */
25
+ defaultOf(id: string): unknown;
26
+ /** The shown values by id, each computed when first read; `ctx.values` is the same object. */
27
+ values: Readonly<Record<string, unknown>>;
28
+ }
29
+ /** What createGroupValues computes the values from. */
30
+ export interface GroupValuesParams {
31
+ /** The group's fields, as data: unique ids, no mixed-boolean. */
32
+ definitions: readonly FieldDefinition[];
33
+ /** The app's field context (useFieldContext), which `values` is added to. */
34
+ appCtx: FieldCtx;
35
+ /** A field's kept value; `undefined` when none is kept, so the field shows its current default. */
36
+ kept(id: string): unknown | undefined;
37
+ /**
38
+ * Where a kept value's sanitizing warnings go, with the field's id: the caller decides whether
39
+ * to show them (and how often). A default made fit is the definer's, not kept input, so it
40
+ * never warns.
41
+ */
42
+ warn(id: string, message: string): void;
43
+ }
44
+ /**
45
+ * The values of a field group for one render, computed from its definitions and kept values.
46
+ *
47
+ * Every option function of the definitions receives `ctx`: the app's field context and
48
+ * `values`, the fields' shown values by id. A field's value is computed when an option (or the
49
+ * caller) first reads it, and kept for the rest of this snapshot; a new render makes a new one.
50
+ * The shown value is the kept value (else the current default) checked against the field's
51
+ * options as they are now, as a store's value is (sanitizeStored); one that no longer fits
52
+ * shows the fallback, which the other fields see too.
53
+ *
54
+ * Throws here for two definitions with the same id and for a mixed-boolean definition (naming
55
+ * the field), and for an app context that already has `values`. Throws when read, naming the
56
+ * field and the option, for an option reading an id not in the group and for `initialValue`
57
+ * reading `values`. Fields whose values depend on each other throw when read, naming the cycle
58
+ * (`Fields 'a' -> 'b' -> 'a' depend on each other's values`): a field's value and default are in
59
+ * progress while its options run, and reading one in progress is a cycle. Options that never
60
+ * affect a value (`visible`, `label`, `unit`, ...) are not run here, so they cannot form one.
61
+ */
62
+ export declare function createGroupValues({ definitions, appCtx, kept, warn }: GroupValuesParams): GroupValues;
63
+ //# sourceMappingURL=fieldGroupValues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fieldGroupValues.d.ts","sourceRoot":"","sources":["../src/fieldGroupValues.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkD,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClG,OAAO,EAAkC,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAElF;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,8FAA8F;IAC9F,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3C;AAED,uDAAuD;AACvD,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACxC,6EAA6E;IAC7E,MAAM,EAAE,QAAQ,CAAC;IACjB,mGAAmG;IACnG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACtC;;;;OAIG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,iBAAiB,GAAG,WAAW,CA4ErG"}