@aircall/blocks 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as React$1 from "react";
3
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { VariantProps } from "class-variance-authority";
5
5
  import * as _tanstack_react_form0 from "@tanstack/react-form";
6
- import { DeepKeys, DeepKeysOfType, FieldAsyncValidateOrFn, FieldValidateOrFn } from "@tanstack/react-form";
6
+ import { DeepKeys, DeepKeysOfType, DeepValue, FieldAsyncValidateOrFn, FieldListeners, FieldValidateOrFn, FieldValidators } from "@tanstack/react-form";
7
7
  import { useRender } from "@base-ui/react/use-render";
8
8
  import * as class_variance_authority_types0 from "class-variance-authority/types";
9
9
 
@@ -1284,17 +1284,33 @@ declare const useForm: <TFormData, TOnMount extends _tanstack_react_form0.FormVa
1284
1284
  readonly CardSaveBar: typeof CardSaveBar;
1285
1285
  }>;
1286
1286
  declare const withForm: <TFormData, TOnMount extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnChange extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnChangeAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnBlur extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnBlurAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnSubmit extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnSubmitAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnDynamic extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnDynamicAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnServer extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TSubmitMeta, TRenderProps extends object = {}>({
1287
- render,
1288
- props
1289
- }: _tanstack_react_form0.WithFormProps<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta, {}, {
1290
- readonly SubmitButton: typeof SubmitButton;
1291
- readonly CardSaveBar: typeof CardSaveBar;
1292
- }, TRenderProps>) => React$1.FunctionComponent<React$1.PropsWithChildren<NoInfer<[unknown] extends [TRenderProps] ? any : TRenderProps> & {
1293
- form: _tanstack_react_form0.AppFieldExtendedReactFormApi<[unknown] extends [TFormData] ? any : TFormData, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnMount] ? [TOnMount] extends [TOnMount & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnMount : TOnMount, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnChange] ? [TOnChange] extends [TOnChange & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnChange : TOnChange, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnChangeAsync] ? [TOnChangeAsync] extends [TOnChangeAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnChangeAsync : TOnChangeAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnBlur] ? [TOnBlur] extends [TOnBlur & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlur : TOnBlur, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnBlurAsync] ? [TOnBlurAsync] extends [TOnBlurAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlurAsync : TOnBlurAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnSubmit] ? [TOnSubmit] extends [TOnSubmit & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmit : TOnSubmit, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnSubmitAsync] ? [TOnSubmitAsync] extends [TOnSubmitAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmitAsync : TOnSubmitAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnDynamic] ? [TOnDynamic] extends [TOnDynamic & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamic : TOnDynamic, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnDynamicAsync] ? [TOnDynamicAsync] extends [TOnDynamicAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamicAsync : TOnDynamicAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnServer] ? [TOnServer] extends [TOnServer & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnServer : TOnServer, [unknown] extends [TSubmitMeta] ? any : TSubmitMeta, {}, {
1287
+ render,
1288
+ props
1289
+ }: _tanstack_react_form0.WithFormProps<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta, {}, {
1294
1290
  readonly SubmitButton: typeof SubmitButton;
1295
1291
  readonly CardSaveBar: typeof CardSaveBar;
1296
- }>;
1297
- }>>;
1292
+ }, TRenderProps>) => React$1.FunctionComponent<React$1.PropsWithChildren<NoInfer<[unknown] extends [TRenderProps] ? any : TRenderProps> & {
1293
+ form: _tanstack_react_form0.AppFieldExtendedReactFormApi<[unknown] extends [TFormData] ? any : TFormData, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnMount] ? [TOnMount] extends [TOnMount & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnMount : TOnMount, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnChange] ? [TOnChange] extends [TOnChange & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnChange : TOnChange, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnChangeAsync] ? [TOnChangeAsync] extends [TOnChangeAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnChangeAsync : TOnChangeAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnBlur] ? [TOnBlur] extends [TOnBlur & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlur : TOnBlur, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnBlurAsync] ? [TOnBlurAsync] extends [TOnBlurAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlurAsync : TOnBlurAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnSubmit] ? [TOnSubmit] extends [TOnSubmit & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmit : TOnSubmit, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnSubmitAsync] ? [TOnSubmitAsync] extends [TOnSubmitAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmitAsync : TOnSubmitAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnDynamic] ? [TOnDynamic] extends [TOnDynamic & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamic : TOnDynamic, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnDynamicAsync] ? [TOnDynamicAsync] extends [TOnDynamicAsync & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamicAsync : TOnDynamicAsync, [_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined] extends [TOnServer] ? [TOnServer] extends [TOnServer & (_tanstack_react_form0.FormValidateOrFn<TFormData> | undefined)] ? any : TOnServer : TOnServer, [unknown] extends [TSubmitMeta] ? any : TSubmitMeta, {}, {
1294
+ readonly SubmitButton: typeof SubmitButton;
1295
+ readonly CardSaveBar: typeof CardSaveBar;
1296
+ }>;
1297
+ }>>, withFieldGroup: <TFieldGroupData, TSubmitMeta, TRenderProps extends object = {}>({
1298
+ render,
1299
+ props,
1300
+ defaultValues
1301
+ }: _tanstack_react_form0.WithFieldGroupProps<TFieldGroupData, {}, {
1302
+ readonly SubmitButton: typeof SubmitButton;
1303
+ readonly CardSaveBar: typeof CardSaveBar;
1304
+ }, TSubmitMeta, TRenderProps>) => <TFormData, TFields extends _tanstack_react_form0.DeepKeysOfType<TFormData, TFieldGroupData | null | undefined> | _tanstack_react_form0.FieldsMap<TFormData, TFieldGroupData>, TOnMount extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnChange extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnChangeAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnBlur extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnBlurAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnSubmit extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnSubmitAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnDynamic extends _tanstack_react_form0.FormValidateOrFn<TFormData> | undefined, TOnDynamicAsync extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TOnServer extends _tanstack_react_form0.FormAsyncValidateOrFn<TFormData> | undefined, TFormSubmitMeta>(params: React$1.PropsWithChildren<NoInfer<TRenderProps> & {
1305
+ form: _tanstack_react_form0.AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, {}, {
1306
+ readonly SubmitButton: typeof SubmitButton;
1307
+ readonly CardSaveBar: typeof CardSaveBar;
1308
+ }> | _tanstack_react_form0.AppFieldExtendedReactFieldGroupApi<unknown, TFormData, string | _tanstack_react_form0.FieldsMap<unknown, TFormData>, any, any, any, any, any, any, any, any, any, any, unknown extends TSubmitMeta ? TFormSubmitMeta : TSubmitMeta, {}, {
1309
+ readonly SubmitButton: typeof SubmitButton;
1310
+ readonly CardSaveBar: typeof CardSaveBar;
1311
+ }>;
1312
+ fields: TFields;
1313
+ }>) => ReturnType<React$1.FunctionComponent>;
1298
1314
  //#endregion
