@stapel/forms-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/MODULE.md +172 -0
  3. package/README.md +207 -0
  4. package/dist/api/export.d.ts +66 -0
  5. package/dist/api/export.d.ts.map +1 -0
  6. package/dist/api/export.js +138 -0
  7. package/dist/api/export.js.map +1 -0
  8. package/dist/api/extensions.d.ts +8 -0
  9. package/dist/api/extensions.d.ts.map +1 -0
  10. package/dist/api/extensions.js +8 -0
  11. package/dist/api/extensions.js.map +1 -0
  12. package/dist/api/formsApi.d.ts +113 -0
  13. package/dist/api/formsApi.d.ts.map +1 -0
  14. package/dist/api/formsApi.js +81 -0
  15. package/dist/api/formsApi.js.map +1 -0
  16. package/dist/api/generated/schema.d.ts +987 -0
  17. package/dist/api/generated/schema.d.ts.map +1 -0
  18. package/dist/api/generated/schema.js +2 -0
  19. package/dist/api/generated/schema.js.map +1 -0
  20. package/dist/api/types.d.ts +222 -0
  21. package/dist/api/types.d.ts.map +1 -0
  22. package/dist/api/types.js +14 -0
  23. package/dist/api/types.js.map +1 -0
  24. package/dist/default/ConfigField.d.ts +32 -0
  25. package/dist/default/ConfigField.d.ts.map +1 -0
  26. package/dist/default/ConfigField.js +101 -0
  27. package/dist/default/ConfigField.js.map +1 -0
  28. package/dist/default/ErrorAlert.d.ts +9 -0
  29. package/dist/default/ErrorAlert.d.ts.map +1 -0
  30. package/dist/default/ErrorAlert.js +27 -0
  31. package/dist/default/ErrorAlert.js.map +1 -0
  32. package/dist/default/FormBuilderPane.d.ts +36 -0
  33. package/dist/default/FormBuilderPane.d.ts.map +1 -0
  34. package/dist/default/FormBuilderPane.js +70 -0
  35. package/dist/default/FormBuilderPane.js.map +1 -0
  36. package/dist/default/FormsListPane.d.ts +18 -0
  37. package/dist/default/FormsListPane.d.ts.map +1 -0
  38. package/dist/default/FormsListPane.js +28 -0
  39. package/dist/default/FormsListPane.js.map +1 -0
  40. package/dist/default/ResponsesPane.d.ts +21 -0
  41. package/dist/default/ResponsesPane.d.ts.map +1 -0
  42. package/dist/default/ResponsesPane.js +118 -0
  43. package/dist/default/ResponsesPane.js.map +1 -0
  44. package/dist/default/StapelForm.d.ts +76 -0
  45. package/dist/default/StapelForm.d.ts.map +1 -0
  46. package/dist/default/StapelForm.js +118 -0
  47. package/dist/default/StapelForm.js.map +1 -0
  48. package/dist/default/fields.d.ts +11 -0
  49. package/dist/default/fields.d.ts.map +1 -0
  50. package/dist/default/fields.js +273 -0
  51. package/dist/default/fields.js.map +1 -0
  52. package/dist/default/index.d.ts +42 -0
  53. package/dist/default/index.d.ts.map +1 -0
  54. package/dist/default/index.js +37 -0
  55. package/dist/default/index.js.map +1 -0
  56. package/dist/default/slots.d.ts +59 -0
  57. package/dist/default/slots.d.ts.map +1 -0
  58. package/dist/default/slots.js +25 -0
  59. package/dist/default/slots.js.map +1 -0
  60. package/dist/default/theme.d.ts +23 -0
  61. package/dist/default/theme.d.ts.map +1 -0
  62. package/dist/default/theme.js +38 -0
  63. package/dist/default/theme.js.map +1 -0
  64. package/dist/default/types.d.ts +12 -0
  65. package/dist/default/types.d.ts.map +1 -0
  66. package/dist/default/types.js +2 -0
  67. package/dist/default/types.js.map +1 -0
  68. package/dist/flows/errors.d.ts +12 -0
  69. package/dist/flows/errors.d.ts.map +1 -0
  70. package/dist/flows/errors.js +13 -0
  71. package/dist/flows/errors.js.map +1 -0
  72. package/dist/flows/registry.d.ts +22 -0
  73. package/dist/flows/registry.d.ts.map +1 -0
  74. package/dist/flows/registry.js +20 -0
  75. package/dist/flows/registry.js.map +1 -0
  76. package/dist/headless/FormBuilder.d.ts +100 -0
  77. package/dist/headless/FormBuilder.d.ts.map +1 -0
  78. package/dist/headless/FormBuilder.js +278 -0
  79. package/dist/headless/FormBuilder.js.map +1 -0
  80. package/dist/headless/FormFill.d.ts +88 -0
  81. package/dist/headless/FormFill.d.ts.map +1 -0
  82. package/dist/headless/FormFill.js +279 -0
  83. package/dist/headless/FormFill.js.map +1 -0
  84. package/dist/headless/FormList.d.ts +34 -0
  85. package/dist/headless/FormList.d.ts.map +1 -0
  86. package/dist/headless/FormList.js +37 -0
  87. package/dist/headless/FormList.js.map +1 -0
  88. package/dist/headless/FormsProvider.d.ts +19 -0
  89. package/dist/headless/FormsProvider.d.ts.map +1 -0
  90. package/dist/headless/FormsProvider.js +15 -0
  91. package/dist/headless/FormsProvider.js.map +1 -0
  92. package/dist/headless/ResponsesTable.d.ts +82 -0
  93. package/dist/headless/ResponsesTable.d.ts.map +1 -0
  94. package/dist/headless/ResponsesTable.js +195 -0
  95. package/dist/headless/ResponsesTable.js.map +1 -0
  96. package/dist/i18n/errorsMap.d.ts +10 -0
  97. package/dist/i18n/errorsMap.d.ts.map +1 -0
  98. package/dist/i18n/errorsMap.js +20 -0
  99. package/dist/i18n/errorsMap.js.map +1 -0
  100. package/dist/i18n/es.d.ts +7 -0
  101. package/dist/i18n/es.d.ts.map +1 -0
  102. package/dist/i18n/es.js +118 -0
  103. package/dist/i18n/es.js.map +1 -0
  104. package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
  105. package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
  106. package/dist/i18n/generated/errors.es.gen.js +79 -0
  107. package/dist/i18n/generated/errors.es.gen.js.map +1 -0
  108. package/dist/i18n/generated/errors.gen.d.ts +485 -0
  109. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  110. package/dist/i18n/generated/errors.gen.js +246 -0
  111. package/dist/i18n/generated/errors.gen.js.map +1 -0
  112. package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
  113. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
  114. package/dist/i18n/generated/errors.ru.gen.js +79 -0
  115. package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
  116. package/dist/i18n/keys.d.ts +141 -0
  117. package/dist/i18n/keys.d.ts.map +1 -0
  118. package/dist/i18n/keys.js +233 -0
  119. package/dist/i18n/keys.js.map +1 -0
  120. package/dist/i18n/ru.d.ts +7 -0
  121. package/dist/i18n/ru.d.ts.map +1 -0
  122. package/dist/i18n/ru.js +127 -0
  123. package/dist/i18n/ru.js.map +1 -0
  124. package/dist/index.d.ts +61 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +55 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/model/context.d.ts +11 -0
  129. package/dist/model/context.d.ts.map +1 -0
  130. package/dist/model/context.js +16 -0
  131. package/dist/model/context.js.map +1 -0
  132. package/dist/model/mutations.d.ts +135 -0
  133. package/dist/model/mutations.d.ts.map +1 -0
  134. package/dist/model/mutations.js +223 -0
  135. package/dist/model/mutations.js.map +1 -0
  136. package/dist/model/queries.d.ts +59 -0
  137. package/dist/model/queries.d.ts.map +1 -0
  138. package/dist/model/queries.js +117 -0
  139. package/dist/model/queries.js.map +1 -0
  140. package/dist/model/queryKeys.d.ts +28 -0
  141. package/dist/model/queryKeys.d.ts.map +1 -0
  142. package/dist/model/queryKeys.js +23 -0
  143. package/dist/model/queryKeys.js.map +1 -0
  144. package/dist/model/runtime.d.ts +28 -0
  145. package/dist/model/runtime.d.ts.map +1 -0
  146. package/dist/model/runtime.js +14 -0
  147. package/dist/model/runtime.js.map +1 -0
  148. package/dist/widgets/configForms.d.ts +112 -0
  149. package/dist/widgets/configForms.d.ts.map +1 -0
  150. package/dist/widgets/configForms.js +211 -0
  151. package/dist/widgets/configForms.js.map +1 -0
  152. package/dist/widgets/registry.d.ts +83 -0
  153. package/dist/widgets/registry.d.ts.map +1 -0
  154. package/dist/widgets/registry.js +27 -0
  155. package/dist/widgets/registry.js.map +1 -0
  156. package/dist/widgets/validate.d.ts +57 -0
  157. package/dist/widgets/validate.d.ts.map +1 -0
  158. package/dist/widgets/validate.js +173 -0
  159. package/dist/widgets/validate.js.map +1 -0
  160. package/llms.txt +124 -0
  161. package/manifest.json +1038 -0
  162. package/package.json +108 -0
  163. package/src/analytics/generated/events.json +7 -0
  164. package/src/api/export.ts +195 -0
  165. package/src/api/extensions.ts +7 -0
  166. package/src/api/formsApi.ts +329 -0
  167. package/src/api/generated/schema.ts +989 -0
  168. package/src/api/types.ts +276 -0
  169. package/src/default/ConfigField.tsx +227 -0
  170. package/src/default/ErrorAlert.tsx +46 -0
  171. package/src/default/FormBuilderPane.tsx +414 -0
  172. package/src/default/FormsListPane.tsx +144 -0
  173. package/src/default/ResponsesPane.tsx +332 -0
  174. package/src/default/StapelForm.tsx +377 -0
  175. package/src/default/fields.tsx +454 -0
  176. package/src/default/index.ts +71 -0
  177. package/src/default/slots.ts +78 -0
  178. package/src/default/theme.tsx +65 -0
  179. package/src/default/types.ts +12 -0
  180. package/src/flows/errors.ts +16 -0
  181. package/src/flows/registry.ts +32 -0
  182. package/src/headless/FormBuilder.tsx +453 -0
  183. package/src/headless/FormFill.tsx +396 -0
  184. package/src/headless/FormList.tsx +74 -0
  185. package/src/headless/FormsProvider.tsx +20 -0
  186. package/src/headless/ResponsesTable.tsx +341 -0
  187. package/src/i18n/errorsMap.ts +31 -0
  188. package/src/i18n/es.ts +140 -0
  189. package/src/i18n/generated/errors.es.gen.ts +85 -0
  190. package/src/i18n/generated/errors.gen.ts +277 -0
  191. package/src/i18n/generated/errors.json +583 -0
  192. package/src/i18n/generated/errors.ru.gen.ts +85 -0
  193. package/src/i18n/keys.ts +264 -0
  194. package/src/i18n/ru.ts +150 -0
  195. package/src/index.ts +184 -0
  196. package/src/model/context.tsx +28 -0
  197. package/src/model/mutations.ts +387 -0
  198. package/src/model/queries.ts +154 -0
  199. package/src/model/queryKeys.ts +61 -0
  200. package/src/model/runtime.ts +48 -0
  201. package/src/widgets/registry.ts +101 -0
  202. package/src/widgets/validate.ts +224 -0
  203. package/tsconfig.json +26 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Zero-flow registry shim (slim wave §21/S3). stapel-forms annotates no
