@businessdash/sdk 0.9.81 → 0.9.90

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 (261) hide show
  1. package/README.md +115 -902
  2. package/assets/{biab-sw.js → bd-sw.js} +2 -2
  3. package/dist/account-core/index.d.ts +1 -1
  4. package/dist/account-core/index.d.ts.map +1 -1
  5. package/dist/adapters-revalidate.d.ts +2 -2
  6. package/dist/adapters-revalidate.js +2 -2
  7. package/dist/adapters-revalidate.js.map +1 -1
  8. package/dist/analytics-core.d.ts +6 -6
  9. package/dist/analytics-core.d.ts.map +1 -1
  10. package/dist/analytics-core.js +4 -4
  11. package/dist/analytics-core.js.map +1 -1
  12. package/dist/analytics-server.d.ts +6 -6
  13. package/dist/analytics-server.d.ts.map +1 -1
  14. package/dist/analytics-server.js +5 -5
  15. package/dist/auth-handler.d.ts +14 -14
  16. package/dist/auth-handler.d.ts.map +1 -1
  17. package/dist/auth-handler.js +16 -16
  18. package/dist/auth-handler.js.map +1 -1
  19. package/dist/auth-links.d.ts +2 -2
  20. package/dist/auth-links.d.ts.map +1 -1
  21. package/dist/auth-links.js +2 -2
  22. package/dist/auth-links.js.map +1 -1
  23. package/dist/{biab-forms.css → bd-forms.css} +158 -158
  24. package/dist/cli.d.ts +4 -4
  25. package/dist/cli.js +78 -78
  26. package/dist/cli.js.map +1 -1
  27. package/dist/client.d.ts +158 -147
  28. package/dist/client.d.ts.map +1 -1
  29. package/dist/client.js +156 -137
  30. package/dist/client.js.map +1 -1
  31. package/dist/consent-core/index.cjs +2 -2
  32. package/dist/consent-core/index.d.ts +2 -2
  33. package/dist/consent-core/index.d.ts.map +1 -1
  34. package/dist/consent-core/index.js +10 -8
  35. package/dist/consent-core/index.js.map +1 -1
  36. package/dist/contracts.d.ts +411 -52
  37. package/dist/contracts.d.ts.map +1 -1
  38. package/dist/contracts.js +89 -15
  39. package/dist/contracts.js.map +1 -1
  40. package/dist/data-model-schema.d.ts +15 -15
  41. package/dist/data-model-schema.d.ts.map +1 -1
  42. package/dist/data-model-schema.js +19 -17
  43. package/dist/data-model-schema.js.map +1 -1
  44. package/dist/distribution.d.ts +5 -5
  45. package/dist/distribution.d.ts.map +1 -1
  46. package/dist/distribution.js +4 -4
  47. package/dist/element.d.ts +36 -36
  48. package/dist/element.d.ts.map +1 -1
  49. package/dist/element.js +43 -43
  50. package/dist/element.js.map +1 -1
  51. package/dist/embed-protocol.d.ts +11 -11
  52. package/dist/embed-protocol.d.ts.map +1 -1
  53. package/dist/embed-protocol.js +1 -1
  54. package/dist/embed-protocol.js.map +1 -1
  55. package/dist/errors.d.ts +15 -15
  56. package/dist/errors.d.ts.map +1 -1
  57. package/dist/errors.js +18 -18
  58. package/dist/errors.js.map +1 -1
  59. package/dist/forms-core/autocomplete.d.ts +3 -3
  60. package/dist/forms-core/autocomplete.d.ts.map +1 -1
  61. package/dist/forms-core/autocomplete.js +2 -2
  62. package/dist/forms-core/controller.d.ts +10 -10
  63. package/dist/forms-core/controller.d.ts.map +1 -1
  64. package/dist/forms-core/controller.js +4 -4
  65. package/dist/forms-core/controller.js.map +1 -1
  66. package/dist/forms-core/elements.d.ts +4 -4
  67. package/dist/forms-core/elements.d.ts.map +1 -1
  68. package/dist/forms-core/elements.js +7 -7
  69. package/dist/forms-core/elements.js.map +1 -1
  70. package/dist/forms-core/formula.d.ts +1 -1
  71. package/dist/forms-core/formula.js +1 -1
  72. package/dist/forms-core/index.d.ts +6 -6
  73. package/dist/forms-core/index.d.ts.map +1 -1
  74. package/dist/forms-core/index.js +5 -5
  75. package/dist/forms-core/index.js.map +1 -1
  76. package/dist/forms-core/units.d.ts +1 -1
  77. package/dist/forms-core/units.js +1 -1
  78. package/dist/forms-core/us-states.d.ts +1 -1
  79. package/dist/forms-core/us-states.js +1 -1
  80. package/dist/forms-core/validation.d.ts +12 -3
  81. package/dist/forms-core/validation.d.ts.map +1 -1
  82. package/dist/forms-core/validation.js +152 -17
  83. package/dist/forms-core/validation.js.map +1 -1
  84. package/dist/forms.d.ts +2 -2
  85. package/dist/forms.d.ts.map +1 -1
  86. package/dist/forms.js +2 -2
  87. package/dist/forms.js.map +1 -1
  88. package/dist/framework-support.js +1 -1
  89. package/dist/framework-support.js.map +1 -1
  90. package/dist/gallery-types.d.ts +1 -1
  91. package/dist/graphql.d.ts +3 -3
  92. package/dist/graphql.d.ts.map +1 -1
  93. package/dist/graphql.js +3 -3
  94. package/dist/graphql.js.map +1 -1
  95. package/dist/index.cjs +350 -273
  96. package/dist/index.d.cts +5 -5
  97. package/dist/index.d.mts +5 -5
  98. package/dist/index.d.ts +5 -5
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +5 -5
  101. package/dist/index.js.map +1 -1
  102. package/dist/legal-core/index.cjs +8 -8
  103. package/dist/legal-core/index.d.ts +3 -3
  104. package/dist/legal-core/index.d.ts.map +1 -1
  105. package/dist/legal-core/index.js +11 -11
  106. package/dist/legal-core/index.js.map +1 -1
  107. package/dist/marketing-core/index.d.ts +51 -1
  108. package/dist/marketing-core/index.d.ts.map +1 -1
  109. package/dist/marketing-core/index.js +35 -0
  110. package/dist/marketing-core/index.js.map +1 -1
  111. package/dist/marketing-schema.d.ts +9 -9
  112. package/dist/marketing-schema.d.ts.map +1 -1
  113. package/dist/marketing-schema.js +1 -1
  114. package/dist/mcp.d.ts +4 -4
  115. package/dist/mcp.js +4 -4
  116. package/dist/native.d.ts +6 -6
  117. package/dist/native.d.ts.map +1 -1
  118. package/dist/native.js +4 -4
  119. package/dist/native.js.map +1 -1
  120. package/dist/next-revalidate.d.ts +8 -8
  121. package/dist/next-revalidate.d.ts.map +1 -1
  122. package/dist/next-revalidate.js +7 -7
  123. package/dist/next-revalidate.js.map +1 -1
  124. package/dist/notifications-core/index.d.ts +1 -1
  125. package/dist/notifications-core/index.js +1 -1
  126. package/dist/parallel-pages.d.ts +4 -4
  127. package/dist/parallel-pages.d.ts.map +1 -1
  128. package/dist/parallel-pages.js +5 -5
  129. package/dist/parallel-pages.js.map +1 -1
  130. package/dist/portal-core/index.d.ts +1 -1
  131. package/dist/portal-core/index.js +1 -1
  132. package/dist/proxy.d.ts +18 -18
  133. package/dist/proxy.d.ts.map +1 -1
  134. package/dist/proxy.js +10 -10
  135. package/dist/proxy.js.map +1 -1
  136. package/dist/push-core/index.cjs +1 -1
  137. package/dist/push-core/index.d.ts +1 -1
  138. package/dist/push-core/index.js +1 -1
  139. package/dist/push-core/index.js.map +1 -1
  140. package/dist/react-analytics.d.ts +14 -14
  141. package/dist/react-analytics.d.ts.map +1 -1
  142. package/dist/react-analytics.js +15 -15
  143. package/dist/react-analytics.js.map +1 -1
  144. package/dist/react-attribution.d.ts +2 -2
  145. package/dist/react-attribution.d.ts.map +1 -1
  146. package/dist/react-attribution.js +1 -1
  147. package/dist/react-marketing.js +5 -5
  148. package/dist/react-marketing.js.map +1 -1
  149. package/dist/react.d.ts +38 -61
  150. package/dist/react.d.ts.map +1 -1
  151. package/dist/react.js +247 -261
  152. package/dist/react.js.map +1 -1
  153. package/dist/reserved-paths.d.ts +2 -2
  154. package/dist/reserved-paths.js +3 -3
  155. package/dist/reserved-paths.js.map +1 -1
  156. package/dist/revalidate-verify.d.ts +3 -3
  157. package/dist/revalidate-verify.d.ts.map +1 -1
  158. package/dist/revalidate-verify.js +1 -1
  159. package/dist/revalidate-verify.js.map +1 -1
  160. package/dist/sdk.d.ts +49 -49
  161. package/dist/sdk.d.ts.map +1 -1
  162. package/dist/sdk.js +47 -51
  163. package/dist/sdk.js.map +1 -1
  164. package/dist/seed-core/index.js +8 -8
  165. package/dist/seed-core/index.js.map +1 -1
  166. package/dist/seo-core/blog.d.ts +1 -1
  167. package/dist/seo-core/blog.js +1 -1
  168. package/dist/seo-core/blog.js.map +1 -1
  169. package/dist/seo-core/index.cjs +2 -2
  170. package/dist/seo-core/index.js +1 -1
  171. package/dist/seo-core/index.js.map +1 -1
  172. package/dist/seo.d.ts +1 -1
  173. package/dist/seo.d.ts.map +1 -1
  174. package/dist/seo.js +1 -1
  175. package/dist/seo.js.map +1 -1
  176. package/dist/sitemap-core/index.cjs +3 -3
  177. package/dist/sitemap-core/index.js +2 -2
  178. package/dist/sitemap-core/index.js.map +1 -1
  179. package/dist/socials.d.ts +6 -6
  180. package/dist/socials.d.ts.map +1 -1
  181. package/dist/socials.js +5 -5
  182. package/dist/socials.js.map +1 -1
  183. package/dist/solid-compat.d.ts +1 -1
  184. package/dist/solid-compat.js +4 -4
  185. package/dist/solid-compat.js.map +1 -1
  186. package/dist/store-core/index.d.ts +1 -1
  187. package/dist/vanilla.d.ts +12 -12
  188. package/dist/vanilla.d.ts.map +1 -1
  189. package/dist/vanilla.js +192 -145
  190. package/dist/vanilla.js.map +1 -1
  191. package/openapi.json +264 -1
  192. package/package.json +8 -8
  193. package/src/account-core/index.ts +1 -1
  194. package/src/adapters-revalidate.ts +3 -3
  195. package/src/analytics-core.ts +8 -8
  196. package/src/analytics-server.ts +6 -6
  197. package/src/angular/{biab-form.component.ts → bd-form.component.ts} +269 -161
  198. package/src/angular/headless.ts +12 -12
  199. package/src/angular/index.ts +9 -9
  200. package/src/auth-handler.ts +22 -22
  201. package/src/auth-links.ts +2 -2
  202. package/src/cli.ts +81 -81
  203. package/src/client.ts +218 -193
  204. package/src/consent-core/index.ts +10 -8
  205. package/src/contracts.ts +99 -15
  206. package/src/data-model-schema.ts +29 -26
  207. package/src/distribution.ts +5 -5
  208. package/src/element.ts +54 -54
  209. package/src/embed-protocol.ts +14 -14
  210. package/src/errors.ts +27 -27
  211. package/src/forms-core/autocomplete.ts +3 -3
  212. package/src/forms-core/controller.ts +12 -12
  213. package/src/forms-core/elements.ts +7 -7
  214. package/src/forms-core/formula.ts +1 -1
  215. package/src/forms-core/index.ts +8 -8
  216. package/src/forms-core/units.ts +1 -1
  217. package/src/forms-core/us-states.ts +1 -1
  218. package/src/forms-core/validation.ts +158 -14
  219. package/src/forms.ts +2 -2
  220. package/src/framework-support.ts +1 -1
  221. package/src/gallery-types.ts +1 -1
  222. package/src/graphql.ts +4 -4
  223. package/src/index.ts +38 -38
  224. package/src/legal-core/index.ts +12 -12
  225. package/src/marketing-core/index.ts +62 -0
  226. package/src/marketing-schema.ts +9 -9
  227. package/src/mcp.ts +4 -4
  228. package/src/native.ts +14 -14
  229. package/src/next-revalidate.ts +10 -10
  230. package/src/notifications-core/index.ts +1 -1
  231. package/src/parallel-pages.ts +6 -6
  232. package/src/portal-core/index.ts +1 -1
  233. package/src/proxy.ts +28 -28
  234. package/src/push-core/index.ts +2 -2
  235. package/src/qwik.tsx +262 -166
  236. package/src/react-analytics.tsx +17 -17
  237. package/src/react-attribution.tsx +2 -2
  238. package/src/react-marketing.tsx +5 -5
  239. package/src/react.tsx +541 -500
  240. package/src/reserved-paths.ts +3 -3
  241. package/src/revalidate-verify.ts +5 -5
  242. package/src/sdk.ts +106 -110
  243. package/src/seed-core/index.ts +8 -8
  244. package/src/seo-core/blog.ts +2 -2
  245. package/src/seo-core/index.ts +1 -1
  246. package/src/seo.ts +1 -1
  247. package/src/sitemap-core/index.ts +2 -2
  248. package/src/socials.ts +6 -6
  249. package/src/solid-compat.tsx +5 -5
  250. package/src/solid-surfaces.tsx +28 -28
  251. package/src/solid.tsx +264 -175
  252. package/src/store-core/index.ts +1 -1
  253. package/src/styles/{biab-forms.css → bd-forms.css} +158 -158
  254. package/src/svelte/{BiabForm.svelte → BdForm.svelte} +240 -173
  255. package/src/svelte/context.ts +16 -16
  256. package/src/svelte/headless.ts +10 -10
  257. package/src/svelte/index.ts +16 -16
  258. package/src/vanilla.ts +250 -169
  259. package/src/vue/{BiabForm.vue → BdForm.vue} +270 -157
  260. package/src/vue/headless.ts +11 -11
  261. package/src/vue/index.ts +26 -26
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `@businessdash/sdk/angular` — `<biab-form>` drop-in Angular component.
2
+ * `@businessdash/sdk/angular` — `<bd-form>` drop-in Angular component.
3
3
  *