1299
1315
  //#region src/form/form-context.d.ts
1300
1316
  /**
@@ -1311,6 +1327,18 @@ declare const withForm: <TFormData, TOnMount extends _tanstack_react_form0.FormV
1311
1327
  declare const fieldContext: React$1.Context<_tanstack_react_form0.AnyFieldApi>, formContext: React$1.Context<_tanstack_react_form0.AnyFormApi>, useFieldContext: <TData>() => _tanstack_react_form0.FieldApi<any, string, TData, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>, useFormContext: () => _tanstack_react_form0.ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
1312
1328
  //#endregion
1313
1329
  //#region src/components/form-field.d.ts
1330
+ type FormFieldShellProps = {
1331
+ label?: string;
1332
+ description?: string;
1333
+ necessityIndicator?: 'required' | 'optional';
1334
+ children: React$1.ReactNode;
1335
+ };
1336
+ declare function FormFieldShell({
1337
+ label,
1338
+ description,
1339
+ necessityIndicator,
1340
+ children
1341
+ }: FormFieldShellProps): react_jsx_runtime0.JSX.Element;
1314
1342
  /**
1315
1343
  * Precisely-typed view of the bound field handed to a wrapper's `children`.
1316
1344
  * `TData` is `DeepValue<TFormData, TName>` — so `value` and `handleChange` are the field's
@@ -1344,26 +1372,60 @@ type BoundForm = {
1344
1372
  };
1345
1373
  AppField: (...args: never[]) => unknown;
1346
1374
  };
1347
- type FieldValidator<TFormData, TName> = TName extends DeepKeys<TFormData> ? FieldValidateOrFn<TFormData, TName> : never;
1348
- type FieldAsyncValidator<TFormData, TName> = TName extends DeepKeys<TFormData> ? FieldAsyncValidateOrFn<TFormData, TName> : never;
1349
- /** Field validators typed against the field's value — the `validators` prop of every wrapper. */
1350
- type FormFieldValidators<TFormData, TName> = {
1351
- onMount?: FieldValidator<TFormData, TName>;
1352
- onChange?: FieldValidator<TFormData, TName>;
1353
- onChangeAsync?: FieldAsyncValidator<TFormData, TName>;
1354
- onChangeAsyncDebounceMs?: number;
1355
- onBlur?: FieldValidator<TFormData, TName>;
1356
- onBlurAsync?: FieldAsyncValidator<TFormData, TName>;
1357
- onBlurAsyncDebounceMs?: number;
1358
- onSubmit?: FieldValidator<TFormData, TName>;
1359
- onSubmitAsync?: FieldAsyncValidator<TFormData, TName>;
1375
+ /**
1376
+ * Field validators typed against the field's value the `validators` prop of every wrapper.
1377
+ *
1378
+ * Reuses TanStack's own `FieldValidators` (the exact interface `AppField` accepts) instead of a
1379
+ * hand-rolled mirror, so every slot AppField supports is exposed with identical typing:
1380
+ * `onMount`, `onChange`/`onChangeAsync`/`onChangeListenTo`, `onBlur`/`onBlurAsync`/`onBlurListenTo`,
1381
+ * `onSubmit`/`onSubmitAsync`, `onDynamic`/`onDynamicAsync`, plus the `*DebounceMs` knobs.
1382
+ *
1383
+ * The per-slot validator generics can't be inferred here (the wrapper is generic over `TForm`/
1384
+ * `TName`, not a generic *function* like `AppField`), so each is instantiated to its widest
1385
+ * `FieldValidateOrFn`/`FieldAsyncValidateOrFn`. `value` still infers as the field's real type
1386
+ * because `TData` (`DeepValue<TFormData, TName>`) is concrete. The outer conditional keeps `TName`
1387
+ * unconstrained at the boundary so `DeepKeysOfType` subsets (e.g. the string-only Select keys)
1388
+ * resolve without a constraint clash, exactly like the previous guard.
1389
+ */
1390
+ type FormFieldValidators<TFormData, TName> = TName extends DeepKeys<TFormData> ? FieldValidators<TFormData, TName, DeepValue<TFormData, TName>, FieldValidateOrFn<TFormData, TName>, FieldValidateOrFn<TFormData, TName>, FieldAsyncValidateOrFn<TFormData, TName>, FieldValidateOrFn<TFormData, TName>, FieldAsyncValidateOrFn<TFormData, TName>, FieldValidateOrFn<TFormData, TName>, FieldAsyncValidateOrFn<TFormData, TName>, FieldValidateOrFn<TFormData, TName>, FieldAsyncValidateOrFn<TFormData, TName>> : never;
1391
+ /**
1392
+ * The full `AppField` binding surface every wrapper forwards, typed against the field's value.
1393
+ *
1394
+ * This is the **single source of truth** for which `form.AppField` props the `Form*Field` wrappers
1395
+ * pass through — spread it into each wrapper's props so the set can't drift per-wrapper.
1396
+ *
1397
+ * Forwarded: `validators`, `listeners`, `defaultValue`, `asyncDebounceMs`, `asyncAlways`.
1398
+ * Deliberately omitted: `name`/`children` (owned by the wrapper), `mode` (owned by `FormArrayField`,
1399
+ * which is the only wrapper that drives an `AppField` in `mode="array"`), and
1400
+ * `defaultMeta`/`disableErrorFlat` (advanced escape hatches that fight the shared shell).
1401
+ */
1402
+ type FieldBindingProps<TFormData, TName> = TName extends DeepKeys<TFormData> ? {
1403
+ validators?: FormFieldValidators<TFormData, TName>;
1404
+ listeners?: FieldListeners<TFormData, TName>;
1405
+ defaultValue?: DeepValue<TFormData, TName>;
1406
+ asyncDebounceMs?: number;
1407
+ asyncAlways?: boolean;
1408
+ } : never;
1409
+ /**
1410
+ * Presentational necessity prop every wrapper exposes, forwarded to the DS `FieldLabel`. Follows
1411
+ * the "mark the exceptions" convention: tag only the minority necessity in a form (`'optional'` in
1412
+ * a mostly-mandatory form, `'required'` in a mostly-optional one), never both. Label-only —
1413
+ * enforcement stays in `validators`. Shared so the set can't drift per-wrapper.
1414
+ */
1415
+ type NecessityProps = {
1416
+ necessityIndicator?: 'required' | 'optional';
1360
1417
  };
1361
1418
  type FormFieldBaseProps<TData, TControl> = {
1362
1419
  form: Pick<BoundForm, 'AppField'>;
1363
1420
  name: string;
1364
1421
  validators?: unknown;
1365
- label: string;
1366
- description?: string; /** Builds the primitive-specific wiring bundle handed to `children`. */
1422
+ listeners?: unknown;
1423
+ defaultValue?: unknown;
1424
+ asyncDebounceMs?: number;
1425
+ asyncAlways?: boolean;
1426
+ label?: string;
1427
+ description?: string;
1428
+ necessityIndicator?: 'required' | 'optional'; /** Builds the primitive-specific wiring bundle handed to `children`. */
1367
1429
  buildControl: (field: TypedField<TData>) => TControl;
1368
1430
  children: (field: TypedField<TData>, control: TControl) => React$1.ReactNode;
1369
1431
  };
@@ -1393,11 +1455,10 @@ type SelectControl = {
1393
1455
  type FormSelectFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1394
1456
  /** The form instance from `useForm()` — threaded as a prop so `name`/`validators` are typed. */form: TForm;
1395
1457
  name: TName;
1396
- label: string;
1397
- description?: string;
1398
- validators?: FormFieldValidators<TForm['state']['values'], TName>; /** Compose the Select yourself; spread the bundles. Trigger/content stay fully customizable. */
1458
+ label?: string;
1459
+ description?: string; /** Compose the Select yourself; spread the bundles. Trigger/content stay fully customizable. */
1399
1460
  children: (field: TypedField<string>, control: SelectControl) => React$1.ReactNode;
1400
- };
1461
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1401
1462
  /**
1402
1463
  * Select field wrapper. Owns the binding + shared `Field/Label/Error` shell, and hands the
1403
1464
  * consumer pre-wired `selectProps` / `selectTriggerProps` to spread onto `@aircall/ds` Select
@@ -1434,11 +1495,10 @@ type InputControl = {
1434
1495
  type FormInputFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1435
1496
  form: TForm;
1436
1497
  name: TName;
1437
- label: string;
1498
+ label?: string;
1438
1499
  description?: string;
1439
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1440
1500
  children: (field: TypedField<string>, control: InputControl) => React$1.ReactNode;
1441
- };
1501
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1442
1502
  /**
1443
1503
  * Text-input field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1444
1504
  * with an `inputProps` bundle. Compose the input yourself (add adornments, counters, prefixes).
@@ -1470,11 +1530,10 @@ type ComboboxControl = {
1470
1530
  type FormComboboxFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1471
1531
  form: TForm;
1472
1532
  name: TName;
1473
- label: string;
1533
+ label?: string;
1474
1534
  description?: string;
1475
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1476
1535
  children: (field: TypedField<string>, control: ComboboxControl) => React$1.ReactNode;
1477
- };
1536
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1478
1537
  /**
1479
1538
  * Combobox (searchable single-select) field wrapper for **string-valued** fields. Same threaded-
1480
1539
  * `form` typing and shared shell as the other wrappers; the render-prop hands you `comboboxProps`
@@ -1515,11 +1574,10 @@ type MultiComboboxControl = {
1515
1574
  type FormMultiComboboxFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string[]>> = {
1516
1575
  form: TForm;
1517
1576
  name: TName;
1518
- label: string;
1577
+ label?: string;
1519
1578
  description?: string;
1520
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1521
1579
  children: (field: TypedField<string[]>, control: MultiComboboxControl) => React$1.ReactNode;
1522
- };
1580
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1523
1581
  /**
1524
1582
  * Multi-select combobox field wrapper — same threaded-`form` typing and shared shell as
1525
1583
  * `FormSelectField`, with `comboboxProps` / `comboboxInputProps` bundles. `name` is restricted
@@ -1536,6 +1594,80 @@ type FormMultiComboboxFieldProps<TForm extends BoundForm, TName extends DeepKeys
1536
1594
  */
1537
1595
  declare function FormMultiComboboxField<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string[]>>(props: FormMultiComboboxFieldProps<TForm, TName>): react_jsx_runtime0.JSX.Element;
1538
1596
  //#endregion
1597
+ //#region src/components/form-array-field.d.ts
1598
+ /**
1599
+ * The element type of the bound array field — `DeepValue<TFormData, TName>` unwrapped one level.
1600
+ * `contacts: { label: string; value: string }[]` ⇒ `{ label: string; value: string }`.
1601
+ */
1602
+ type ArrayItem<TFormData, TName> = DeepValue<TFormData, TName> extends ReadonlyArray<infer TItem> ? TItem : never;
1603
+ /**
1604
+ * Precisely-typed view of an array-mode field handed to a `FormArrayField` child.
1605
+ *
1606
+ * Unlike the single-control `TypedField`, this exposes TanStack's array mutators (kept under their
1607
+ * native names so it's a transparent pass-through of `form.AppField` in `mode="array"`). The
1608
+ * `options?` argument each mutator also accepts upstream is intentionally elided — the common path
1609
+ * is index/value only; reach for `form.AppField` directly if you need it.
1610
+ *
1611
+ * Children read `state.value` to map over the rows and render nested `Form*Field`s with **indexed**
1612
+ * names (`contacts[${i}].label`), and call the mutators from add/remove/reorder controls.
1613
+ */
1614
+ type ArrayField<TItem> = {
1615
+ state: {
1616
+ value: TItem[];
1617
+ meta: {
1618
+ errors: readonly unknown[];
1619
+ isTouched: boolean;
1620
+ };
1621
+ }; /** Append `value` to the end of the array. */
1622
+ pushValue: (value: TItem) => void; /** Insert `value` at `index`, shifting later items right. */
1623
+ insertValue: (index: number, value: TItem) => void; /** Replace the item at `index` in place. */
1624
+ replaceValue: (index: number, value: TItem) => void; /** Remove the item at `index`, shifting later items left. */
1625
+ removeValue: (index: number) => void; /** Swap the items at `aIndex` and `bIndex`. */
1626
+ swapValues: (aIndex: number, bIndex: number) => void; /** Move the item at `aIndex` to `bIndex`, shifting the rest to fill. */
1627
+ moveValue: (aIndex: number, bIndex: number) => void; /** Remove every item. */
1628
+ clearValues: () => void; /** Mark the array field touched (e.g. on blur of the whole group). */
1629
+ handleBlur: () => void;
1630
+ };
1631
+ type FormArrayFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], unknown[]>> = {
1632
+ /** The form instance from `useForm()` — threaded as a prop so `name`/`validators` are typed. */form: TForm;
1633
+ name: TName;
1634
+ label?: string;
1635
+ description?: string;
1636
+ /**
1637
+ * Render the list. Map over `field.state.value`, render a nested `Form*Field` per row (same
1638
+ * `form`, indexed `name`), and wire add/remove/reorder controls to the array mutators.
1639
+ */
1640
+ children: (field: ArrayField<ArrayItem<TForm['state']['values'], TName>>) => React$1.ReactNode;
1641
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1642
+ /**
1643
+ * Array field wrapper — drives a `form.AppField` in `mode="array"` and owns the shared
1644
+ * `Field/Label/Description/Error` shell, so an array-level validator (e.g. "add at least one")
1645
+ * surfaces under the list. `name` is restricted to the form's **array** fields, and the child's
1646
+ * `field` is typed to the element type, with TanStack's array mutators (`pushValue`, `removeValue`,
1647
+ * `moveValue`, …) exposed for add/remove/reorder.
1648
+ *
1649
+ * It deliberately has **no control bundle**: an array is a container of rows, not a single input.
1650
+ * Each row is composed from the existing single-control wrappers with an indexed `name`.
1651
+ *
1652
+ * @example
1653
+ * <FormArrayField form={form} name="webhooks" label="Webhooks" description="Endpoints to notify">
1654
+ * {field => (
1655
+ * <>
1656
+ * {field.state.value.map((_row, i) => (
1657
+ * <div key={i} className="flex items-end gap-2">
1658
+ * <FormInputField form={form} name={`webhooks[${i}].url`} label="URL">
1659
+ * {(_f, { inputProps }) => <Input {...inputProps} placeholder="https://…" />}
1660
+ * </FormInputField>
1661
+ * <Button variant="ghost" onClick={() => field.removeValue(i)}>Remove</Button>
1662
+ * </div>
1663
+ * ))}
1664
+ * <Button variant="outline" onClick={() => field.pushValue({ url: '' })}>Add webhook</Button>
1665
+ * </>
1666
+ * )}
1667
+ * </FormArrayField>
1668
+ */
1669
+ declare function FormArrayField<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], unknown[]>>(props: FormArrayFieldProps<TForm, TName>): react_jsx_runtime0.JSX.Element;
1670
+ //#endregion
1539
1671
  //#region src/components/form-textarea-field.d.ts