3
+ * `@flow_step` yet — its backend `docs/flows.json` carries no `forms.*`
4
+ * flows, so `gen:flows` skips emission for this pair (no `flows/generated/`
5
+ * scaffolding). This hand-written shim preserves the pair's public
6
+ * flow-registry surface at its zero-flow shape.
7
+ *
8
+ * When the backend annotates flows, `pnpm gen:flows` emits
9
+ * `./generated/flows.gen.ts` again — replace these exports with re-exports
10
+ * from it (the shapes match by construction) and delete this file.
11
+ */
12
+ export declare const FORMS_FLOWS: {};
13
+ /** Canonical flow ids present in flows.json (none yet — see above). */
14
+ export type FormsFlowId = keyof typeof FORMS_FLOWS;
15
+ export type FormsFlowSpec = (typeof FORMS_FLOWS)[FormsFlowId];
16
+ export interface FlowEndpoint {
17
+ readonly method: string;
18
+ readonly path: string;
19
+ }
20
+ /** All HTTP endpoints a flow touches, in step order (for the contract test / MSW). */
21
+ export declare function flowEndpoints(id: FormsFlowId): readonly FlowEndpoint[];
22
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/flows/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,IAAc,CAAC;AAEvC,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,WAAW,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,WAAW,CAAC,CAAC;AAE9D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,EAAE,EAAE,WAAW,GAAG,SAAS,YAAY,EAAE,CAOtE"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Zero-flow registry shim (slim wave §21/S3). stapel-forms annotates no
3
+ * `@flow_step` yet — its backend `docs/flows.json` carries no `forms.*`
4
+ * flows, so `gen:flows` skips emission for this pair (no `flows/generated/`
5
+ * scaffolding). This hand-written shim preserves the pair's public
6
+ * flow-registry surface at its zero-flow shape.
7
+ *
8
+ * When the backend annotates flows, `pnpm gen:flows` emits
9
+ * `./generated/flows.gen.ts` again — replace these exports with re-exports
10
+ * from it (the shapes match by construction) and delete this file.
11
+ */
12
+ export const FORMS_FLOWS = {};
13
+ /** All HTTP endpoints a flow touches, in step order (for the contract test / MSW). */
14
+ export function flowEndpoints(id) {
15
+ // Same widened body as the generated registry's — valid for the zero-flow
16
+ // shape AND correct once flows exist.
17
+ const spec = FORMS_FLOWS[id];
18
+ return spec ? spec.steps.flatMap((s) => s.endpoints) : [];
19
+ }
20
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/flows/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAW,CAAC;AAYvC,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,EAAe;IAC3C,0EAA0E;IAC1E,sCAAsC;IACtC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAEd,CAAC;IACd,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,100 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ActionAvailability, LoadState, StapelApiError } from "@stapel/core";
3
+ import type { ConfigFieldSpec, FieldKind, FormFieldDef, FormRow, FormSchemaMeta, FormState } from "../api/types.js";
4
+ /** One row of the builder's field list, with everything the UI needs to draw
5
+ * and judge it. */
6
+ export interface BuilderField {
7
+ readonly field: FormFieldDef;
8
+ /**
9
+ * The kind's entry in the server's catalogue, or `undefined` when the
10
+ * catalogue has not loaded yet or does not list this kind at all (a stored
11
+ * schema can outlive a host's `FIELD_KINDS` allowlist).
12
+ */
13
+ readonly kindInfo: FieldKind | undefined;
14
+ /** The kind's config declaration, straight from
15
+ * `stapel_attributes.config_form()` via `GET /field-kinds`. Empty for a
16
+ * builder-less kind. */
17
+ readonly configFields: readonly ConfigFieldSpec[];
18
+ /**
19
+ * True when this field's options cannot be edited here. TWO server signals
20
+ * feed it, and they are different facts:
21
+ *
22
+ * - `registered: false` — the host allowlisted a kind the attributes
23
+ * registry does not carry. The field is still listed, because a stored
24
+ * schema may already use it and a builder that dropped the kind would
25
+ * silently drop the field.
26
+ * - `fields: []` — the kind is registered but declares no config form at
27
+ * all (this is how `convertible_unit` arrives).
28
+ *
29
+ * Either way the field stays LISTED, reorderable and removable, and stays
30
+ * authorable through the draft PUT.
31
+ */
32
+ readonly builderLess: boolean;
33
+ /** Config-widget kinds in this field's declaration that the SKIN has no
34
+ * editor for. Named so the UI can say which options it is not showing rather
35
+ * than presenting a partial form as a complete one. */
36
+ readonly unsupportedConfigKeys: readonly string[];
37
+ }
38
+ /** The bag `<FormBuilder>` hands its render prop (spec §8). */
39
+ export interface FormBuilderBag {
40
+ /** The form row. A failed read is never "no such form" — LoadState law. */
41
+ readonly state: LoadState<FormRow>;
42
+ /** The draft under edit: the row's `draft_schema` when there is one, else
43
+ * the active version's schema as a starting point, else empty. */
44
+ readonly fields: readonly BuilderField[];
45
+ readonly meta: FormSchemaMeta;
46
+ /** True when the local draft differs from what the server holds. */
47
+ readonly isDirty: boolean;
48
+ addField(kind: string): void;
49
+ removeField(slug: string): void;
50
+ /** Move a field to a new index (drag-reorder, or a pair of arrow buttons —
51
+ * field ORDER is schema order). */
52
+ moveField(slug: string, toIndex: number): void;
53
+ /** Patch a field's own attributes (`slug`, `name`, `mandatory`, …). */
54
+ updateField(slug: string, patch: Partial<FormFieldDef>): void;
55
+ /** Set one config key of one field. `undefined` REMOVES the key, which is
56
+ * not the same as writing `null`: the engine reads an absent key as "use my
57
+ * own default". */
58
+ setFieldConfig(slug: string, key: string, value: unknown): void;
59
+ setMeta(patch: Partial<FormSchemaMeta>): void;
60
+ /**
61
+ * The kinds the builder may offer, from the server's catalogue: allowed by
62
+ * this deployment, carried by the attributes registry, and declaring a
63
+ * config form. `LoadState` rather than a bare array — a catalogue that
64
+ * failed to load must not read as "this deployment has no field kinds".
65
+ */
66
+ readonly availableKinds: LoadState<readonly FieldKind[]>;
67
+ /** The config-WIDGET vocabulary (`config_form.FIELD_KINDS`) and the params
68
+ * each widget understands, for a skin rendering the config rows. */
69
+ readonly configWidgets: Readonly<Record<string, readonly string[]>>;
70
+ readonly save: ActionAvailability;
71
+ doSave(): void;
72
+ readonly publish: ActionAvailability;
73
+ doPublish(): void;
74
+ readonly isSaving: boolean;
75
+ readonly isPublishing: boolean;
76
+ setState(next: FormState): void;
77
+ rotateLink(): void;
78
+ /** The last refusal from save/publish/state/rotate — a `StapelApiError` so
79
+ * the skin can branch with `hasErrorCode` (`forms_duplicate_slug` carries
80
+ * `params.slug`, `forms_invalid_schema` carries `params.key`, …). */
81
+ readonly error: StapelApiError | null;
82
+ refetch(): void;
83
+ }
84
+ /**
85
+ * Headless form builder — the authoring surface, renderless and DATA-DRIVEN.
86
+ *
87
+ * There is no per-kind hand-written form anywhere in this component or in the
88
+ * skin that renders it: a field's options come from `GET /field-kinds`, which
89
+ * serves `stapel_attributes.config_form()` verbatim. Until stapel-forms 0.2.0
90
+ * there was no such route and the pair had to mirror those declarations in
91
+ * TypeScript — a table that drifts silently. Reading the registry makes the
92
+ * declaration the single source of truth again: a type registered through
93
+ * `EXTRA_TYPES` shows up here with no client release at all (spec §8).
94
+ */
95
+ export declare function FormBuilder(props: {
96
+ workspaceId: string;
97
+ formId: string;
98
+ children: (bag: FormBuilderBag) => ReactNode;
99
+ }): ReactNode;
100
+ //# sourceMappingURL=FormBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormBuilder.d.ts","sourceRoot":"","sources":["../../src/headless/FormBuilder.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,OAAO,KAAK,EACV,kBAAkB,EAClB,SAAS,EACT,cAAc,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,YAAY,EACZ,OAAO,EAEP,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAC;AAUzB;mBACmB;AACnB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;IACzC;;4BAEwB;IACxB,QAAQ,CAAC,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IAClD;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;2DAEuD;IACvD,QAAQ,CAAC,qBAAqB,EAAE,SAAS,MAAM,EAAE,CAAC;CACnD;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IACnC;sEACkE;IAClE,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;uCACmC;IACnC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,uEAAuE;IACvE,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;IAC9D;;uBAEmB;IACnB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAChE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAE9C;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;IACzD;wEACoE;IACpE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IAEpE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,MAAM,IAAI,IAAI,CAAC;IACf,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,SAAS,IAAI,IAAI,CAAC;IAClB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAE/B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IAEnB;;yEAEqE;IACrE,QAAQ,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,OAAO,IAAI,IAAI,CAAC;CACjB;AA2DD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,SAAS,CAAC;CAC9C,GAAG,SAAS,CAoQZ"}
@@ -0,0 +1,278 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { actionAvailable, actionBlocked, isStapelApiError, loadStateFromQuery, mapLoad, requireLoaded, } from "@stapel/core";
3
+ import { useFieldKinds, useForm } from "../model/queries.js";
4
+ import { usePublishForm, useRotateLink, useSaveDraft, useSetFormState, } from "../model/mutations.js";
5
+ import { FORMS_I18N_KEYS } from "../i18n/keys.js";
6
+ /**
7
+ * The config-WIDGET kinds the `/default` skin can draw (`ConfigField`).
8
+ *
9
+ * Upstream's widget vocabulary is 13 entries; the skin implements 11. The two
10
+ * it does not — `hierarchical_options` (a tree editor) and `timestamp_array` —
11
+ * make the individual config ROW unrenderable, not the whole kind, so a field
12
+ * declaring one still gets its other options and the UI names what it is not
13
+ * showing. Kept here rather than imported from `/default` so the headless bag
14
+ * can report it without pulling antd into the main bundle.
15
+ */
16
+ const SKIN_CONFIG_WIDGETS = new Set([
17
+ "number",
18
+ "text",
19
+ "checkbox",
20
+ "translatable_text",
21
+ "number_options",
22
+ "string_options",
23
+ "color_options",
24
+ "select",
25
+ "select_options_with_default",
26
+ "max_selected_dropdown",
27
+ "timestamp",
28
+ ]);
29
+ /**
30
+ * The config a freshly-added field starts with: every default the SERVER's
31
+ * declaration carries, and nothing else.
32
+ *
33
+ * Keys with no declared default stay ABSENT rather than written as `null` —
34
+ * the engine reads an absent key as "use my own default", and writing one
35
+ * changes stored behaviour.
36
+ */
37
+ function defaultConfigFor(kind) {
38
+ const config = {};
39
+ for (const spec of kind?.fields ?? []) {
40
+ if (spec.default !== undefined)
41
+ config[spec.name] = spec.default;
42
+ }
43
+ return config;
44
+ }
45
+ /** A slug that does not collide with anything already in the draft. */
46
+ function freeSlug(kind, taken) {
47
+ const base = kind === "header" ? "heading" : kind;
48
+ for (let n = 1;; n += 1) {
49
+ const candidate = `${base}_${n}`;
50
+ if (!taken.has(candidate))
51
+ return candidate;
52
+ }
53
+ }
54
+ /** The draft a form starts from: its own scratchpad if it has one, otherwise
55
+ * nothing. The ACTIVE version is deliberately not copied in — editing a live
56
+ * form starts from the draft the admin last saved, and a form with no draft is
57
+ * a form nobody has started editing. */
58
+ function initialSchema(row) {
59
+ return row?.draft_schema ?? { fields: [], meta: {} };
60
+ }
61
+ /**
62
+ * Headless form builder — the authoring surface, renderless and DATA-DRIVEN.
63
+ *
64
+ * There is no per-kind hand-written form anywhere in this component or in the
65
+ * skin that renders it: a field's options come from `GET /field-kinds`, which
66
+ * serves `stapel_attributes.config_form()` verbatim. Until stapel-forms 0.2.0
67
+ * there was no such route and the pair had to mirror those declarations in
68
+ * TypeScript — a table that drifts silently. Reading the registry makes the
69
+ * declaration the single source of truth again: a type registered through
70
+ * `EXTRA_TYPES` shows up here with no client release at all (spec §8).
71
+ */
72
+ export function FormBuilder(props) {
73
+ const query = useForm(props.workspaceId, props.formId);
74
+ const state = loadStateFromQuery(query);
75
+ const row = state.status === "ready" ? state.data : undefined;
76
+ const kindsQuery = useFieldKinds(props.workspaceId);
77
+ const kindsState = loadStateFromQuery(kindsQuery);
78
+ const catalogue = kindsState.status === "ready" ? kindsState.data : undefined;
79
+ const kindsBySlug = useMemo(() => {
80
+ const map = new Map();
81
+ for (const kind of catalogue?.kinds ?? [])
82
+ map.set(kind.kind, kind);
83
+ return map;
84
+ }, [catalogue]);
85
+ const saveMutation = useSaveDraft();
86
+ const publishMutation = usePublishForm();
87
+ const stateMutation = useSetFormState();
88
+ const rotateMutation = useRotateLink();
89
+ const [schema, setSchema] = useState(() => initialSchema(row));
90
+ const [error, setError] = useState(null);
91
+ // Adopt the server's draft when the form first loads, and after any save —
92
+ // but never mid-edit, which would throw away what the admin is typing. The
93
+ // ref tracks WHICH server draft has been adopted, so a background refetch
94
+ // that returns the same draft is not an adoption event.
95
+ const adopted = useRef(null);
96
+ useEffect(() => {
97
+ if (row === undefined)
98
+ return;
99
+ const fingerprint = JSON.stringify(row.draft_schema ?? null);
100
+ if (adopted.current === fingerprint)
101
+ return;
102
+ adopted.current = fingerprint;
103
+ setSchema(initialSchema(row));
104
+ }, [row]);
105
+ const isDirty = useMemo(() => JSON.stringify(schema) !== JSON.stringify(initialSchema(row)), [schema, row]);
106
+ const fields = useMemo(() => schema.fields.map((field) => {
107
+ const kindInfo = kindsBySlug.get(field.kind);
108
+ const configFields = kindInfo?.fields ?? [];
109
+ return {
110
+ field,
111
+ kindInfo,
112
+ configFields,
113
+ // Both server signals collapse to one rendering decision here, but
114
+ // they are reported separately on `kindInfo` so a skin can word them
115
+ // differently: "this deployment does not know this kind" is not the
116
+ // same news as "this kind has no options".
117
+ builderLess: kindInfo === undefined ||
118
+ kindInfo.registered === false ||
119
+ configFields.length === 0,
120
+ unsupportedConfigKeys: configFields
121
+ .filter((spec) => !SKIN_CONFIG_WIDGETS.has(spec.kind))
122
+ .map((spec) => spec.name),
123
+ };
124
+ }), [schema, kindsBySlug]);
125
+ const addField = useCallback((kind) => {
126
+ setSchema((current) => {
127
+ const taken = new Set(current.fields.map((f) => f.slug));
128
+ const config = defaultConfigFor(kindsBySlug.get(kind));
129
+ const field = {
130
+ slug: freeSlug(kind, taken),
131
+ kind,
132
+ name: "",
133
+ mandatory: false,
134
+ ...(Object.keys(config).length > 0 ? { config } : {}),
135
+ };
136
+ return { ...current, fields: [...current.fields, field] };
137
+ });
138
+ setError(null);
139
+ }, [kindsBySlug]);
140
+ const removeField = useCallback((slug) => {
141
+ setSchema((current) => ({
142
+ ...current,
143
+ fields: current.fields.filter((f) => f.slug !== slug),
144
+ }));
145
+ setError(null);
146
+ }, []);
147
+ const moveField = useCallback((slug, toIndex) => {
148
+ setSchema((current) => {
149
+ const from = current.fields.findIndex((f) => f.slug === slug);
150
+ if (from === -1)
151
+ return current;
152
+ const moving = current.fields[from];
153
+ if (moving === undefined)
154
+ return current;
155
+ const rest = current.fields.filter((_, index) => index !== from);
156
+ const clamped = Math.max(0, Math.min(toIndex, rest.length));
157
+ return {
158
+ ...current,
159
+ fields: [...rest.slice(0, clamped), moving, ...rest.slice(clamped)],
160
+ };
161
+ });
162
+ setError(null);
163
+ }, []);
164
+ const updateField = useCallback((slug, patch) => {
165
+ setSchema((current) => ({
166
+ ...current,
167
+ fields: current.fields.map((f) => f.slug === slug ? { ...f, ...patch } : f),
168
+ }));
169
+ setError(null);
170
+ }, []);
171
+ const setFieldConfig = useCallback((slug, key, value) => {
172
+ setSchema((current) => ({
173
+ ...current,
174
+ fields: current.fields.map((f) => {
175
+ if (f.slug !== slug)
176
+ return f;
177
+ // `undefined` REMOVES the key. An absent config key means "the
178
+ // engine's own default"; writing null means "this value", and the
179
+ // two are different stored schemas. Rebuilt by filtering rather
180
+ // than `delete` (a dynamic delete deoptimizes the object shape).
181
+ const current = f.config ?? {};
182
+ const config = Object.fromEntries(Object.entries(current).filter(([existing]) => existing !== key));
183
+ if (value !== undefined)
184
+ config[key] = value;
185
+ return { ...f, config };
186
+ }),
187
+ }));
188
+ setError(null);
189
+ }, []);
190
+ const setMeta = useCallback((patch) => {
191
+ setSchema((current) => ({ ...current, meta: { ...current.meta, ...patch } }));
192
+ setError(null);
193
+ }, []);
194
+ const isSaving = saveMutation.isPending;
195
+ const isPublishing = publishMutation.isPending;
196
+ const save = useMemo(() => requireLoaded(state, () => {
197
+ if (isSaving)
198
+ return actionBlocked(FORMS_I18N_KEYS.builderSaving);
199
+ if (!isDirty)
200
+ return actionBlocked(FORMS_I18N_KEYS.builderNoChanges);
201
+ return actionAvailable();
202
+ }), [state, isSaving, isDirty]);
203
+ const publish = useMemo(() => requireLoaded(state, () => {
204
+ if (isPublishing)
205
+ return actionBlocked(FORMS_I18N_KEYS.builderPublishing);
206
+ if (schema.fields.length === 0) {
207
+ return actionBlocked(FORMS_I18N_KEYS.builderEmptySchema);
208
+ }
209
+ // Publishing what the server holds, not what is on screen: an unsaved
210
+ // draft would publish the PREVIOUS text while the admin looks at the
211
+ // new one. Save first, then publish — two acts, in that order.
212
+ if (isDirty)
213
+ return actionBlocked(FORMS_I18N_KEYS.builderUnsavedDraft);
214
+ return actionAvailable();
215
+ }), [state, isPublishing, isDirty, schema.fields.length]);
216
+ const onError = useCallback((caught) => {
217
+ // See mutations.ts: a fault that is not a StapelApiError has no code to
218
+ // show, and pretending it does renders `undefined` at the person.
219
+ setError(isStapelApiError(caught) ? caught : null);
220
+ }, []);
221
+ const ref = useMemo(() => ({ workspaceId: props.workspaceId, formId: props.formId }), [props.workspaceId, props.formId]);
222
+ const doSave = useCallback(() => {
223
+ if (!save.available)
224
+ return;
225
+ setError(null);
226
+ saveMutation.mutate({ ...ref, schema }, { onError });
227
+ }, [save, saveMutation, ref, schema, onError]);
228
+ const doPublish = useCallback(() => {
229
+ if (!publish.available)
230
+ return;
231
+ setError(null);
232
+ publishMutation.mutate(ref, { onError });
233
+ }, [publish, publishMutation, ref, onError]);
234
+ const setFormState = useCallback((next) => {
235
+ setError(null);
236
+ stateMutation.mutate({ ...ref, state: next }, { onError });
237
+ }, [stateMutation, ref, onError]);
238
+ const rotateLink = useCallback(() => {
239
+ setError(null);
240
+ rotateMutation.mutate(ref, { onError });
241
+ }, [rotateMutation, ref, onError]);
242
+ const refetch = useCallback(() => {
243
+ void query.refetch();
244
+ }, [query]);
245
+ // Offer only what this deployment can actually build with: allowed by its
246
+ // FIELD_KINDS setting, carried by the attributes registry, and declaring a
247
+ // config form. A kind failing any of those is still RENDERABLE and still
248
+ // authorable through the draft PUT — it is just not something the builder
249
+ // can hand somebody a button for.
250
+ const availableKinds = useMemo(() => mapLoad(kindsState, (cat) => cat.kinds.filter((kind) => kind.allowed &&
251
+ kind.registered &&
252
+ (kind.fields?.length ?? 0) > 0)), [kindsState]);
253
+ return props.children({
254
+ state,
255
+ fields,
256
+ meta: schema.meta ?? {},
257
+ isDirty,
258
+ addField,
259
+ removeField,
260
+ moveField,
261
+ updateField,
262
+ setFieldConfig,
263
+ setMeta,
264
+ availableKinds,
265
+ configWidgets: catalogue?.configWidgets ?? {},
266
+ save,
267
+ doSave,
268
+ publish,
269
+ doPublish,
270
+ isSaving,
271
+ isPublishing,
272
+ setState: setFormState,
273
+ rotateLink,
274
+ error,
275
+ refetch,
276
+ });
277
+ }
278
+ //# sourceMappingURL=FormBuilder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormBuilder.js","sourceRoot":"","sources":["../../src/headless/FormBuilder.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ActionAvailability, FlowError, LoadState, StapelApiError } from "@stapel/core";
3
+ import type { PublicForm } from "../api/types.js";
4
+ /** The bag `<FormFill>` hands its render prop (spec §7.2). */
5
+ export interface FormFillBag {
6
+ /**
7
+ * The schema read as a state a skin cannot flatten. A failed fetch renders
8
+ * the `failed` arm — NEVER an empty or missing form. Distinguish the
9
+ * verdicts from the outage with
10
+ * `hasErrorCode(error, "error.404.forms_not_found")` /
11
+ * `"error.410.forms_closed"`; anything else (network, 5xx) is "we could not
12
+ * ask", which is a different sentence from "there is no form here".
13
+ */
14
+ readonly state: LoadState<PublicForm>;
15
+ /** Current answers, keyed by slug. */
16
+ readonly values: Readonly<Record<string, unknown>>;
17
+ setValue(slug: string, value: unknown): void;
18
+ /**
19
+ * Per-field refusals, keyed by slug — the client mirror and the server's
20
+ * verdicts in one map. A server `error.400.feature_*` is routed here by its
21
+ * `params.field`; a multi-field refusal arrives in `params.fields[]` and
22
+ * every entry lands.
23
+ */
24
+ readonly fieldErrors: Readonly<Record<string, FlowError>>;
25
+ /** Why the submit button is off, when it is. */
26
+ readonly submit: ActionAvailability;
27
+ doSubmit(): void;
28
+ readonly isSubmitting: boolean;
29
+ /** Set once the server accepted the answers. */
30
+ readonly submitted: {
31
+ readonly confirmation: string;
32
+ } | null;
33
+ /** Whole-form failures (410 closed, 429 throttled, 413 too large, the
34
+ * submission cap). A `StapelApiError` rather than a `FlowError` so a skin
35
+ * can branch with `hasErrorCode` (which reads the API dialects, not the
36
+ * render dialect). */
37
+ readonly formError: StapelApiError | null;
38
+ /**
39
+ * True after a `409 forms_version_superseded`: the schema was refetched and
40
+ * compatible answers were preserved. The skin must say so — "the form
41
+ * changed, please review and resubmit" — because silently swapping the
42
+ * fields under a person and asking them to press submit again is how a
43
+ * wrong answer gets recorded.
44
+ */
45
+ readonly superseded: boolean;
46
+ /**
47
+ * Field kinds in this schema that nothing can render (no host registration,
48
+ * no builtin widget). Non-empty means {@link submit} is blocked: silently
49
+ * skipping a possibly-REQUIRED field would fabricate an invalid submission
50
+ * and let the server refuse a field the person never saw.
51
+ */
52
+ readonly unsupportedKinds: readonly string[];
53
+ /**
54
+ * The captcha seam (spec §12 risk 3). The netintel tier decides whether a
55
+ * token is required at all, so this stays optional and the captcha layer
56
+ * refuses on its own terms; the pair only transports it. Wire it the way
57
+ * auth-react's OTP flow does — the widget hands a token, the bag puts it in
58
+ * the submit body.
59
+ */
60
+ setCaptchaToken(token: string | null): void;
61
+ readonly captchaToken: string | null;
62
+ refetch(): void;
63
+ }
64
+ /**
65
+ * Headless form fill — the anonymous respondent's whole surface, renderless.
66
+ *
67
+ * ```tsx
68
+ * <FormFill publicId="k3J…x9">
69
+ * {(bag) => matchLoad(bag.state, { loading, failed, ready })}
70
+ * </FormFill>
71
+ * ```
72
+ *
73
+ * The `/default` skin's `<StapelForm>` is one renderer over this bag; a host
74
+ * that wants its own visuals writes another and loses nothing.
75
+ */
76
+ export declare function FormFill(props: {
77
+ publicId: string;
78
+ /** Kinds the CALLER can draw, beyond anything in the widget registry. The
79
+ * `/default` skin passes its antd builtins here, which is what lets this
80
+ * headless component judge `unsupportedKinds` without importing the skin. */
81
+ builtinKinds?: readonly string[];
82
+ /** Called once the server accepts the answers. */
83
+ onSubmitted?: (result: {
84
+ readonly confirmation: string;
85
+ }) => void;
86
+ children: (bag: FormFillBag) => ReactNode;
87
+ }): ReactNode;
88
+ //# sourceMappingURL=FormFill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormFill.d.ts","sourceRoot":"","sources":["../../src/headless/FormFill.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,OAAO,KAAK,EACV,kBAAkB,EAClB,SAAS,EACT,SAAS,EACT,cAAc,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAgB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAWhE,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IACtC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC1D,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,IAAI,IAAI,CAAC;IACjB,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D;;;0BAGsB;IACtB,QAAQ,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;;;;;;OAMG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;CACjB;AAoGD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB;;iFAE6E;IAC7E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,kDAAkD;IAClD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,QAAQ,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,SAAS,CAAC;CAC3C,GAAG,SAAS,CA8LZ"}