4
4
  * Ship mode: SOURCE. This file is a single standalone Angular component shipped
5
5
  * as plain `.ts` (no ng-packagr / APF) and compiled by the CONSUMER's Angular
@@ -14,13 +14,13 @@
14
14
  * multi-step nav + progress, concurrent reveal, submission + file uploads — live
15
15
  * in the core. This component:
16
16
  *
17
- * 1. Creates a controller from `slug` + an injected/`@Input` `BiabClient` (or
17
+ * 1. Creates a controller from `slug` + an injected/`@Input` `BdClient` (or
18
18
  * a pre-fetched `schema` for SSR).
19
19
  * 2. Mirrors the controller's `snapshot()` into a single Angular `signal`,
20
20
  * re-publishing on every `subscribe` notify (so OnPush change detection
21
21
  * fires precisely when the form state changes).
22
22
  * 3. Renders the FULL nested field tree from `snapshot.visibleFields` using
23
- * the `biab-*` light-DOM class convention (no shadow root), so the org's
23
+ * the `bd-*` light-DOM class convention (no shadow root), so the org's
24
24
  * own CSS cascades exactly as it does on the dashboard renderer.
25
25
  * 4. Applies `schema.settings` — animation, multi-step + progress
26
26
  * (bar/steps), concurrent reveal, and the org icon — so the drop-in
@@ -30,8 +30,8 @@
30
30
  * field renderer, mapped onto Angular template control flow. Because the tree is
31
31
  * recursive (`input_group` / `condition_block` / `or_condition` / `flex_row`
32
32
  * with children), the component renders itself recursively via the private
33
- * `<biab-form-node>` sub-component below — the public surface stays a single
34
- * `<biab-form>` tag.
33
+ * `<bd-form-node>` sub-component below — the public surface stays a single
34
+ * `<bd-form>` tag.
35
35
  */
36
36
 
37
37
  import {
@@ -49,7 +49,7 @@ import {
49
49
  } from "@angular/core";
50
50
 
51
51
  import {
52
- type BiabClient,
52
+ type BdClient,
53
53
  type FieldError,
54
54
  type FormController,
55
55
  type FormCoreField,
@@ -72,42 +72,42 @@ import {
72
72
  } from "../forms-core/autocomplete.js";
73
73
 
74
74
  // ─────────────────────────────────────────────────────────────────────────────
75
- // DI — provide a shared BiabClient app-wide so consumers don't repeat it on
76
- // every `<biab-form>`. Mirrors the React `<DashboardProvider>` ergonomics but
75
+ // DI — provide a shared BdClient app-wide so consumers don't repeat it on
76
+ // every `<bd-form>`. Mirrors the React `<DashboardProvider>` ergonomics but
77
77
  // uses Angular's idiomatic InjectionToken + provider helper.
78
78
  // ─────────────────────────────────────────────────────────────────────────────
79
79
 
80
80
  /**
81
- * Optional DI token carrying the shared {@link BiabClient}. Provide it once at
82
- * the app root (e.g. via {@link provideBiabClient}) and every `<biab-form>`
81
+ * Optional DI token carrying the shared {@link BdClient}. Provide it once at
82
+ * the app root (e.g. via {@link provideBdClient}) and every `<bd-form>`
83
83
  * picks it up automatically — no `[client]` binding required. A `[client]`
84
84
  * input still wins per-instance when both are present.
85
85
  */
86
- export const BIAB_CLIENT = new InjectionToken<BiabClient>("BIAB_CLIENT");
86
+ export const BD_CLIENT = new InjectionToken<BdClient>("BD_CLIENT");
87
87
 
88
88
  /**
89
89
  * Provider helper for the application config. Drop into `bootstrapApplication`'s
90
- * `providers` (or a route's `providers`) so `<biab-form slug="…">` works with no
90
+ * `providers` (or a route's `providers`) so `<bd-form slug="…">` works with no
91
91
  * per-instance `[client]`:
92
92
  *
93
93
  * ```ts
94
94
  * bootstrapApplication(App, {
95
- * providers: [provideBiabClient(myBiabClient)],
95
+ * providers: [provideBdClient(myBdClient)],
96
96
  * });
97
97
  * ```
98
98
  */
99
- export function provideBiabClient(client: BiabClient) {
100
- return { provide: BIAB_CLIENT, useValue: client };
99
+ export function provideBdClient(client: BdClient) {
100
+ return { provide: BD_CLIENT, useValue: client };
101
101
  }
102
102
 
103
103
  // ─────────────────────────────────────────────────────────────────────────────
104
104
  // Recursive field-tree renderer — one node + its subtree. Private to the module;
105
- // the public API is only `<biab-form>`. Faithful port of FormPreview's
106
- // `RenderTree` + `FieldRow` + `FieldInput`, using the same `biab-*` classes and
107
- // `data-biab-*` hooks so org CSS targets the same selectors as the dashboard.
105
+ // the public API is only `<bd-form>`. Faithful port of FormPreview's
106
+ // `RenderTree` + `FieldRow` + `FieldInput`, using the same `bd-*` classes and
107
+ // `data-bd-*` hooks so org CSS targets the same selectors as the dashboard.
108
108
  // ─────────────────────────────────────────────────────────────────────────────
109
109
 
110
- /** Stable CSS hook for a field (the `data-biab-field` value): explicit key →
110
+ /** Stable CSS hook for a field (the `data-bd-field` value): explicit key →
111
111
  * camelCase label slug → id. Mirrors FormPreview's `fieldCssKey`. */
112
112
  function fieldCssKey(field: FormCoreField): string {
113
113
  const explicit =
@@ -136,17 +136,17 @@ function fieldCssKey(field: FormCoreField): string {
136
136
  const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
137
137
 
138
138
  @Component({
139
- selector: "biab-form-node",
139
+ selector: "bd-form-node",
140
140
  changeDetection: ChangeDetectionStrategy.OnPush,
141
141
  template: `
142
142
  @switch (field.type) {
143
143
  @case ("input_group") {
144
- <div class="biab-group" [attr.data-biab-group]="cssKey()">
144
+ <div class="bd-group" [attr.data-bd-group]="cssKey()">
145
145
  @if (field.label && field.showGroupLabel) {
146
- <div class="biab-group__label">{{ field.label }}</div>
146
+ <div class="bd-group__label">{{ field.label }}</div>
147
147
  }
148
148
  @for (child of revealedChildren(); track child.id) {
149
- <biab-form-node
149
+ <bd-form-node
150
150
  [field]="child"
151
151
  [values]="values"
152
152
  [errors]="errors"
@@ -161,7 +161,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
161
161
  @case ("condition_block") {
162
162
  @if (conditionPasses()) {
163
163
  @for (child of children(); track child.id) {
164
- <biab-form-node
164
+ <bd-form-node
165
165
  [field]="child"
166
166
  [values]="values"
167
167
  [errors]="errors"
@@ -175,23 +175,23 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
175
175
  }
176
176
  @case ("or_condition") {
177
177
  <div
178
- class="biab-or"
179
- [class.biab-or--error]="isErrored()"
180
- [attr.data-biab-or]="cssKey()"
178
+ class="bd-or"
179
+ [class.bd-or--error]="isErrored()"
180
+ [attr.data-bd-or]="cssKey()"
181
181
  >
182
182
  @if (field.label) {
183
- <div class="biab-or__label">
183
+ <div class="bd-or__label">
184
184
  {{ field.label }}
185
- <span class="biab-or__hint">Fill at least one side</span>
185
+ <span class="bd-or__hint">Fill at least one side</span>
186
186
  </div>
187
187
  }
188
- <div class="biab-or__grid">
189
- <div class="biab-or__side">
190
- <div class="biab-or__side-label">
188
+ <div class="bd-or__grid">
189
+ <div class="bd-or__side">
190
+ <div class="bd-or__side-label">
191
191
  {{ orLeftLabel() }}
192
192
  </div>
193
193
  @if (children()[0]; as left) {
194
- <biab-form-node
194
+ <bd-form-node
195
195
  [field]="left"
196
196
  [values]="values"
197
197
  [errors]="errors"
@@ -202,13 +202,13 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
202
202
  />
203
203
  }
204
204
  </div>
205
- <div class="biab-or__divider"><span>OR</span></div>
206
- <div class="biab-or__side">
207
- <div class="biab-or__side-label">
205
+ <div class="bd-or__divider"><span>OR</span></div>
206
+ <div class="bd-or__side">
207
+ <div class="bd-or__side-label">
208
208
  {{ orRightLabel() }}
209
209
  </div>
210
210
  @if (children()[1]; as right) {
211
- <biab-form-node
211
+ <bd-form-node
212
212
  [field]="right"
213
213
  [values]="values"
214
214
  [errors]="errors"
@@ -221,15 +221,15 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
221
221
  </div>
222
222
  </div>
223
223
  @if (isErrored()) {
224
- <p class="biab-field__error">Fill in at least one side to continue.</p>
224
+ <p class="bd-field__error">Fill in at least one side to continue.</p>
225
225
  }
226
226
  </div>
227
227
  }
228
228
  @case ("flex_row") {
229
- <div class="biab-flex-row">
229
+ <div class="bd-flex-row">
230
230
  @for (child of children(); track child.id) {
231
- <div class="biab-flex-row__cell">
232
- <biab-form-node
231
+ <div class="bd-flex-row__cell">
232
+ <bd-form-node
233
233
  [field]="child"
234
234
  [values]="values"
235
235
  [errors]="errors"
@@ -245,44 +245,44 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
245
245
  @default {
246
246
  <!-- Leaf field row -->
247
247
  <div
248
- class="biab-field"
248
+ class="bd-field"
249
249
  [class]="rowClass()"
250
250
  [attr.data-field-id]="field.id"
251
- [attr.data-biab-field]="cssKey()"
252
- [attr.data-biab-type]="field.type"
251
+ [attr.data-bd-field]="cssKey()"
252
+ [attr.data-bd-type]="field.type"
253
253
  >
254
254
  @if (showLabel()) {
255
- <label class="biab-field__label" [for]="field.id">
255
+ <label class="bd-field__label" [for]="field.id">
256
256
  {{ field.label || "" }}
257
257
  @if (field.required) {
258
- <span class="biab-field__required">*</span>
258
+ <span class="bd-field__required">*</span>
259
259
  }
260
260
  </label>
261
261
  }
262
262
 
263
263
  @switch (field.type) {
264
264
  @case ("title") {
265
- <h2 class="biab-field__title">{{ field.label || "Untitled" }}</h2>
265
+ <h2 class="bd-field__title">{{ field.label || "Untitled" }}</h2>
266
266
  }
267
267
  @case ("subtitle") {
268
- <h3 class="biab-field__subtitle">{{ field.label || "" }}</h3>
268
+ <h3 class="bd-field__subtitle">{{ field.label || "" }}</h3>
269
269
  }
270
270
  @case ("separator") {
271
- <hr class="biab-field__separator" />
271
+ <hr class="bd-field__separator" />
272
272
  }
273
273
  @case ("richtext") {
274
274
  <!-- content is org-authored HTML; the host page is trusted same-origin.
275
275
  Sanitize upstream if you accept untrusted form authors. -->
276
- <div class="biab-field__richtext" [innerHTML]="field.content || ''"></div>
276
+ <div class="bd-field__richtext" [innerHTML]="field.content || ''"></div>
277
277
  }
278
278
  @case ("image") {
279
279
  @if (imageSrc()) {
280
- <img class="biab-field__image" [src]="imageSrc()" alt="" />
280
+ <img class="bd-field__image" [src]="imageSrc()" alt="" />
281
281
  }
282
282
  }
283
283
  @case ("textarea") {
284
284
  <textarea
285
- class="biab-input biab-input--textarea"
285
+ class="bd-input bd-input--textarea"
286
286
  [id]="field.id"
287
287
  [value]="stringValue()"
288
288
  [placeholder]="field.placeholder || ''"
@@ -297,22 +297,22 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
297
297
  the worse default. A list attribute plus a datalist gives
298
298
  native typeahead: no JS, no styling opinion. -->
299
299
  <input
300
- class="biab-input biab-combobox"
300
+ class="bd-input bd-combobox"
301
301
  type="text"
302
302
  [id]="field.id"
303
- [attr.list]="'biab-list-' + field.id"
303
+ [attr.list]="'bd-list-' + field.id"
304
304
  [placeholder]="field.placeholder || 'Search…'"
305
305
  [value]="stringValue()"
306
306
  (input)="onInput($event)"
307
307
  />
308
- <datalist [id]="'biab-list-' + field.id">
308
+ <datalist [id]="'bd-list-' + field.id">
309
309
  @for (opt of field.options || []; track opt) {
310
310
  <option [value]="opt"></option>
311
311
  }
312
312
  </datalist>
313
313
  } @else {
314
314
  <select
315
- class="biab-input biab-input--select"
315
+ class="bd-input bd-input--select"
316
316
  [id]="field.id"
317
317
  [value]="stringValue()"
318
318
  (change)="onInput($event)"
@@ -328,16 +328,16 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
328
328
  <!-- The value is an icon NAME; art resolves against the
329
329
  platform's icon route, so no icon library ships in the
330
330
  SDK bundle. -->
331
- <div class="biab-iconselect">
331
+ <div class="bd-iconselect">
332
332
  <input
333
- class="biab-input biab-iconselect__search"
333
+ class="bd-input bd-iconselect__search"
334
334
  type="text"
335
335
  [placeholder]="field.placeholder || 'Search icons…'"
336
336
  [value]="iconQuery()"
337
337
  (input)="onIconQuery($event)"
338
338
  />
339
339
  @if (stringValue()) {
340
- <span class="biab-iconselect__current">
340
+ <span class="bd-iconselect__current">
341
341
  <img
342
342
  [src]="iconSrc(stringValue())"
343
343
  [alt]="stringValue()"
@@ -346,18 +346,18 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
346
346
  />
347
347
  <button
348
348
  type="button"
349
- class="biab-iconselect__clear"
349
+ class="bd-iconselect__clear"
350
350
  (click)="setIconValue('')"
351
351
  >
352
352
  ×
353
353
  </button>
354
354
  </span>
355
355
  }
356
- <div class="biab-iconselect__grid">
356
+ <div class="bd-iconselect__grid">
357
357
  @for (name of visibleIcons(); track name) {
358
358
  <button
359
359
  type="button"
360
- class="biab-iconselect__item"
360
+ class="bd-iconselect__item"
361
361
  [class.is-selected]="stringValue() === name"
362
362
  [title]="name"
363
363
  [attr.aria-label]="name"
@@ -372,12 +372,12 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
372
372
  }
373
373
  @case ("image_input") {
374
374
  <!-- One image, either source; the value is a plain URL string. -->
375
- <div class="biab-imageinput">
375
+ <div class="bd-imageinput">
376
376
  @if (imageSources() === "both") {
377
- <div class="biab-imageinput__modes">
377
+ <div class="bd-imageinput__modes">
378
378
  <button
379
379
  type="button"
380
- class="biab-imageinput__mode"
380
+ class="bd-imageinput__mode"
381
381
  [class.is-active]="imageMode() === 'upload'"
382
382
  [attr.aria-pressed]="imageMode() === 'upload'"
383
383
  (click)="setImageMode('upload')"
@@ -386,7 +386,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
386
386
  </button>
387
387
  <button
388
388
  type="button"
389
- class="biab-imageinput__mode"
389
+ class="bd-imageinput__mode"
390
390
  [class.is-active]="imageMode() === 'url'"
391
391
  [attr.aria-pressed]="imageMode() === 'url'"
392
392
  (click)="setImageMode('url')"
@@ -397,14 +397,14 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
397
397
  }
398
398
  @if (imageMode() === "upload" && imageSources() !== "url") {
399
399
  <input
400
- class="biab-input biab-imageinput__file"
400
+ class="bd-input bd-imageinput__file"
401
401
  type="file"
402
402
  [attr.accept]="imageAccept()"
403
403
  (change)="onImagePick($event)"
404
404
  />
405
405
  } @else {
406
406
  <input
407
- class="biab-input biab-imageinput__url"
407
+ class="bd-input bd-imageinput__url"
408
408
  type="url"
409
409
  [placeholder]="
410
410
  field.placeholder || 'https://example.com/image.png'
@@ -415,7 +415,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
415
415
  }
416
416
  @if (showImagePreview() && stringValue()) {
417
417
  <img
418
- class="biab-imageinput__preview"
418
+ class="bd-imageinput__preview"
419
419
  [src]="stringValue()"
420
420
  alt=""
421
421
  />
@@ -423,9 +423,9 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
423
423
  </div>
424
424
  }
425
425
  @case ("radio") {
426
- <div class="biab-input biab-input--radio">
426
+ <div class="bd-input bd-input--radio">
427
427
  @for (opt of field.options || []; track opt) {
428
- <label class="biab-radio__item">
428
+ <label class="bd-radio__item">
429
429
  <input
430
430
  type="radio"
431
431
  [name]="field.id"
@@ -439,9 +439,9 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
439
439
  </div>
440
440
  }
441
441
  @case ("checkbox") {
442
- <div class="biab-input biab-input--checkbox">
442
+ <div class="bd-input bd-input--checkbox">
443
443
  @for (opt of field.options || []; track opt) {
444
- <label class="biab-checkbox__item">
444
+ <label class="bd-checkbox__item">
445
445
  <input
446
446
  type="checkbox"
447
447
  [value]="opt"
@@ -454,7 +454,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
454
454
  </div>
455
455
  }
456
456
  @case ("boolean") {
457
- <label class="biab-input biab-input--boolean">
457
+ <label class="bd-input bd-input--boolean">
458
458
  <input
459
459
  type="checkbox"
460
460
  [checked]="booleanValue()"
@@ -465,7 +465,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
465
465
  }
466
466
  @case ("date") {
467
467
  <input
468
- class="biab-input biab-input--date"
468
+ class="bd-input bd-input--date"
469
469
  type="date"
470
470
  [id]="field.id"
471
471
  [value]="stringValue()"
@@ -474,7 +474,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
474
474
  }
475
475
  @case ("email") {
476
476
  <input
477
- class="biab-input biab-input--email"
477
+ class="bd-input bd-input--email"
478
478
  type="email"
479
479
  [id]="field.id"
480
480
  [value]="stringValue()"
@@ -485,7 +485,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
485
485
  }
486
486
  @case ("phone") {
487
487
  <input
488
- class="biab-input biab-input--phone"
488
+ class="bd-input bd-input--phone"
489
489
  type="tel"
490
490
  [id]="field.id"
491
491
  [value]="stringValue()"
@@ -496,7 +496,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
496
496
  }
497
497
  @case ("number") {
498
498
  <input
499
- class="biab-input biab-input--number"
499
+ class="bd-input bd-input--number"
500
500
  type="number"
501
501
  [id]="field.id"
502
502
  [value]="numberInputValue()"
@@ -505,21 +505,21 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
505
505
  />
506
506
  }
507
507
  @case ("stepper") {
508
- <div class="biab-input biab-input--stepper">
509
- <button type="button" class="biab-btn biab-stepper__dec" (click)="bumpStepper(-1)">
508
+ <div class="bd-input bd-input--stepper">
509
+ <button type="button" class="bd-btn bd-stepper__dec" (click)="bumpStepper(-1)">
510
510
 
511
511
  </button>
512
- <span class="biab-stepper__value">{{ numberValue() }}</span>
513
- <button type="button" class="biab-btn biab-stepper__inc" (click)="bumpStepper(1)">
512
+ <span class="bd-stepper__value">{{ numberValue() }}</span>
513
+ <button type="button" class="bd-btn bd-stepper__inc" (click)="bumpStepper(1)">
514
514
  +
515
515
  </button>
516
516
  </div>
517
517
  }
518
518
  @case ("slider") {
519
- <div class="biab-input biab-input--slider">
520
- <div class="biab-slider__readout">
519
+ <div class="bd-input bd-input--slider">
520
+ <div class="bd-slider__readout">
521
521
  <span>{{ sliderFmt(sliderMin()) }}</span>
522
- <span class="biab-slider__current">{{ sliderFmt(numberValue()) }}</span>
522
+ <span class="bd-slider__current">{{ sliderFmt(numberValue()) }}</span>
523
523
  <span>{{ sliderFmt(sliderMax()) }}</span>
524
524
  </div>
525
525
  <input
@@ -533,12 +533,12 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
533
533
  </div>
534
534
  }
535
535
  @case ("star_rating") {
536
- <div class="biab-input biab-input--stars">
536
+ <div class="bd-input bd-input--stars">
537
537
  @for (i of starIndexes(); track i) {
538
538
  <button
539
539
  type="button"
540
- class="biab-star"
541
- [class.biab-star--on]="i < numberValue()"
540
+ class="bd-star"
541
+ [class.bd-star--on]="i < numberValue()"
542
542
  (click)="setValue(i + 1)"
543
543
  [attr.aria-label]="i + 1 + ' stars'"
544
544
  >
@@ -548,12 +548,12 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
548
548
  </div>
549
549
  }
550
550
  @case ("happiness_rating") {
551
- <div class="biab-input biab-input--happiness">
551
+ <div class="bd-input bd-input--happiness">
552
552
  @for (face of happinessFaces(); track $index) {
553
553
  <button
554
554
  type="button"
555
- class="biab-happiness__face"
556
- [class.biab-happiness__face--on]="$index === numberValue()"
555
+ class="bd-happiness__face"
556
+ [class.bd-happiness__face--on]="$index === numberValue()"
557
557
  (click)="setValue($index)"
558
558
  >
559
559
  {{ face }}
@@ -562,12 +562,12 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
562
562
  </div>
563
563
  }
564
564
  @case ("color_picker") {
565
- <div class="biab-input biab-input--colors">
565
+ <div class="bd-input bd-input--colors">
566
566
  @for (color of field.options || []; track color) {
567
567
  <button
568
568
  type="button"
569
- class="biab-color"
570
- [class.biab-color--on]="stringValue() === color"
569
+ class="bd-color"
570
+ [class.bd-color--on]="stringValue() === color"
571
571
  [style.backgroundColor]="color"
572
572
  [title]="color"
573
573
  (click)="setValue(color)"
@@ -576,12 +576,12 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
576
576
  </div>
577
577
  }
578
578
  @case ("date_range") {
579
- <div class="biab-input biab-input--date-range">
580
- <label class="biab-subfield">
579
+ <div class="bd-input bd-input--date-range">
580
+ <label class="bd-subfield">
581
581
  <span>Start</span>
582
582
  <input type="date" [value]="rangeStart()" (input)="onRange('start', $event)" />
583
583
  </label>
584
- <label class="biab-subfield">
584
+ <label class="bd-subfield">
585
585
  <span>End</span>
586
586
  <input type="date" [value]="rangeEnd()" (input)="onRange('end', $event)" />
587
587
  </label>
@@ -590,24 +590,24 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
590
590
  @case ("time_picker") {
591
591
  @switch (timeMode()) {
592
592
  @case ("time_block") {
593
- <div class="biab-input biab-input--time-block">
594
- <label class="biab-subfield">
593
+ <div class="bd-input bd-input--time-block">
594
+ <label class="bd-subfield">
595
595
  <span>Start</span>
596
596
  <input type="time" [value]="rangeStart()" (input)="onRange('start', $event)" />
597
597
  </label>
598
- <label class="biab-subfield">
598
+ <label class="bd-subfield">
599
599
  <span>End</span>
600
600
  <input type="time" [value]="rangeEnd()" (input)="onRange('end', $event)" />
601
601
  </label>
602
602
  </div>
603
603
  }
604
604
  @case ("appointment") {
605
- <div class="biab-input biab-input--appointment">
606
- <label class="biab-subfield">
605
+ <div class="bd-input bd-input--appointment">
606
+ <label class="bd-subfield">
607
607
  <span>Date</span>
608
608
  <input type="date" [value]="apptDate()" (input)="onAppt('date', $event)" />
609
609
  </label>
610
- <label class="biab-subfield">
610
+ <label class="bd-subfield">
611
611
  <span>Time</span>
612
612
  <input type="time" [value]="apptTime()" (input)="onAppt('time', $event)" />
613
613
  </label>
@@ -615,7 +615,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
615
615
  }
616
616
  @default {
617
617
  <input
618
- class="biab-input biab-input--time"
618
+ class="bd-input bd-input--time"
619
619
  type="time"
620
620
  [value]="stringValue()"
621
621
  (input)="onInput($event)"
@@ -623,9 +623,75 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
623
623
  }
624
624
  }
625
625
  }
626
+ @case ("name") {
627
+ <div class="bd-input bd-input--name">
628
+ @if (nameCfg().prefix) {
629
+ <label class="bd-subfield bd-subfield--full">
630
+ <span>Title</span>
631
+ <input
632
+ placeholder="Dr."
633
+ [value]="nameField('prefix')"
634
+ [attr.autocomplete]="nameAutoComplete('honorific-prefix')"
635
+ (input)="onName('prefix', $event)"
636
+ />
637
+ </label>
638
+ }
639
+ <label class="bd-subfield">
640
+ <span>First name</span>
641
+ <input
642
+ placeholder="Jane"
643
+ [value]="nameField('first')"
644
+ [attr.autocomplete]="nameAutoComplete('given-name')"
645
+ (input)="onName('first', $event)"
646
+ />
647
+ </label>
648
+ @if (nameCfg().middle) {
649
+ <label class="bd-subfield">
650
+ <span>Middle name</span>
651
+ <input
652
+ placeholder="Quinn"
653
+ [value]="nameField('middle')"
654
+ [attr.autocomplete]="nameAutoComplete('additional-name')"
655
+ (input)="onName('middle', $event)"
656
+ />
657
+ </label>
658
+ }
659
+ <label class="bd-subfield" [class.bd-subfield--full]="nameLastFull()">
660
+ <span>Last name</span>
661
+ <input
662
+ placeholder="Doe"
663
+ [value]="nameField('last')"
664
+ [attr.autocomplete]="nameAutoComplete('family-name')"
665
+ (input)="onName('last', $event)"
666
+ />
667
+ </label>
668
+ @if (nameCfg().suffix) {
669
+ <label class="bd-subfield" [class.bd-subfield--full]="nameSuffixFull()">
670
+ <span>Suffix</span>
671
+ <input
672
+ placeholder="Jr."
673
+ [value]="nameField('suffix')"
674
+ [attr.autocomplete]="nameAutoComplete('honorific-suffix')"
675
+ (input)="onName('suffix', $event)"
676
+ />
677
+ </label>
678
+ }
679
+ @if (nameCfg().nicknames) {
680
+ <!-- Raw comma-separated string while typing; submit normalizes it into a string[]. -->
681
+ <label class="bd-subfield bd-subfield--full">
682
+ <span>Nicknames</span>
683
+ <input
684
+ placeholder="Johnny, JM — separate with commas"
685
+ [value]="nameNicknamesText()"
686
+ (input)="onName('nicknames', $event)"
687
+ />
688
+ </label>
689
+ }
690
+ </div>
691
+ }
626
692
  @case ("address") {
627
- <div class="biab-input biab-input--address">
628
- <label class="biab-subfield biab-subfield--full">
693
+ <div class="bd-input bd-input--address">
694
+ <label class="bd-subfield bd-subfield--full">
629
695
  <span>Address Line 1</span>
630
696
  <input
631
697
  [value]="addrField('line1')"
@@ -633,7 +699,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
633
699
  (input)="onAddr('line1', $event)"
634
700
  />
635
701
  </label>
636
- <label class="biab-subfield biab-subfield--full">
702
+ <label class="bd-subfield bd-subfield--full">
637
703
  <span>Address Line 2</span>
638
704
  <input
639
705
  [value]="addrField('line2')"
@@ -641,7 +707,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
641
707
  (input)="onAddr('line2', $event)"
642
708
  />
643
709
  </label>
644
- <label class="biab-subfield">
710
+ <label class="bd-subfield">
645
711
  <span>City</span>
646
712
  <input
647
713
  [value]="addrField('city')"
@@ -649,7 +715,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
649
715
  (input)="onAddr('city', $event)"
650
716
  />
651
717
  </label>
652
- <label class="biab-subfield">
718
+ <label class="bd-subfield">
653
719
  <span>State</span>
654
720
  <input
655
721
  [value]="addrField('state')"
@@ -657,7 +723,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
657
723
  (input)="onAddr('state', $event)"
658
724
  />
659
725
  </label>
660
- <label class="biab-subfield biab-subfield--full">
726
+ <label class="bd-subfield bd-subfield--full">
661
727
  <span>Zip</span>
662
728
  <input
663
729
  [value]="addrField('zip')"
@@ -668,7 +734,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
668
734
  </div>
669
735
  }
670
736
  @case ("file_upload") {
671
- <div class="biab-input biab-input--file">
737
+ <div class="bd-input bd-input--file">
672
738
  <input
673
739
  type="file"
674
740
  [multiple]="fileMultiple()"
@@ -676,9 +742,9 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
676
742
  (change)="onFile($event)"
677
743
  />
678
744
  @if (fileList().length) {
679
- <ul class="biab-file__list">
745
+ <ul class="bd-file__list">
680
746
  @for (f of fileList(); track f.storageId) {
681
- <li class="biab-file__item">{{ f.name }}</li>
747
+ <li class="bd-file__item">{{ f.name }}</li>
682
748
  }
683
749
  </ul>
684
750
  }
@@ -687,7 +753,7 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
687
753
  @default {
688
754
  <!-- text and any plain-text input -->
689
755
  <input
690
- class="biab-input biab-input--text"
756
+ class="bd-input bd-input--text"
691
757
  type="text"
692
758
  [id]="field.id"
693
759
  [value]="stringValue()"
@@ -699,24 +765,24 @@ const NO_LABEL_TYPES = new Set(["title", "subtitle", "separator", "richtext"]);
699
765
  }
700
766
 
701
767
  @if (isErrored()) {
702
- <p class="biab-field__error">{{ errorMessage() }}</p>
768
+ <p class="bd-field__error">{{ errorMessage() }}</p>
703
769
  }
704
770
  @if (field.helpText) {
705
- <p class="biab-field__help">{{ field.helpText }}</p>
771
+ <p class="bd-field__help">{{ field.helpText }}</p>
706
772
  }
707
773
  </div>
708
774
  }
709
775
  }
710
776
  `,
711
777
  })
712
- export class BiabFormNodeComponent {
778
+ export class BdFormNodeComponent {
713
779
  /** The field (subtree root) to render. */
714
780
  @Input({ required: true }) field!: FormCoreField;
715
781
  /** Working values, keyed by field id (the snapshot's `values`). */
716
782
  @Input({ required: true }) values!: Record<string, unknown>;
717
783
  /** Field-level errors, keyed by field id (the snapshot's `errors`). */
718
784
  @Input({ required: true }) errors!: Record<string, FieldError>;
719
- /** Autofill config threaded from `<biab-form>` (global switch + per-field
785
+ /** Autofill config threaded from `<bd-form>` (global switch + per-field
720
786
  * overrides). Drives the `autocomplete` attribute on value-bearing inputs. */
721
787
  @Input() autocompleteConfig: AutocompleteConfig = {};
722
788
 
@@ -759,6 +825,12 @@ export class BiabFormNodeComponent {
759
825
  return this.autoCompleteOff() ? "off" : token;
760
826
  }
761
827
 
828
+ /** Fixed token for a name composite sub-field, suppressed to "off" when the
829
+ * field's resolved token is "off". */
830
+ nameAutoComplete(token: string): string {
831
+ return this.autoCompleteOff() ? "off" : token;
832
+ }
833
+
762
834
  children(): FormCoreField[] {
763
835
  return (this.field.children ?? []) as FormCoreField[];
764
836
  }
@@ -787,9 +859,9 @@ export class BiabFormNodeComponent {
787
859
  }
788
860
 
789
861
  rowClass(): string {
790
- const parts = [`biab-field--${this.field.type}`];
791
- if (this.field.required) parts.push("biab-field--required");
792
- if (this.isErrored()) parts.push("biab-field--error");
862
+ const parts = [`bd-field--${this.field.type}`];
863
+ if (this.field.required) parts.push("bd-field--required");
864
+ if (this.isErrored()) parts.push("bd-field--error");
793
865
  return parts.join(" ");
794
866
  }
795
867
 
@@ -1036,6 +1108,42 @@ export class BiabFormNodeComponent {
1036
1108
  this.setValue({ ...this.addrObj(), [key]: value });
1037
1109
  }
1038
1110
 
1111
+ // ── name ──
1112
+ private nameObj(): Record<string, unknown> {
1113
+ const v = this.raw();
1114
+ return v && typeof v === "object" ? (v as Record<string, unknown>) : {};
1115
+ }
1116
+ nameCfg(): NonNullable<FormCoreField["nameConfig"]> {
1117
+ return this.field.nameConfig ?? {};
1118
+ }
1119
+ nameField(key: string): string {
1120
+ const v = this.nameObj()[key];
1121
+ return typeof v === "string" ? v : "";
1122
+ }
1123
+ /** Last name spans the row only when a middle name shares the first row with
1124
+ * the first name and no suffix follows to pair with it. */
1125
+ nameLastFull(): boolean {
1126
+ const cfg = this.nameCfg();
1127
+ return cfg.middle === true && cfg.suffix !== true;
1128
+ }
1129
+ /** Suffix spans the row when it isn't paired with the last name. */
1130
+ nameSuffixFull(): boolean {
1131
+ return this.nameCfg().middle !== true;
1132
+ }
1133
+ /** The nicknames input holds the raw comma-separated string while the
1134
+ * visitor types; submit normalizes it into a string[]. */
1135
+ nameNicknamesText(): string {
1136
+ const v = this.nameObj()["nicknames"];
1137
+ if (Array.isArray(v)) {
1138
+ return v.filter((n): n is string => typeof n === "string").join(", ");
1139
+ }
1140
+ return typeof v === "string" ? v : "";
1141
+ }
1142
+ onName(key: string, event: Event): void {
1143
+ const value = (event.target as HTMLInputElement).value;
1144
+ this.setValue({ ...this.nameObj(), [key]: value });
1145
+ }
1146
+
1039
1147
  // ── phone ──
1040
1148
  phonePlaceholder(): string {
1041
1149
  if (this.field.placeholder) return this.field.placeholder;
@@ -1072,62 +1180,62 @@ export class BiabFormNodeComponent {
1072
1180
  }
1073
1181
 
1074
1182
  // ─────────────────────────────────────────────────────────────────────────────
1075
- // Public component — `<biab-form>`. Owns the controller, mirrors its snapshot
1183
+ // Public component — `<bd-form>`. Owns the controller, mirrors its snapshot
1076
1184
  // into a signal, and orchestrates steps / progress / submit. Renders each
1077
- // visible top-level node via `<biab-form-node>`.
1185
+ // visible top-level node via `<bd-form-node>`.
1078
1186
  // ─────────────────────────────────────────────────────────────────────────────
1079
1187
 
1080
1188
  @Component({
1081
- selector: "biab-form",
1189
+ selector: "bd-form",
1082
1190
  changeDetection: ChangeDetectionStrategy.OnPush,
1083
- imports: [BiabFormNodeComponent],
1191
+ imports: [BdFormNodeComponent],
1084
1192
  host: {
1085
- class: "biab-form",
1086
- "[class.biab-form--submitting]": "snapshot().submitting",
1087
- "[class.biab-form--submitted]": "snapshot().submitted",
1088
- "[attr.data-biab-animation]": "snapshot().settings.animation",
1193
+ class: "bd-form",
1194
+ "[class.bd-form--submitting]": "snapshot().submitting",
1195
+ "[class.bd-form--submitted]": "snapshot().submitted",
1196
+ "[attr.data-bd-animation]": "snapshot().settings.animation",
1089
1197
  },
1090
1198
  template: `
1091
1199
  @let snap = snapshot();
1092
1200
 
1093
1201
  @if (snap.orgIcon) {
1094
- <img class="biab-form__org-icon" [src]="snap.orgIcon" alt="" />
1202
+ <img class="bd-form__org-icon" [src]="snap.orgIcon" alt="" />
1095
1203
  }
1096
1204
 
1097
1205
  @switch (snap.status) {
1098
1206
  @case ("loading") {
1099
- <div class="biab-form__loading">Loading…</div>
1207
+ <div class="bd-form__loading">Loading…</div>
1100
1208
  }
1101
1209
  @case ("error") {
1102
- <div class="biab-form__load-error">
1210
+ <div class="bd-form__load-error">
1103
1211
  {{ snap.loadError || "Couldn't load this form." }}
1104
1212
  </div>
1105
1213
  }
1106
1214
  @default {
1107
1215
  @if (snap.submitted) {
1108
- <div class="biab-form__success">
1216
+ <div class="bd-form__success">
1109
1217
  {{ successMessage() }}
1110
1218
  </div>
1111
1219
  } @else {
1112
1220
  <form
1113
- class="biab-form__body"
1114
- [attr.data-biab-step]="snap.step"
1221
+ class="bd-form__body"
1222
+ [attr.data-bd-step]="snap.step"
1115
1223
  (ngSubmit)="onSubmit()"
1116
1224
  >
1117
1225
  @if (showSteps()) {
1118
- <div class="biab-progress biab-progress--steps">
1119
- <div class="biab-progress__title">Form Progress</div>
1120
- <ol class="biab-progress__list">
1226
+ <div class="bd-progress bd-progress--steps">
1227
+ <div class="bd-progress__title">Form Progress</div>
1228
+ <ol class="bd-progress__list">
1121
1229
  @for (s of snap.steps; track $index; let i = $index) {
1122
1230
  <li
1123
- class="biab-progress__step"
1124
- [class.biab-progress__step--current]="i === snap.step"
1125
- [class.biab-progress__step--done]="i < snap.step"
1231
+ class="bd-progress__step"
1232
+ [class.bd-progress__step--current]="i === snap.step"
1233
+ [class.bd-progress__step--done]="i < snap.step"
1126
1234
  >
1127
- <span class="biab-progress__index">
1235
+ <span class="bd-progress__index">
1128
1236
  {{ i < snap.step ? "✓" : i + 1 }}
1129
1237
  </span>
1130
- <span class="biab-progress__label">{{ stepTitle(s, i) }}</span>
1238
+ <span class="bd-progress__label">{{ stepTitle(s, i) }}</span>
1131
1239
  </li>
1132
1240
  }
1133
1241
  </ol>
@@ -1135,13 +1243,13 @@ export class BiabFormNodeComponent {
1135
1243
  }
1136
1244
 
1137
1245
  @if (showBar()) {
1138
- <div class="biab-progress biab-progress--bar">
1139
- <span class="biab-progress__count">
1246
+ <div class="bd-progress bd-progress--bar">
1247
+ <span class="bd-progress__count">
1140
1248
  {{ snap.step + 1 }} / {{ snap.stepCount }}
1141
1249
  </span>
1142
- <div class="biab-progress__track">
1250
+ <div class="bd-progress__track">
1143
1251
  <div
1144
- class="biab-progress__fill"
1252
+ class="bd-progress__fill"
1145
1253
  [style.width.%]="snap.progressPercent"
1146
1254
  ></div>
1147
1255
  </div>
@@ -1149,21 +1257,21 @@ export class BiabFormNodeComponent {
1149
1257
  }
1150
1258
 
1151
1259
  @if (snap.errorList.length) {
1152
- <div class="biab-form__errors" role="alert">
1153
- <div class="biab-form__errors-title">
1260
+ <div class="bd-form__errors" role="alert">
1261
+ <div class="bd-form__errors-title">
1154
1262
  Please complete the highlighted required field{{
1155
1263
  snap.errorList.length === 1 ? "" : "s"
1156
1264
  }}.
1157
1265
  </div>
1158
- <div class="biab-form__errors-list">
1266
+ <div class="bd-form__errors-list">
1159
1267
  {{ errorLabels() }}
1160
1268
  </div>
1161
1269
  </div>
1162
1270
  }
1163
1271
 
1164
- <div class="biab-form__fields" [attr.data-biab-direction]="snap.forward ? 'forward' : 'back'">
1272
+ <div class="bd-form__fields" [attr.data-bd-direction]="snap.forward ? 'forward' : 'back'">
1165
1273
  @for (node of snap.visibleFields; track node.id) {
1166
- <biab-form-node
1274
+ <bd-form-node
1167
1275
  [field]="node"
1168
1276
  [values]="snap.values"
1169
1277
  [errors]="snap.errors"
@@ -1175,11 +1283,11 @@ export class BiabFormNodeComponent {
1175
1283
  }
1176
1284
  </div>
1177
1285
 
1178
- <div class="biab-nav">
1286
+ <div class="bd-nav">
1179
1287
  @if (isMultiStep()) {
1180
1288
  <button
1181
1289
  type="button"
1182
- class="biab-btn biab-btn--back"
1290
+ class="bd-btn bd-btn--back"
1183
1291
  [disabled]="snap.isFirstStep"
1184
1292
  (click)="back()"
1185
1293
  >
@@ -1189,7 +1297,7 @@ export class BiabFormNodeComponent {
1189
1297
  @if (isMultiStep() && !snap.isLastStep) {
1190
1298
  <button
1191
1299
  type="button"
1192
- class="biab-btn biab-btn--next"
1300
+ class="bd-btn bd-btn--next"
1193
1301
  [disabled]="!snap.canAdvance"
1194
1302
  (click)="next()"
1195
1303
  >
@@ -1198,7 +1306,7 @@ export class BiabFormNodeComponent {
1198
1306
  } @else {
1199
1307
  <button
1200
1308
  type="submit"
1201
- class="biab-btn biab-btn--submit"
1309
+ class="bd-btn bd-btn--submit"
1202
1310
  [disabled]="snap.submitting || snap.uploading"
1203
1311
  >
1204
1312
  {{ snap.submitting ? "Sending…" : submitLabel() }}
@@ -1207,7 +1315,7 @@ export class BiabFormNodeComponent {
1207
1315
  </div>
1208
1316
 
1209
1317
  @if (failureMessage()) {
1210
- <p class="biab-form__submit-error" role="alert">{{ failureMessage() }}</p>
1318
+ <p class="bd-form__submit-error" role="alert">{{ failureMessage() }}</p>
1211
1319
  }
1212
1320
  </form>
1213
1321
  }
@@ -1215,10 +1323,10 @@ export class BiabFormNodeComponent {
1215
1323
  }
1216
1324
  `,
1217
1325
  })
1218
- export class BiabFormComponent implements OnInit {
1326
+ export class BdFormComponent implements OnInit {
1219
1327
  private readonly destroyRef = inject(DestroyRef);
1220
1328
  /** App-wide client from DI (optional). A `[client]` input wins over it. */
1221
- private readonly injectedClient = inject(BIAB_CLIENT, { optional: true });
1329
+ private readonly injectedClient = inject(BD_CLIENT, { optional: true });
1222
1330
 
1223
1331
  // ── Inputs (signal inputs; the public, framework-consistent surface) ──
1224
1332
 
@@ -1229,7 +1337,7 @@ export class BiabFormComponent implements OnInit {
1229
1337
  * submit / upload. */
1230
1338
  readonly schema = input<FormSchema | undefined>(undefined);
1231
1339
  /** Per-instance client override. Falls back to the DI-provided client. */
1232
- readonly client = input<BiabClient | undefined>(undefined);
1340
+ readonly client = input<BdClient | undefined>(undefined);
1233
1341
  /** Submit button label (final step). Defaults to "Submit". */
1234
1342
  readonly submitLabel = input<string>("Submit");
1235
1343
  /** Validate-only submit: nothing is stored, no side effects. */
@@ -1275,7 +1383,7 @@ export class BiabFormComponent implements OnInit {
1275
1383
  private controller: FormController | null = null;
1276
1384
 
1277
1385
  // Resolve the effective client: per-instance input → DI token.
1278
- private resolveClient(): BiabClient | undefined {
1386
+ private resolveClient(): BdClient | undefined {
1279
1387
  return this.client() ?? this.injectedClient ?? undefined;
1280
1388
  }
1281
1389