1540
1672
  /**
1541
1673
  * Wiring bundle handed to a `FormTextareaField` child. Spread `textareaProps` onto a DS
@@ -1552,11 +1684,10 @@ type TextareaControl = {
1552
1684
  type FormTextareaFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1553
1685
  form: TForm;
1554
1686
  name: TName;
1555
- label: string;
1687
+ label?: string;
1556
1688
  description?: string;
1557
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1558
1689
  children: (field: TypedField<string>, control: TextareaControl) => React$1.ReactNode;
1559
- };
1690
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1560
1691
  /**
1561
1692
  * Multiline text-input field wrapper — same threaded-`form` typing and shared shell as
1562
1693
  * `FormSelectField`, with a `textareaProps` bundle. Compose the textarea yourself (rows,
@@ -1586,11 +1717,10 @@ type RadioGroupControl = {
1586
1717
  type FormRadioGroupFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1587
1718
  form: TForm;
1588
1719
  name: TName;
1589
- label: string;
1720
+ label?: string;
1590
1721
  description?: string;
1591
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1592
1722
  children: (field: TypedField<string>, control: RadioGroupControl) => React$1.ReactNode;
1593
- };
1723
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1594
1724
  /**
1595
1725
  * Radio-group field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1596
1726
  * with a `radioGroupProps` bundle. Compose the items yourself. `name` is restricted to the
@@ -1623,11 +1753,10 @@ type OTPControl = {
1623
1753
  type FormOTPFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string>> = {
1624
1754
  form: TForm;
1625
1755
  name: TName;
1626
- label: string;
1756
+ label?: string;
1627
1757
  description?: string;
1628
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1629
1758
  children: (field: TypedField<string>, control: OTPControl) => React$1.ReactNode;
1630
- };
1759
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1631
1760
  /**
1632
1761
  * One-time-code field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1633
1762
  * with an `otpProps` bundle. Compose the slots yourself. `name` is restricted to the form's
@@ -1656,11 +1785,10 @@ type SwitchControl = {
1656
1785
  type FormSwitchFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], boolean>> = {
1657
1786
  form: TForm;
1658
1787
  name: TName;
1659
- label: string;
1788
+ label?: string;
1660
1789
  description?: string;
1661
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1662
1790
  children: (field: TypedField<boolean>, control: SwitchControl) => React$1.ReactNode;
1663
- };
1791
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1664
1792
  /**
1665
1793
  * Toggle field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1666
1794
  * with a `switchProps` bundle. `name` is restricted to the form's **boolean** fields — a
@@ -1673,38 +1801,37 @@ type FormSwitchFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<
1673
1801
  */
1674
1802
  declare function FormSwitchField<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], boolean>>(props: FormSwitchFieldProps<TForm, TName>): react_jsx_runtime0.JSX.Element;
1675
1803
  //#endregion
1676
- //#region src/components/form-number-field.d.ts
1804
+ //#region src/components/form-numeric-field.d.ts
1677
1805
  /**
1678
- * Wiring bundle handed to a `FormNumberField` child. Spread `numberProps` onto a DS `Input`
1679
- * with `type="number"` — value/onChange/onBlur/aria are pre-wired (empty input coerces to `0`).
1806
+ * Wiring bundle handed to a `FormNumericField` child. Spread `numericProps` onto a DS
1807
+ * `NumericInput` — value/onValueChange/onBlur/aria are pre-wired (a cleared input coerces to `0`).
1680
1808
  */
1681
- type NumberControl = {
1682
- numberProps: {
1809
+ type NumericControl = {
1810
+ numericProps: {
1683
1811
  value: number;
1684
- onChange: (event: React$1.ChangeEvent<HTMLInputElement>) => void;
1812
+ onValueChange: (value: number | null) => void;
1685
1813
  onBlur: () => void;
1686
1814
  'aria-invalid': true | undefined;
1687
1815
  };
1688
1816
  };
1689
- type FormNumberFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], number>> = {
1817
+ type FormNumericFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], number>> = {
1690
1818
  form: TForm;
1691
1819
  name: TName;
1692
- label: string;
1820
+ label?: string;
1693
1821
  description?: string;
1694
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1695
- children: (field: TypedField<number>, control: NumberControl) => React$1.ReactNode;
1696
- };
1822
+ children: (field: TypedField<number>, control: NumericControl) => React$1.ReactNode;
1823
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1697
1824
  /**
1698
- * Numeric-input field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1699
- * with a `numberProps` bundle. `name` is restricted to the form's **number** fields — a
1700
- * string/boolean `name` is a type error.
1825
+ * Numeric field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1826
+ * with a `numericProps` bundle for the DS `NumericInput` (prefer it over `Input type="number"`).
1827
+ * `name` is restricted to the form's **number** fields — a string/boolean `name` is a type error.
1701
1828
  *
1702
1829
  * @example
1703
- * <FormNumberField form={form} name="age" label="Age" validators={{ onChange }}>
1704
- * {(field, { numberProps }) => <Input type="number" {...numberProps} />}
1705
- * </FormNumberField>
1830
+ * <FormNumericField form={form} name="age" label="Age" validators={{ onChange }}>
1831
+ * {(field, { numericProps }) => <NumericInput {...numericProps} />}
1832
+ * </FormNumericField>
1706
1833
  */
1707
- declare function FormNumberField<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], number>>(props: FormNumberFieldProps<TForm, TName>): react_jsx_runtime0.JSX.Element;
1834
+ declare function FormNumericField<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], number>>(props: FormNumericFieldProps<TForm, TName>): react_jsx_runtime0.JSX.Element;
1708
1835
  //#endregion
1709
1836
  //#region src/components/form-slider-field.d.ts
1710
1837
  /**
@@ -1721,11 +1848,10 @@ type SliderControl = {
1721
1848
  type FormSliderFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], number>> = {
1722
1849
  form: TForm;
1723
1850
  name: TName;
1724
- label: string;
1851
+ label?: string;
1725
1852
  description?: string;
1726
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1727
1853
  children: (field: TypedField<number>, control: SliderControl) => React$1.ReactNode;
1728
- };
1854
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1729
1855
  /**
1730
1856
  * Slider field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
1731
1857
  * with a `sliderProps` bundle. `name` is restricted to the form's **number** fields; the bundle
@@ -1754,11 +1880,10 @@ type ToggleGroupControl = {
1754
1880
  type FormToggleGroupFieldProps<TForm extends BoundForm, TName extends DeepKeysOfType<TForm['state']['values'], string | null>> = {
1755
1881
  form: TForm;
1756
1882
  name: TName;
1757
- label: string;
1883
+ label?: string;
1758
1884
  description?: string;
1759
- validators?: FormFieldValidators<TForm['state']['values'], TName>;
1760
1885
  children: (field: TypedField<string | null>, control: ToggleGroupControl) => React$1.ReactNode;
1761
- };
1886
+ } & FieldBindingProps<TForm['state']['values'], TName> & NecessityProps;
1762
1887
  /**
1763
1888
  * Single-select toggle-group field wrapper — same threaded-`form` typing and shared shell as
1764
1889
  * `FormSelectField`, with a `toggleGroupProps` bundle. `name` is restricted to the form's
@@ -2006,4 +2131,4 @@ declare namespace DashboardSidebarSubmenuSeparator {
2006
2131
  type Props = DashboardSidebarSubmenuSeparatorProps;
2007
2132
  }
2008
2133
  //#endregion
2009
- export { BetaBadge, type BoundForm, CardSaveBar, type CardSaveBarProps, ChatbotExpandSuggestion, type ChatbotExpandSuggestionProps, ChatbotInput, type ChatbotInputProps, ChatbotPage, ChatbotPageBody, type ChatbotPageBodyProps, ChatbotPageContent, type ChatbotPageContentProps, ChatbotPageConversation, type ChatbotPageConversationProps, ChatbotPageFooter, type ChatbotPageFooterProps, ChatbotPageHeading, type ChatbotPageHeadingProps, ChatbotPageInputBar, type ChatbotPageInputBarProps, type ChatbotPageProps, ChatbotPanel, ChatbotPanelHeader, type ChatbotPanelHeaderProps, type ChatbotPanelProps, ChatbotPanelSuggestion, type ChatbotPanelSuggestionItem, type ChatbotPanelSuggestionProps, ChatbotPanelTrigger, type ChatbotPanelTriggerProps, ChatbotResponseBlock, type ChatbotResponseBlockFeedback, type ChatbotResponseBlockProps, ChatbotSidebar, type ChatbotSidebarConversation, ChatbotUserMessage, type ChatbotUserMessageProps, type ComboboxControl, ComingSoonDescription, ComingSoonEmptyState, ComingSoonMedia, ComingSoonTitle, CommonForm, CopyButton, CopyButtonIcon, CopyButtonLabel, DashboardPage, DashboardPageBanner, DashboardPageContent, DashboardPageHeader, DashboardPageHeaderAction, DashboardPageHeaderActions, DashboardPageHeaderDescription, DashboardPageHeaderNav, DashboardPageHeaderNavBack, DashboardPageHeaderPrefix, DashboardPageHeaderSubtitle, DashboardPageHeaderTitle, DashboardPageHeaderTitleGroup, DashboardPageMain, DashboardPageTabs, DashboardSidebar, DashboardSidebarContent, DashboardSidebarFooter, DashboardSidebarGroup, DashboardSidebarGroupLabel, DashboardSidebarHeader, DashboardSidebarMenu, DashboardSidebarMenuBadge, DashboardSidebarMenuButton, DashboardSidebarMenuItem, DashboardSidebarNav, type DashboardSidebarNavGroup, type DashboardSidebarNavItem, type DashboardSidebarNavItemAction, type DashboardSidebarNavItemLink, type DashboardSidebarNavItemSubmenu, type DashboardSidebarNavSubmenuItem, DashboardSidebarProvider, DashboardSidebarSubmenu, DashboardSidebarSubmenuItem, DashboardSidebarSubmenuSeparator, DashboardSidebarTrigger, DashboardStandalonePage, DashboardStandalonePageAction, DashboardStandalonePageActions, DashboardStandalonePageContent, DashboardStandalonePageHeader, DashboardStandalonePageTitle, DeprecatedBadge, EmptyState, EmptyStateActions, EmptyStateButton, EmptyStateDescription, EmptyStateExternalLink, EmptyStateHeader, EmptyStateMedia, EmptyStateTitle, FormComboboxField, type FormComboboxFieldProps, FormFieldBase, type FormFieldValidators, FormInputField, type FormInputFieldProps, FormMultiComboboxField, type FormMultiComboboxFieldProps, FormNumberField, type FormNumberFieldProps, FormOTPField, type FormOTPFieldProps, FormRadioGroupField, type FormRadioGroupFieldProps, FormSelectField, type FormSelectFieldProps, FormSliderField, type FormSliderFieldProps, FormSwitchField, type FormSwitchFieldProps, FormTextareaField, type FormTextareaFieldProps, FormToggleGroupField, type FormToggleGroupFieldProps, type InputControl, type MultiComboboxControl, NewBadge, NoDataDescription, NoDataEmptyState, NoDataMedia, NoDataTitle, NoSupportDescription, NoSupportEmptyState, NoSupportMedia, NoSupportTitle, NotFoundDescription, NotFoundEmptyState, NotFoundMedia, NotFoundTitle, type NumberControl, type OTPControl, ProfessionalBadge, type RadioGroupControl, RestrictedAccessDescription, RestrictedAccessEmptyState, RestrictedAccessMedia, RestrictedAccessTitle, RoleBadge, type RoleBadgeRole, SaveBar, type SaveBarProps, type SelectControl, type SliderControl, SubmitButton, type SubmitButtonProps, type SwitchControl, type TextareaControl, type ToggleGroupControl, TrialBadge, type TypedField, UnknownErrorDescription, UnknownErrorEmptyState, UnknownErrorMedia, UnknownErrorTitle, fieldContext, formContext, toFieldErrors, useCopyToClipboard, useSidebar as useDashboardSidebar, useFieldContext, useForm, useFormContext, withForm };
2134
+ export { type ArrayField, type ArrayItem, BetaBadge, type BoundForm, CardSaveBar, type CardSaveBarProps, ChatbotExpandSuggestion, type ChatbotExpandSuggestionProps, ChatbotInput, type ChatbotInputProps, ChatbotPage, ChatbotPageBody, type ChatbotPageBodyProps, ChatbotPageContent, type ChatbotPageContentProps, ChatbotPageConversation, type ChatbotPageConversationProps, ChatbotPageFooter, type ChatbotPageFooterProps, ChatbotPageHeading, type ChatbotPageHeadingProps, ChatbotPageInputBar, type ChatbotPageInputBarProps, type ChatbotPageProps, ChatbotPanel, ChatbotPanelHeader, type ChatbotPanelHeaderProps, type ChatbotPanelProps, ChatbotPanelSuggestion, type ChatbotPanelSuggestionItem, type ChatbotPanelSuggestionProps, ChatbotPanelTrigger, type ChatbotPanelTriggerProps, ChatbotResponseBlock, type ChatbotResponseBlockFeedback, type ChatbotResponseBlockProps, ChatbotSidebar, type ChatbotSidebarConversation, ChatbotUserMessage, type ChatbotUserMessageProps, type ComboboxControl, ComingSoonDescription, ComingSoonEmptyState, ComingSoonMedia, ComingSoonTitle, CommonForm, CopyButton, CopyButtonIcon, CopyButtonLabel, DashboardPage, DashboardPageBanner, DashboardPageContent, DashboardPageHeader, DashboardPageHeaderAction, DashboardPageHeaderActions, DashboardPageHeaderDescription, DashboardPageHeaderNav, DashboardPageHeaderNavBack, DashboardPageHeaderPrefix, DashboardPageHeaderSubtitle, DashboardPageHeaderTitle, DashboardPageHeaderTitleGroup, DashboardPageMain, DashboardPageTabs, DashboardSidebar, DashboardSidebarContent, DashboardSidebarFooter, DashboardSidebarGroup, DashboardSidebarGroupLabel, DashboardSidebarHeader, DashboardSidebarMenu, DashboardSidebarMenuBadge, DashboardSidebarMenuButton, DashboardSidebarMenuItem, DashboardSidebarNav, type DashboardSidebarNavGroup, type DashboardSidebarNavItem, type DashboardSidebarNavItemAction, type DashboardSidebarNavItemLink, type DashboardSidebarNavItemSubmenu, type DashboardSidebarNavSubmenuItem, DashboardSidebarProvider, DashboardSidebarSubmenu, DashboardSidebarSubmenuItem, DashboardSidebarSubmenuSeparator, DashboardSidebarTrigger, DashboardStandalonePage, DashboardStandalonePageAction, DashboardStandalonePageActions, DashboardStandalonePageContent, DashboardStandalonePageHeader, DashboardStandalonePageTitle, DeprecatedBadge, EmptyState, EmptyStateActions, EmptyStateButton, EmptyStateDescription, EmptyStateExternalLink, EmptyStateHeader, EmptyStateMedia, EmptyStateTitle, type FieldBindingProps, FormArrayField, type FormArrayFieldProps, FormComboboxField, type FormComboboxFieldProps, FormFieldBase, FormFieldShell, type FormFieldShellProps, type FormFieldValidators, FormInputField, type FormInputFieldProps, FormMultiComboboxField, type FormMultiComboboxFieldProps, FormNumericField, type FormNumericFieldProps, FormOTPField, type FormOTPFieldProps, FormRadioGroupField, type FormRadioGroupFieldProps, FormSelectField, type FormSelectFieldProps, FormSliderField, type FormSliderFieldProps, FormSwitchField, type FormSwitchFieldProps, FormTextareaField, type FormTextareaFieldProps, FormToggleGroupField, type FormToggleGroupFieldProps, type InputControl, type MultiComboboxControl, type NecessityProps, NewBadge, NoDataDescription, NoDataEmptyState, NoDataMedia, NoDataTitle, NoSupportDescription, NoSupportEmptyState, NoSupportMedia, NoSupportTitle, NotFoundDescription, NotFoundEmptyState, NotFoundMedia, NotFoundTitle, type NumericControl, type OTPControl, ProfessionalBadge, type RadioGroupControl, RestrictedAccessDescription, RestrictedAccessEmptyState, RestrictedAccessMedia, RestrictedAccessTitle, RoleBadge, type RoleBadgeRole, SaveBar, type SaveBarProps, type SelectControl, type SliderControl, SubmitButton, type SubmitButtonProps, type SwitchControl, type TextareaControl, type ToggleGroupControl, TrialBadge, type TypedField, UnknownErrorDescription, UnknownErrorEmptyState, UnknownErrorMedia, UnknownErrorTitle, fieldContext, formContext, toFieldErrors, useCopyToClipboard, useSidebar as useDashboardSidebar, useFieldContext, useForm, useFormContext, withFieldGroup, withForm };
package/dist/index.js CHANGED
@@ -1930,7 +1930,7 @@ const CommonForm = createFormHook({
1930
1930
  }
1931
1931
  });
1932
1932
  const useForm = CommonForm.useAppForm;
1933
- const { withForm } = CommonForm;
1933
+ const { withForm, withFieldGroup } = CommonForm;
1934
1934
 
1935
1935
  //#endregion
1936
1936
  //#region src/form/field-errors.ts
@@ -1950,12 +1950,15 @@ function toFieldErrors(errors) {
1950
1950
 
1951
1951
  //#endregion
1952
1952
  //#region src/components/form-field.tsx
1953
- function FormFieldShell({ label, description, children }) {
1953
+ function FormFieldShell({ label, description, necessityIndicator, children }) {
1954
1954
  const errors = useFieldContext().state.meta.errors;
1955
1955
  return /* @__PURE__ */ jsxs(Field, {
1956
1956
  "data-invalid": errors.length > 0 ? true : void 0,
1957
1957
  children: [
1958
- /* @__PURE__ */ jsx(FieldLabel, { children: label }),
1958
+ label && /* @__PURE__ */ jsx(FieldLabel, {
1959
+ necessityIndicator,
1960
+ children: label
1961
+ }),
1959
1962
  description && /* @__PURE__ */ jsx(FieldDescription, { children: description }),
1960
1963
  children,
1961
1964
  /* @__PURE__ */ jsx(FieldError, { errors: toFieldErrors(errors) })
@@ -1968,14 +1971,19 @@ function FormFieldShell({ label, description, children }) {
1968
1971
  * the AppField + shared shell. `TData`/`TControl` carry the precise field + bundle shapes through.
1969
1972
  */
1970
1973
  function FormFieldBase(props) {
1971
- const { form, name, validators, label, description, buildControl, children } = props;
1974
+ const { form, name, validators, listeners, defaultValue, asyncDebounceMs, asyncAlways, label, description, necessityIndicator, buildControl, children } = props;
1972
1975
  const AppField = form.AppField;
1973
1976
  return /* @__PURE__ */ jsx(AppField, {
1974
1977
  name,
1975
1978
  validators,
1979
+ listeners,
1980
+ defaultValue,
1981
+ asyncDebounceMs,
1982
+ asyncAlways,
1976
1983
  children: (field) => /* @__PURE__ */ jsx(FormFieldShell, {
1977
1984
  label,
1978
1985
  description,
1986
+ necessityIndicator,
1979
1987
  children: children(field, buildControl(field))
1980
1988
  })
1981
1989
  });
@@ -2122,6 +2130,55 @@ function FormMultiComboboxField(props) {
2122
2130
  });
2123
2131
  }
2124
2132
 
2133
+ //#endregion
2134
+ //#region src/components/form-array-field.tsx
2135
+ /**
2136
+ * Array field wrapper — drives a `form.AppField` in `mode="array"` and owns the shared
2137
+ * `Field/Label/Description/Error` shell, so an array-level validator (e.g. "add at least one")
2138
+ * surfaces under the list. `name` is restricted to the form's **array** fields, and the child's
2139
+ * `field` is typed to the element type, with TanStack's array mutators (`pushValue`, `removeValue`,
2140
+ * `moveValue`, …) exposed for add/remove/reorder.
2141
+ *
2142
+ * It deliberately has **no control bundle**: an array is a container of rows, not a single input.
2143
+ * Each row is composed from the existing single-control wrappers with an indexed `name`.
2144
+ *
2145
+ * @example
2146
+ * <FormArrayField form={form} name="webhooks" label="Webhooks" description="Endpoints to notify">
2147
+ * {field => (
2148
+ * <>
2149
+ * {field.state.value.map((_row, i) => (
2150
+ * <div key={i} className="flex items-end gap-2">
2151
+ * <FormInputField form={form} name={`webhooks[${i}].url`} label="URL">
2152
+ * {(_f, { inputProps }) => <Input {...inputProps} placeholder="https://…" />}
2153
+ * </FormInputField>
2154
+ * <Button variant="ghost" onClick={() => field.removeValue(i)}>Remove</Button>
2155
+ * </div>
2156
+ * ))}
2157
+ * <Button variant="outline" onClick={() => field.pushValue({ url: '' })}>Add webhook</Button>
2158
+ * </>
2159
+ * )}
2160
+ * </FormArrayField>
2161
+ */
2162
+ function FormArrayField(props) {
2163
+ const { form, name, validators, listeners, defaultValue, asyncDebounceMs, asyncAlways, label, description, necessityIndicator, children } = props;
2164
+ const AppField = form.AppField;
2165
+ return /* @__PURE__ */ jsx(AppField, {
2166
+ name,
2167
+ mode: "array",
2168
+ validators,
2169
+ listeners,
2170
+ defaultValue,
2171
+ asyncDebounceMs,
2172
+ asyncAlways,
2173
+ children: (field) => /* @__PURE__ */ jsx(FormFieldShell, {
2174
+ label,
2175
+ description,
2176
+ necessityIndicator,
2177
+ children: children(field)
2178
+ })
2179
+ });
2180
+ }
2181
+
2125
2182
  //#endregion
2126
2183
  //#region src/components/form-textarea-field.tsx
2127
2184
  function textareaControl(field) {
@@ -2235,29 +2292,29 @@ function FormSwitchField(props) {
2235
2292
  }
2236
2293
 
2237
2294
  //#endregion
2238
- //#region src/components/form-number-field.tsx
2239
- function numberControl(field) {
2240
- return { numberProps: {
2295
+ //#region src/components/form-numeric-field.tsx
2296
+ function numericControl(field) {
2297
+ return { numericProps: {
2241
2298
  value: field.state.value,
2242
- onChange: (event) => field.handleChange(event.target.value === "" ? 0 : event.target.valueAsNumber),
2299
+ onValueChange: (value) => field.handleChange(value ?? 0),
2243
2300
  onBlur: field.handleBlur,
2244
2301
  "aria-invalid": field.state.meta.errors.length > 0 ? true : void 0
2245
2302
  } };
2246
2303
  }
2247
2304
  /**
2248
- * Numeric-input field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
2249
- * with a `numberProps` bundle. `name` is restricted to the form's **number** fields — a
2250
- * string/boolean `name` is a type error.
2305
+ * Numeric field wrapper — same threaded-`form` typing and shared shell as `FormSelectField`,
2306
+ * with a `numericProps` bundle for the DS `NumericInput` (prefer it over `Input type="number"`).
2307
+ * `name` is restricted to the form's **number** fields — a string/boolean `name` is a type error.
2251
2308
  *
2252
2309
  * @example
2253
- * <FormNumberField form={form} name="age" label="Age" validators={{ onChange }}>
2254
- * {(field, { numberProps }) => <Input type="number" {...numberProps} />}
2255
- * </FormNumberField>
2310
+ * <FormNumericField form={form} name="age" label="Age" validators={{ onChange }}>
2311
+ * {(field, { numericProps }) => <NumericInput {...numericProps} />}
2312
+ * </FormNumericField>
2256
2313
  */
2257
- function FormNumberField(props) {
2314
+ function FormNumericField(props) {
2258
2315
  return /* @__PURE__ */ jsx(FormFieldBase, {
2259
2316
  ...props,
2260
- buildControl: numberControl
2317
+ buildControl: numericControl
2261
2318
  });
2262
2319
  }
2263
2320
 
@@ -2651,4 +2708,4 @@ function DashboardSidebarNav({ groups, renderLink }) {
2651
2708
  }
2652
2709
 
2653
2710
  //#endregion
2654
- export { BetaBadge, CardSaveBar, ChatbotExpandSuggestion, ChatbotInput, ChatbotPage, ChatbotPageBody, ChatbotPageContent, ChatbotPageConversation, ChatbotPageFooter, ChatbotPageHeading, ChatbotPageInputBar, ChatbotPanel, ChatbotPanelHeader, ChatbotPanelSuggestion, ChatbotPanelTrigger, ChatbotResponseBlock, ChatbotSidebar, ChatbotUserMessage, ComingSoonDescription, ComingSoonEmptyState, ComingSoonMedia, ComingSoonTitle, CommonForm, CopyButton, CopyButtonIcon, CopyButtonLabel, DashboardPage, DashboardPageBanner, DashboardPageContent, DashboardPageHeader, DashboardPageHeaderAction, DashboardPageHeaderActions, DashboardPageHeaderDescription, DashboardPageHeaderNav, DashboardPageHeaderNavBack, DashboardPageHeaderPrefix, DashboardPageHeaderSubtitle, DashboardPageHeaderTitle, DashboardPageHeaderTitleGroup, DashboardPageMain, DashboardPageTabs, DashboardSidebar, DashboardSidebarContent, DashboardSidebarFooter, DashboardSidebarGroup, DashboardSidebarGroupLabel, DashboardSidebarHeader, DashboardSidebarMenu, DashboardSidebarMenuBadge, DashboardSidebarMenuButton, DashboardSidebarMenuItem, DashboardSidebarNav, DashboardSidebarProvider, DashboardSidebarSubmenu, DashboardSidebarSubmenuItem, DashboardSidebarSubmenuSeparator, DashboardSidebarTrigger, DashboardStandalonePage, DashboardStandalonePageAction, DashboardStandalonePageActions, DashboardStandalonePageContent, DashboardStandalonePageHeader, DashboardStandalonePageTitle, DeprecatedBadge, EmptyState, EmptyStateActions, EmptyStateButton, EmptyStateDescription, EmptyStateExternalLink, EmptyStateHeader, EmptyStateMedia, EmptyStateTitle, FormComboboxField, FormFieldBase, FormInputField, FormMultiComboboxField, FormNumberField, FormOTPField, FormRadioGroupField, FormSelectField, FormSliderField, FormSwitchField, FormTextareaField, FormToggleGroupField, NewBadge, NoDataDescription, NoDataEmptyState, NoDataMedia, NoDataTitle, NoSupportDescription, NoSupportEmptyState, NoSupportMedia, NoSupportTitle, NotFoundDescription, NotFoundEmptyState, NotFoundMedia, NotFoundTitle, ProfessionalBadge, RestrictedAccessDescription, RestrictedAccessEmptyState, RestrictedAccessMedia, RestrictedAccessTitle, RoleBadge, SaveBar, SubmitButton, TrialBadge, UnknownErrorDescription, UnknownErrorEmptyState, UnknownErrorMedia, UnknownErrorTitle, fieldContext, formContext, toFieldErrors, useCopyToClipboard, useSidebar as useDashboardSidebar, useFieldContext, useForm, useFormContext, withForm };
2711
+ export { BetaBadge, CardSaveBar, ChatbotExpandSuggestion, ChatbotInput, ChatbotPage, ChatbotPageBody, ChatbotPageContent, ChatbotPageConversation, ChatbotPageFooter, ChatbotPageHeading, ChatbotPageInputBar, ChatbotPanel, ChatbotPanelHeader, ChatbotPanelSuggestion, ChatbotPanelTrigger, ChatbotResponseBlock, ChatbotSidebar, ChatbotUserMessage, ComingSoonDescription, ComingSoonEmptyState, ComingSoonMedia, ComingSoonTitle, CommonForm, CopyButton, CopyButtonIcon, CopyButtonLabel, DashboardPage, DashboardPageBanner, DashboardPageContent, DashboardPageHeader, DashboardPageHeaderAction, DashboardPageHeaderActions, DashboardPageHeaderDescription, DashboardPageHeaderNav, DashboardPageHeaderNavBack, DashboardPageHeaderPrefix, DashboardPageHeaderSubtitle, DashboardPageHeaderTitle, DashboardPageHeaderTitleGroup, DashboardPageMain, DashboardPageTabs, DashboardSidebar, DashboardSidebarContent, DashboardSidebarFooter, DashboardSidebarGroup, DashboardSidebarGroupLabel, DashboardSidebarHeader, DashboardSidebarMenu, DashboardSidebarMenuBadge, DashboardSidebarMenuButton, DashboardSidebarMenuItem, DashboardSidebarNav, DashboardSidebarProvider, DashboardSidebarSubmenu, DashboardSidebarSubmenuItem, DashboardSidebarSubmenuSeparator, DashboardSidebarTrigger, DashboardStandalonePage, DashboardStandalonePageAction, DashboardStandalonePageActions, DashboardStandalonePageContent, DashboardStandalonePageHeader, DashboardStandalonePageTitle, DeprecatedBadge, EmptyState, EmptyStateActions, EmptyStateButton, EmptyStateDescription, EmptyStateExternalLink, EmptyStateHeader, EmptyStateMedia, EmptyStateTitle, FormArrayField, FormComboboxField, FormFieldBase, FormFieldShell, FormInputField, FormMultiComboboxField, FormNumericField, FormOTPField, FormRadioGroupField, FormSelectField, FormSliderField, FormSwitchField, FormTextareaField, FormToggleGroupField, NewBadge, NoDataDescription, NoDataEmptyState, NoDataMedia, NoDataTitle, NoSupportDescription, NoSupportEmptyState, NoSupportMedia, NoSupportTitle, NotFoundDescription, NotFoundEmptyState, NotFoundMedia, NotFoundTitle, ProfessionalBadge, RestrictedAccessDescription, RestrictedAccessEmptyState, RestrictedAccessMedia, RestrictedAccessTitle, RoleBadge, SaveBar, SubmitButton, TrialBadge, UnknownErrorDescription, UnknownErrorEmptyState, UnknownErrorMedia, UnknownErrorTitle, fieldContext, formContext, toFieldErrors, useCopyToClipboard, useSidebar as useDashboardSidebar, useFieldContext, useForm, useFormContext, withFieldGroup, withForm };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aircall/blocks",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "sideEffects": [
@@ -81,7 +81,7 @@
81
81
  "vite": "7.3.1",
82
82
  "vitest": "4.0.17",
83
83
  "zod": "4.4.3",
84
- "@aircall/ds": "0.16.1",
84
+ "@aircall/ds": "0.17.1",
85
85
  "@aircall/hooks": "0.5.1"
86
86
  },
87
87
  "keywords": [
@@ -102,6 +102,7 @@
102
102
  "sb:dev": "storybook dev -p 6009",
103
103
  "sb:build": "storybook build",
104
104
  "sb:build:chromatic": "storybook build --test --stats-json",
105
+ "test": "vitest --project=unit",
105
106
  "sb:test": "vitest --project=storybook",
106
107
  "chromatic": "chromatic --storybook-build-dir=./storybook-static"
107
108
  },