@forgeroute/ui 0.3.11 → 0.3.12

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from "@angular/core";
2
- import { ChangeDetectorRef, InjectionToken, OnDestroy, OnInit, Signal, WritableSignal } from "@angular/core";
2
+ import { ChangeDetectorRef, EnvironmentProviders, InjectionToken, OnDestroy, OnInit, Signal, TemplateRef, WritableSignal } from "@angular/core";
3
3
  import { AbstractControl, ControlValueAccessor, FormControl, ValidationErrors, Validator } from "@angular/forms";
4
4
  import { Observable, Subject } from "rxjs";
5
5
  import { HttpClient, HttpInterceptorFn } from "@angular/common/http";
@@ -145,7 +145,7 @@ export interface ColumnDef {
145
145
  overdueThreshold?: number;
146
146
  /**
147
147
  * Maps a row to a progress bar. Return null to render a plain '—' (e.g. nothing to track).
148
- * `label` is the text beside the bar (e.g. "3/5"); `tooltip` is the hover text. Leave `variant` out and the bar colors itself by completion (red / amber / blue / green).
148
+ * `label` is the text beside the bar (e.g. "3/5"); `tooltip` is the hover text. `ariaLabel` is the bar's accessible name (e.g. the row's name + " cap progress"); leave it out and the bar is named by its `label` text — which, with only "80%" to go on, tells a screen-reader user nothing about WHICH row it belongs to, so set it whenever several rows show a bar. Leave `variant` out and the bar colors itself by completion (red / amber / blue / green).
149
149
  */
150
150
  progressFn?: (item: any) => {
151
151
  value: number;
@@ -153,6 +153,7 @@ export interface ColumnDef {
153
153
  label?: string;
154
154
  variant?: 'blue' | 'green' | 'amber' | 'red' | 'slate';
155
155
  tooltip?: string;
156
+ ariaLabel?: string;
156
157
  } | null;
157
158
  /** Key in item holding the tags array. Defaults to col.key.
158
159
  * Expected shape: { name: string; variant?: BadgeVariant }[] */
@@ -237,6 +238,15 @@ export interface SavedView {
237
238
  */
238
239
  export type BulkUpdateOption<T = any> = string | {
239
240
  field: string;
241
+ /**
242
+ * Field changes (`{ field, label }`): the bulk bar button text instead of "Change <column label>", e.g. `{ field: 'state', label: 'Archive / Restore' }`.
243
+ * It is also the inline select's accessible name, the default confirm's title and confirm button, and the default confirm message reads
244
+ * "<label>: <option> for N items?" (instead of "Change <column> to <option> for N items?"). Host `confirm` copy still wins; its
245
+ * `ctx.columnLabel` stays the column's label. Trimmed; blank = ignored. Ignored for the reserved `delete` key. Omit it and nothing changes.
246
+ *
247
+ * { field: 'state', label: 'Archive / Restore' }
248
+ */
249
+ label?: string;
240
250
  exclude?: string[];
241
251
  allowedFrom?: Record<string, string[]>;
242
252
  /**
@@ -312,6 +322,8 @@ export interface BulkActionDef {
312
322
  type: string;
313
323
  /** Button label shown in the bulk bar. */
314
324
  label: string;
325
+ /** True when `label` came from the entry's custom `label` (the default confirm message then reads "<label>: <option> for N items?"). Only set then. */
326
+ customLabel?: boolean;
315
327
  /** Button style. Default: 'secondary'. Use 'danger' for destructive actions. */
316
328
  variant?: 'secondary' | 'danger';
317
329
  /** Values left out of this field's bulk select (from a `{ field, exclude }` bulkUpdateOptions entry). */
@@ -868,8 +880,12 @@ export declare function isoKey(p: {
868
880
  export declare function parseTime(value: string | null | undefined): number | null;
869
881
  /** "9:00 AM" / "12:30 PM" from an `HH:mm` value (returned unchanged when it cannot be parsed; '' for null/undefined/non-strings). */
870
882
  export declare function formatTime(value: string | null | undefined): string;
871
- export interface SelectOption {
872
- value: string;
883
+ /**
884
+ * One choice. `V` defaults to `string`, so existing `SelectOption[]` code is unchanged; `ui-select` itself accepts
885
+ * `SelectOption<string | number>[]` (numeric values for `valueType="number"`).
886
+ */
887
+ export interface SelectOption<V extends string | number = string> {
888
+ value: V;
873
889
  label: string;
874
890
  }
875
891
  export declare class SelectComponent implements ControlValueAccessor, Validator, UiFieldValidity {
@@ -877,7 +893,9 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
877
893
  /** Accessible name for a select with no visible label (e.g. an inline row or table-cell select). */
878
894
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
879
895
  readonly placeholder: import("@angular/core").InputSignal<string>;
880
- readonly options: import("@angular/core").InputSignal<SelectOption[]>;
896
+ readonly options: import("@angular/core").InputSignal<SelectOption<string | number>[]>;
897
+ /** What the control value is: `'string'` (default, numeric option values become strings) or `'number'` (`number | null`). */
898
+ readonly valueType: import("@angular/core").InputSignal<"string" | "number">;
881
899
  readonly required: import("@angular/core").InputSignal<boolean>;
882
900
  readonly hint: import("@angular/core").InputSignal<string>;
883
901
  readonly error: import("@angular/core").InputSignal<string>;
@@ -888,6 +906,8 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
888
906
  private readonly _cvaDisabled;
889
907
  protected readonly disabled: import("@angular/core").Signal<boolean>;
890
908
  protected readonly _value: import("@angular/core").WritableSignal<string>;
909
+ /** Index of the first option matching the written / chosen value, or -1 (so only ONE option is selected even with duplicate keys). */
910
+ protected readonly _selectedIndex: import("@angular/core").Signal<number>;
891
911
  private readonly _reveal;
892
912
  private readonly _issue;
893
913
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -905,12 +925,19 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
905
925
  registerOnValidatorChange(fn: () => void): void;
906
926
  protected readonly _selectId: string;
907
927
  private _onChange;
928
+ private _warned;
929
+ /** The comparison form of a key: in number mode a numeric text ('21.0', ' 21 ') becomes the canonical number text ('21'). */
930
+ private _norm;
931
+ protected _key(opt: SelectOption<string | number>): string;
932
+ private static _toKey;
908
933
  private _onTouched;
909
- writeValue(val: string): void;
910
- registerOnChange(fn: (v: string) => void): void;
934
+ writeValue(val: string | number | null | undefined): void;
935
+ registerOnChange(fn: (v: string | number | null) => void): void;
911
936
  registerOnTouched(fn: () => void): void;
912
937
  setDisabledState(d: boolean): void;
913
938
  protected onChange(event: Event): void;
939
+ /** The control value for the chosen native option value. */
940
+ private _emitted;
914
941
  protected onBlur(): void;
915
942
  private _check;
916
943
  static ɵfac: i0.ɵɵFactoryDeclaration<SelectComponent, never>;
@@ -935,6 +962,11 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
935
962
  "required": false;
936
963
  "isSignal": true;
937
964
  };
965
+ "valueType": {
966
+ "alias": "valueType";
967
+ "required": false;
968
+ "isSignal": true;
969
+ };
938
970
  "required": {
939
971
  "alias": "required";
940
972
  "required": false;
@@ -1290,7 +1322,7 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1290
1322
  /**
1291
1323
  * Derives internal action definitions from the `bulkUpdateOptions` string array.
1292
1324
  * 'delete' → built-in danger action with table-managed confirm dialog.
1293
- * Everything else → matched to a column: label = "Change {col.label}", options from column config.
1325
+ * Everything else → matched to a column: label = the entry's `label` when given, else "Change {col.label}"; options from column config.
1294
1326
  */
1295
1327
  protected readonly _bulkDefs: Signal<BulkActionDef[]>;
1296
1328
  /** Options for a field-based bulk action: the field's options minus the entry's `exclude` values. */
@@ -1788,14 +1820,33 @@ export declare class ModalComponent implements OnInit, OnDestroy {
1788
1820
  }, never, ["*", "[modal-footer-left]", "[modal-footer-right]"], true, never>;
1789
1821
  }
1790
1822
  export type ToastType = 'success' | 'error' | 'info';
1791
- export declare class ToastService {
1823
+ export declare class ToastService implements OnDestroy {
1792
1824
  readonly message: import("@angular/core").WritableSignal<string | null>;
1793
1825
  readonly type: import("@angular/core").WritableSignal<ToastType>;
1826
+ /**
1827
+ * Opt-in: every `<ui-toast>` shows a close button ("Dismiss notification"). Default `false`: toasts look exactly as before.
1828
+ * Set once per app with `provideForgerouteToast({ dismissible: true })`, or per outlet with `<ui-toast [dismissible]="true" />`
1829
+ * (the input wins). Escape, pause on hover / focus and `dismiss()` work either way.
1830
+ */
1831
+ readonly dismissible: import("@angular/core").WritableSignal<boolean>;
1794
1832
  private _timer;
1833
+ private _deadline;
1834
+ private _remaining;
1835
+ private _paused;
1795
1836
  show(msg: string, duration?: number): void;
1796
1837
  /** Neutral (blue) toast for a result that is neither a success nor a failure, e.g. "No changes to save.". */
1797
1838
  info(msg: string, duration?: number): void;
1798
1839
  error(msg: string, duration?: number): void;
1840
+ /** Removes the current toast now (close button / Escape). Safe when nothing is showing. */
1841
+ dismiss(): void;
1842
+ /** Holds the countdown while the pointer or keyboard focus is on the toast (WCAG 2.2.1). No-op when nothing is counting down. */
1843
+ pause(): void;
1844
+ /** Continues after `pause()`; the toast gets at least 1000ms more so it can be read. No-op when not paused. */
1845
+ resume(): void;
1846
+ /** The root injector is destroyed (app teardown, TestBed reset): no timer may outlive it. */
1847
+ ngOnDestroy(): void;
1848
+ private _clearTimer;
1849
+ private _start;
1799
1850
  private _display;
1800
1851
  static ɵfac: i0.ɵɵFactoryDeclaration<ToastService, never>;
1801
1852
  static ɵprov: i0.ɵɵInjectableDeclaration<any>;
@@ -1806,6 +1857,13 @@ export declare class ToastComponent {
1806
1857
  private readonly _id;
1807
1858
  /** True only for the earliest live instance, so exactly one toast (and one live region) renders. */
1808
1859
  protected readonly isHost: import("@angular/core").Signal<boolean>;
1860
+ /**
1861
+ * Opt-in close button. `null` (default) = follow `ToastService.dismissible` (itself `false` unless the app opted in with
1862
+ * `provideForgerouteToast({ dismissible: true })`); `true` / `false` forces it for this outlet. Without it the toast has no
1863
+ * control and looks exactly as it always did.
1864
+ */
1865
+ readonly dismissible: import("@angular/core").InputSignal<boolean | null>;
1866
+ protected readonly showClose: import("@angular/core").Signal<boolean>;
1809
1867
  /**
1810
1868
  * Message that was already on screen when this instance took over from a destroyed host. While it is
1811
1869
  * unchanged the toast renders without the entry animation and without live-region roles, so the
@@ -1814,9 +1872,24 @@ export declare class ToastComponent {
1814
1872
  private readonly _adoptedMessage;
1815
1873
  protected readonly adopted: import("@angular/core").Signal<boolean>;
1816
1874
  private _wasWaiting;
1875
+ private _hover;
1876
+ private _focus;
1877
+ /** Escape on a focused toast dismisses only the toast: a panel or dialog behind it that also listens for Escape stays open. */
1878
+ protected onEscape(e: Event): void;
1879
+ protected setHover(on: boolean): void;
1880
+ protected setFocus(on: boolean): void;
1881
+ protected onFocusOut(e: FocusEvent): void;
1882
+ /** The countdown holds while the pointer or focus is on the toast and continues when both have left. */
1883
+ private _syncPause;
1817
1884
  constructor();
1818
1885
  static ɵfac: i0.ɵɵFactoryDeclaration<ToastComponent, never>;
1819
- static ɵcmp: i0.ɵɵComponentDeclaration<ToastComponent, "ui-toast", never, {}, {}, never, never, true, never>;
1886
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToastComponent, "ui-toast", never, {
1887
+ "dismissible": {
1888
+ "alias": "dismissible";
1889
+ "required": false;
1890
+ "isSignal": true;
1891
+ };
1892
+ }, {}, never, never, true, never>;
1820
1893
  }
1821
1894
  export type ProgressBarVariant = 'blue' | 'green' | 'amber' | 'red' | 'slate';
1822
1895
  /**
@@ -1826,7 +1899,10 @@ export type ProgressBarVariant = 'blue' | 'green' | 'amber' | 'red' | 'slate';
1826
1899
  *
1827
1900
  * The fill color is built in and follows completion, in four bands: red up to 25%, amber up to 50%, blue up to 75%,
1828
1901
  * green above 75% (full = green). Nothing needs to be passed. `variant` is an optional override for a bar whose
1829
- * color means something else (for example an overdue status) — when it is set the color stays fixed.
1902
+ * color means something else (for example an overdue status, or a category-share bar) — when it is set the color stays fixed
1903
+ * at every value. For a share-of-largest bar pass `[value]="count" [max]="largestCount" variant="blue"`.
1904
+ * The width is set inline exactly as before (`style="width: N%;"` and nothing else, so existing consumers and e2e keep working); the transition is off for
1905
+ * `prefers-reduced-motion` and the bar stays visible in forced-colors mode. Invalid numbers (NaN, negative max) draw an empty bar.
1830
1906
  */
1831
1907
  export declare class ProgressBarComponent {
1832
1908
  /** Completed amount. */
@@ -1840,6 +1916,12 @@ export declare class ProgressBarComponent {
1840
1916
  /** Hover / focus tooltip (library `uiTooltip`); `\n` starts a new line. */
1841
1917
  readonly tooltip: import("@angular/core").InputSignal<string>;
1842
1918
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
1919
+ /** Id of an element that names the bar (wins over `ariaLabel` / `label`). */
1920
+ readonly ariaLabelledby: import("@angular/core").InputSignal<string>;
1921
+ /** Human text for `aria-valuetext`, e.g. "12 documents, 40% of the largest category". Empty = not set. */
1922
+ readonly valueText: import("@angular/core").InputSignal<string>;
1923
+ /** `max` made safe: NaN / negative / infinite become 0 (empty bar, no division by zero). */
1924
+ protected readonly safeMax: import("@angular/core").Signal<number>;
1843
1925
  protected readonly clampedValue: import("@angular/core").Signal<number>;
1844
1926
  /** Fill width in %: the rounded share, but never less than MIN_VISIBLE_PERCENT while the value is above 0 (1 of 300 would round to 0% and look empty). */
1845
1927
  protected readonly percent: import("@angular/core").Signal<number>;
@@ -1877,6 +1959,16 @@ export declare class ProgressBarComponent {
1877
1959
  "required": false;
1878
1960
  "isSignal": true;
1879
1961
  };
1962
+ "ariaLabelledby": {
1963
+ "alias": "ariaLabelledby";
1964
+ "required": false;
1965
+ "isSignal": true;
1966
+ };
1967
+ "valueText": {
1968
+ "alias": "valueText";
1969
+ "required": false;
1970
+ "isSignal": true;
1971
+ };
1880
1972
  }, {}, never, never, true, never>;
1881
1973
  }
1882
1974
  /**
@@ -2223,6 +2315,7 @@ export declare class ToggleComponent implements ControlValueAccessor {
2223
2315
  private readonly _cvaDisabled;
2224
2316
  protected readonly disabled: import("@angular/core").Signal<boolean>;
2225
2317
  private readonly _id;
2318
+ protected readonly _inputId: string;
2226
2319
  protected readonly _labelId: string;
2227
2320
  protected readonly _hintId: string;
2228
2321
  private _onChange;
@@ -2264,6 +2357,136 @@ export declare class ToggleComponent implements ControlValueAccessor {
2264
2357
  "checked": "checkedChange";
2265
2358
  }, never, never, true, never>;
2266
2359
  }
2360
+ /** One step of a `ui-stepper`. `key` must be unique within the list. */
2361
+ export interface StepperStep {
2362
+ key: string;
2363
+ label: string;
2364
+ /** Small grey text under the label. */
2365
+ description?: string;
2366
+ /** Shows "(optional)" after the label and announces "optional". */
2367
+ optional?: boolean;
2368
+ /** Never selectable and announced "disabled". Does not change the step's state (complete / current / upcoming). */
2369
+ disabled?: boolean;
2370
+ /** `true` or a message marks the step as failed ("!" indicator, red). A message string is shown under the label and announced. */
2371
+ error?: boolean | string;
2372
+ }
2373
+ export type StepperOrientation = 'horizontal' | 'vertical';
2374
+ /**
2375
+ * Step indicator for wizards, onboarding flows and progress timelines. The state of every step comes from the order of
2376
+ * `steps` and the key in `current`: steps before it are complete, it is current, steps after it are upcoming.
2377
+ *
2378
+ * <!-- Wizard header: completed steps are buttons, so the user can go back (never skip ahead) -->
2379
+ * <ui-stepper [steps]="steps" [clickable]="true" [(current)]="step" />
2380
+ *
2381
+ * <!-- Read-only progress indicator (steps are plain text; the host moves `current`) -->
2382
+ * <ui-stepper ariaLabel="Porting steps" size="sm" [steps]="steps" [current]="step()" />
2383
+ *
2384
+ * <!-- Vertical timeline with descriptions; free navigation to any enabled step -->
2385
+ * <ui-stepper orientation="vertical" [steps]="steps" [clickable]="true" [linear]="false" [(current)]="step" />
2386
+ *
2387
+ * <!-- Error state: `error: true` or a message on a step -->
2388
+ * steps = [{ key: 'info', label: 'Info' }, { key: 'pay', label: 'Payment', error: 'Card was declined' }, ...]
2389
+ *
2390
+ * `current` is two-way (`[(current)]`). An empty or unknown key means there is no current step: every step is upcoming
2391
+ * (and with `linear`, none is selectable). `error` changes the indicator, not the state or `aria-current`.
2392
+ *
2393
+ * Interaction (`clickable`): a selectable step is a native `<button>` (Tab / Enter / Space, visible focus ring). With
2394
+ * `linear` (default) only complete steps and the current step are selectable, so the user can go back but not skip
2395
+ * ahead; with `[linear]="false"` every non-disabled step is. Activating a selectable step other than the current one
2396
+ * sets `current` (so `[(current)]` follows) and emits `stepClick` with its key; activating the current step, or an
2397
+ * unselectable one, does nothing. Without `clickable` there are no buttons or tab stops at all.
2398
+ *
2399
+ * Accessibility: an ordered list named by `ariaLabel`; the current step has `aria-current="step"`. Each step's name is
2400
+ * "<label>, step N of M, completed | current step | not started" (+ ", optional", ", error: <message>", ", disabled"),
2401
+ * built from visually hidden text so it is announced in the list even when the step is not a button. Indicators and
2402
+ * connectors are decorative. A description is referenced with `aria-describedby`.
2403
+ *
2404
+ * Layout: `orientation` horizontal (row with connector lines that fill as steps complete) or vertical (column, connector
2405
+ * on the left). Long labels wrap. At <= 480px a horizontal stepper with `labelsOnSmallScreens="current"` (default) shows
2406
+ * only the current step's label (the others stay in the accessible name); `"all"` keeps them. Touch devices and phones
2407
+ * get >= 44px tall step buttons. The connector transition honours `prefers-reduced-motion`.
2408
+ */
2409
+ export declare class StepperComponent {
2410
+ /** The steps in order. Keys must be unique. */
2411
+ readonly steps: import("@angular/core").InputSignal<StepperStep[]>;
2412
+ /** Key of the current step. Two-way: `[(current)]`. Empty / unknown = no current step (all upcoming). */
2413
+ readonly current: import("@angular/core").ModelSignal<string>;
2414
+ readonly orientation: import("@angular/core").InputSignal<StepperOrientation>;
2415
+ /** `true`: selectable steps are buttons. `false` (default): purely indicative. */
2416
+ readonly clickable: import("@angular/core").InputSignal<boolean>;
2417
+ /** With `clickable`: `true` (default) = only complete steps and the current step are selectable; `false` = any enabled step. */
2418
+ readonly linear: import("@angular/core").InputSignal<boolean>;
2419
+ readonly size: import("@angular/core").InputSignal<"sm" | "md">;
2420
+ /** Accessible name of the list. */
2421
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
2422
+ /** Horizontal, <= 480px: show only the current step's label (`'current'`, default) or every label (`'all'`). */
2423
+ readonly labelsOnSmallScreens: import("@angular/core").InputSignal<"all" | "current">;
2424
+ /** Emits the key of an activated selectable step that was not already current (after `current` was updated). */
2425
+ readonly stepClick: import("@angular/core").OutputEmitterRef<string>;
2426
+ private readonly _id;
2427
+ protected readonly rows: import("@angular/core").Signal<{
2428
+ step: StepperStep;
2429
+ index: number;
2430
+ state: "complete" | "current" | "upcoming";
2431
+ error: boolean;
2432
+ errorMessage: string;
2433
+ selectable: boolean;
2434
+ srText: string;
2435
+ last: boolean;
2436
+ descId: string;
2437
+ }[]>;
2438
+ protected activate(r: {
2439
+ step: StepperStep;
2440
+ selectable: boolean;
2441
+ state: string;
2442
+ }): void;
2443
+ static ɵfac: i0.ɵɵFactoryDeclaration<StepperComponent, never>;
2444
+ static ɵcmp: i0.ɵɵComponentDeclaration<StepperComponent, "ui-stepper", never, {
2445
+ "steps": {
2446
+ "alias": "steps";
2447
+ "required": true;
2448
+ "isSignal": true;
2449
+ };
2450
+ "current": {
2451
+ "alias": "current";
2452
+ "required": false;
2453
+ "isSignal": true;
2454
+ };
2455
+ "orientation": {
2456
+ "alias": "orientation";
2457
+ "required": false;
2458
+ "isSignal": true;
2459
+ };
2460
+ "clickable": {
2461
+ "alias": "clickable";
2462
+ "required": false;
2463
+ "isSignal": true;
2464
+ };
2465
+ "linear": {
2466
+ "alias": "linear";
2467
+ "required": false;
2468
+ "isSignal": true;
2469
+ };
2470
+ "size": {
2471
+ "alias": "size";
2472
+ "required": false;
2473
+ "isSignal": true;
2474
+ };
2475
+ "ariaLabel": {
2476
+ "alias": "ariaLabel";
2477
+ "required": false;
2478
+ "isSignal": true;
2479
+ };
2480
+ "labelsOnSmallScreens": {
2481
+ "alias": "labelsOnSmallScreens";
2482
+ "required": false;
2483
+ "isSignal": true;
2484
+ };
2485
+ }, {
2486
+ "current": "currentChange";
2487
+ "stepClick": "stepClick";
2488
+ }, never, never, true, never>;
2489
+ }
2267
2490
  /** One weekday of the schedule. `open` / `close` are `HH:mm`; `open2` / `close2` are the optional second shift (split shift). */
2268
2491
  export interface BusinessHoursDay {
2269
2492
  /** 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun' */
@@ -2310,6 +2533,8 @@ export declare function businessTimeOptions(step?: BusinessHoursStep, hour24?: b
2310
2533
  * the opening time ("Monday: closing time must be after opening time."); with a second shift it must start after the first ends and end after it starts.
2311
2534
  * Overnight ranges are not supported (a single `open < close` window per shift), the same as call routing.
2312
2535
  *
2536
+ * Test ids: every row is `bh-day-<day>` (mon … sun); inside it `bh-enabled-<day>` (the open/closed checkbox), `bh-open-<day>`, `bh-close-<day>`, and with a second shift `bh-open2-<day>`, `bh-close2-<day>`; the buttons keep `bh-add-shift`, `bh-remove-shift` (scope them with the row) and `bh-copy-weekdays`.
2537
+ *
2313
2538
  * Accessibility: a `role="group"` named by `label`; every select has its own name ("Monday opens", "Monday closes", "Monday second shift opens" …);
2314
2539
  * errors are announced (`role="alert"`) and linked with `aria-describedby`.
2315
2540
  */
@@ -2458,7 +2683,7 @@ export declare class RadioGroupComponent implements ControlValueAccessor, Valida
2458
2683
  readonly error: import("@angular/core").InputSignal<string>;
2459
2684
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
2460
2685
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
2461
- readonly orientation: import("@angular/core").InputSignal<"vertical" | "horizontal">;
2686
+ readonly orientation: import("@angular/core").InputSignal<"horizontal" | "vertical">;
2462
2687
  /** Native `name` shared by the radios; a unique one is generated when omitted. */
2463
2688
  readonly name: import("@angular/core").InputSignal<string>;
2464
2689
  protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
@@ -2739,6 +2964,20 @@ export declare class ButtonComponent {
2739
2964
  readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
2740
2965
  /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
2741
2966
  readonly queryParams: import("@angular/core").InputSignal<Record<string, unknown> | null>;
2967
+ /**
2968
+ * Renders a plain `<a href>` instead of a button: for `mailto:`, `tel:`, `sms:`, `https://…` and relative URLs (use with
2969
+ * `variant="link"` for an inline text link). The browser handles the click, so middle-click, copy-link and open-in-new-tab work and
2970
+ * the host needs no click handler. Other schemes (`javascript:`, `data:`, …) are never rendered as a link: the component stays a
2971
+ * button. `routerLink` wins when both are set. While `disabled` or `loading` the link has no `href` (it only looks disabled).
2972
+ */
2973
+ readonly href: import("@angular/core").InputSignal<string | null>;
2974
+ /** Where to open the `href` (`_blank` = a new tab). */
2975
+ readonly target: import("@angular/core").InputSignal<"_blank" | "_self" | "_parent" | "_top" | null>;
2976
+ /** `rel` of the `href` link. A `_blank` target always gets `noopener noreferrer` added. */
2977
+ readonly rel: import("@angular/core").InputSignal<string | null>;
2978
+ /** The href when it is allowed (http, https, mailto, tel, sms or a relative URL), else null. */
2979
+ protected readonly _safeHref: import("@angular/core").Signal<string | null>;
2980
+ protected readonly _rel: import("@angular/core").Signal<string | null>;
2742
2981
  readonly color: import("@angular/core").InputSignal<string | undefined>;
2743
2982
  readonly active: import("@angular/core").InputSignal<boolean>;
2744
2983
  readonly size: import("@angular/core").InputSignal<"sm" | "md" | "xs" | "lg">;
@@ -2770,6 +3009,21 @@ export declare class ButtonComponent {
2770
3009
  "required": false;
2771
3010
  "isSignal": true;
2772
3011
  };
3012
+ "href": {
3013
+ "alias": "href";
3014
+ "required": false;
3015
+ "isSignal": true;
3016
+ };
3017
+ "target": {
3018
+ "alias": "target";
3019
+ "required": false;
3020
+ "isSignal": true;
3021
+ };
3022
+ "rel": {
3023
+ "alias": "rel";
3024
+ "required": false;
3025
+ "isSignal": true;
3026
+ };
2773
3027
  "color": {
2774
3028
  "alias": "color";
2775
3029
  "required": false;
@@ -3622,6 +3876,8 @@ export interface SignerDocLike {
3622
3876
  id: string;
3623
3877
  title: string;
3624
3878
  requiresCountersign?: boolean;
3879
+ /** The row says whether the countersign box is placed on the document. `false` (with `requiresCountersign`) offers the "Place your countersignature box" link; `undefined` = unknown = no link. */
3880
+ hasCountersignPlacement?: boolean;
3625
3881
  signerRoles?: SignerRole[];
3626
3882
  hasMapping?: boolean;
3627
3883
  hasDataFields?: boolean;
@@ -3643,6 +3899,14 @@ export interface DocumentSignerInfo {
3643
3899
  multiSigner: boolean;
3644
3900
  /** A document that is sent for signing requires the sender's countersignature. */
3645
3901
  countersign: boolean;
3902
+ /**
3903
+ * Only present (and non-empty) when a signable chosen document requires the countersignature and its row says the countersign box is NOT
3904
+ * placed (`hasCountersignPlacement === false`): the documents the "Place your countersignature box" link is offered for.
3905
+ */
3906
+ countersignUnplaced?: {
3907
+ id: string;
3908
+ title: string;
3909
+ }[];
3646
3910
  /** Several documents are chosen and one of them has several signers (it still goes to the one recipient). */
3647
3911
  multiDocumentNote: boolean;
3648
3912
  /** Under the document chip: what the mapping covers ('' when unknown). */
@@ -3686,6 +3950,8 @@ export declare function forwardContext(ctx: SendContext | null | undefined, type
3686
3950
  export declare function mergeFieldText(f: MergeCheckEmptyField): string;
3687
3951
  /** The warning lines of a merge-check answer: one line for one document, `<title>: …` per document for several. Nothing when nothing is empty. */
3688
3952
  export declare function mergeWarningLines(res: MergeCheckResult | null | undefined, docTitles: Record<string, string>): string[];
3953
+ /** The accessible name of the "Place your countersignature box" link of a document. */
3954
+ export declare function countersignPlaceLabel(title: string): string;
3689
3955
  /** What the sender wants done with the chosen documents. Same values as the backend (`ESIGN` | `ATTACH` | `BOTH`). */
3690
3956
  export type DocumentSendPurpose = 'ESIGN' | 'ATTACH' | 'BOTH';
3691
3957
  /** How the request reaches the recipient — derived from the channel: the Email tab sends `EMAIL`, the Text tab `TEXT`. */
@@ -3701,6 +3967,8 @@ export interface DocumentOption {
3701
3967
  hasFile: boolean;
3702
3968
  /** Only present (and true) when the row says the document needs the sender's countersignature (`requiresCountersign` on the `GET /documents` row): then a DocuSign send offers "Require my countersignature after client signs". */
3703
3969
  requiresCountersign?: boolean;
3970
+ /** Only with `documentSigners` and `requiresCountersign`: the row says whether the countersign box is placed. `false` shows the "Place your countersignature box" link after the countersign line (`mapFieldsClick`); absent = unknown = no link. */
3971
+ hasCountersignPlacement?: boolean;
3704
3972
  /** Only with `documentSigners`: the document's customer roles from the `GET /documents` row (`signerRoles`). `[]` = no signature fields (informational). */
3705
3973
  signerRoles?: SignerRole[];
3706
3974
  /** The row says the document has saved placements of any kind (drives the "Fields placed for N signers" line and the merge check). */
@@ -3717,6 +3985,7 @@ export interface DocumentSeed {
3717
3985
  fileName?: string | null;
3718
3986
  hasFile?: boolean;
3719
3987
  requiresCountersign?: boolean;
3988
+ hasCountersignPlacement?: boolean;
3720
3989
  signerRoles?: SignerRole[];
3721
3990
  hasMapping?: boolean;
3722
3991
  hasDataFields?: boolean;
@@ -3906,7 +4175,7 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
3906
4175
  readonly envelope: import("@angular/core").InputSignal<DocumentEnvelopeTarget | null>;
3907
4176
  /** The document list is fixed: an existing envelope's. */
3908
4177
  readonly locked: import("@angular/core").Signal<boolean>;
3909
- protected readonly purposeOptions: import("@angular/core").Signal<SelectOption[]>;
4178
+ protected readonly purposeOptions: import("@angular/core").Signal<SelectOption<string>[]>;
3910
4179
  readonly search$: Subject<string>;
3911
4180
  readonly options: import("@angular/core").WritableSignal<ComboboxOption<DocumentOption>[]>;
3912
4181
  readonly loading: import("@angular/core").WritableSignal<boolean>;
@@ -4344,6 +4613,11 @@ declare class SendEmailComponent implements OnInit {
4344
4613
  readonly validChange: import("@angular/core").OutputEmitterRef<boolean>;
4345
4614
  /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */
4346
4615
  readonly rejected: import("@angular/core").OutputEmitterRef<void>;
4616
+ /** The "Place your countersignature box" link under the countersign line was clicked (document row `hasCountersignPlacement: false`). */
4617
+ readonly mapFieldsClick: import("@angular/core").OutputEmitterRef<{
4618
+ id: string;
4619
+ title: string;
4620
+ }>;
4347
4621
  readonly EMAIL_DISABLED_MESSAGE = "Email sending is disabled, so this can't be sent. Enable it in Communication Settings.";
4348
4622
  mode: 'compose' | 'template';
4349
4623
  subject: string;
@@ -4650,6 +4924,7 @@ declare class SendEmailComponent implements OnInit {
4650
4924
  "emailAdded": "emailAdded";
4651
4925
  "validChange": "validChange";
4652
4926
  "rejected": "rejected";
4927
+ "mapFieldsClick": "mapFieldsClick";
4653
4928
  }, never, never, true, never>;
4654
4929
  }
4655
4930
  /** What the text form sends — the default `POST <baseUrl>/text-messages` body, or the host's `sendFn` argument. */
@@ -5093,6 +5368,15 @@ export declare class SendMessagePanelComponent {
5093
5368
  readonly emailAdded: import("@angular/core").OutputEmitterRef<string>;
5094
5369
  /** A document / envelope send was refused because it changed meanwhile (404 / 409): the host refreshes its list. */
5095
5370
  readonly rejected: import("@angular/core").OutputEmitterRef<void>;
5371
+ /**
5372
+ * With `documentSigners` on: the user clicked "Place your countersignature box" under the countersign line of a document that requires a
5373
+ * countersignature but whose row says `hasCountersignPlacement: false` (docs/send-documents-contract.md, Addendum 2026-10-19 (b)).
5374
+ * Payload = that document `{ id, title }`; the host navigates to Map Data Fields. Never emitted when the flag is true / absent.
5375
+ */
5376
+ readonly mapFieldsClick: import("@angular/core").OutputEmitterRef<{
5377
+ id: string;
5378
+ title: string;
5379
+ }>;
5096
5380
  readonly phoneAdded: import("@angular/core").OutputEmitterRef<string>;
5097
5381
  readonly sendEmailRef: import("@angular/core").Signal<SendEmailComponent | undefined>;
5098
5382
  readonly sendTextRef: import("@angular/core").Signal<SendTextComponent | undefined>;
@@ -5314,6 +5598,7 @@ export declare class SendMessagePanelComponent {
5314
5598
  "sent": "sent";
5315
5599
  "emailAdded": "emailAdded";
5316
5600
  "rejected": "rejected";
5601
+ "mapFieldsClick": "mapFieldsClick";
5317
5602
  "phoneAdded": "phoneAdded";
5318
5603
  }, never, never, true, never>;
5319
5604
  }
@@ -5340,14 +5625,27 @@ export declare class DocumentSignersComponent implements UiFieldValidity {
5340
5625
  /** The complete signer list (recipient first) or null while a role has no contact. Emitted on every change. */
5341
5626
  readonly signersChange: import("@angular/core").OutputEmitterRef<SendSigner[] | null>;
5342
5627
  readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
5628
+ /** The "Place your countersignature box" link was clicked: the document whose countersign box is not placed (the host opens Map Data Fields). */
5629
+ readonly mapFieldsClick: import("@angular/core").OutputEmitterRef<{
5630
+ id: string;
5631
+ title: string;
5632
+ }>;
5343
5633
  readonly picks: import("@angular/core").WritableSignal<Partial<Record<SignerRole, ContactLookup | null>>>;
5344
5634
  protected readonly rowsShown: import("@angular/core").Signal<boolean>;
5345
5635
  protected readonly line: import("@angular/core").Signal<string>;
5636
+ /** Documents the "place your box" link is offered for: native send only (DocuSign places its own fields) and only while the countersign line shows. */
5637
+ protected readonly placeDocs: import("@angular/core").Signal<{
5638
+ id: string;
5639
+ title: string;
5640
+ }[]>;
5346
5641
  readonly problems: import("@angular/core").Signal<Partial<Record<SignerRole, string>>>;
5347
5642
  readonly signers: import("@angular/core").Signal<SendSigner[] | null>;
5348
5643
  readonly errorMessage: import("@angular/core").Signal<string>;
5349
5644
  readonly invalid: import("@angular/core").Signal<boolean>;
5350
5645
  constructor();
5646
+ protected placeLabel(title: string): string;
5647
+ /** Visible text: with several documents the title is part of it (label-in-name: it contains the start of the accessible name). */
5648
+ protected placeText(title: string): string;
5351
5649
  protected label(role: SignerRole): string;
5352
5650
  /** Under a picker: the problems of a chosen contact (no email / same person twice); the empty-row message is the picker's own (`visibleErrorMessage`). */
5353
5651
  protected visibleProblem(role: SignerRole): string;
@@ -5382,6 +5680,7 @@ export declare class DocumentSignersComponent implements UiFieldValidity {
5382
5680
  }, {
5383
5681
  "signersChange": "signersChange";
5384
5682
  "invalidChange": "invalidChange";
5683
+ "mapFieldsClick": "mapFieldsClick";
5385
5684
  }, never, never, true, never>;
5386
5685
  }
5387
5686
  /** Quiet time after the last change before the check is requested. */
@@ -5604,10 +5903,16 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5604
5903
  /** Accessible name for a field with no visible label (e.g. an inline cell or title input). */
5605
5904
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
5606
5905
  readonly placeholder: import("@angular/core").InputSignal<string>;
5607
- readonly type: import("@angular/core").InputSignal<"number" | "text" | "email" | "password" | "tel" | "url">;
5906
+ readonly type: import("@angular/core").InputSignal<"number" | "text" | "email" | "tel" | "password" | "url">;
5608
5907
  readonly maxlength: import("@angular/core").InputSignal<number | undefined>;
5609
5908
  readonly required: import("@angular/core").InputSignal<boolean>;
5610
5909
  readonly autocomplete: import("@angular/core").InputSignal<string>;
5910
+ /**
5911
+ * Which on-screen keyboard a phone shows (`numeric` = digits only, e.g. ZIP / area code; `decimal`, `tel`, `email`, `url`,
5912
+ * `search`, `text`, `none`). Empty (default) = the browser's default for the field, except `type="tel"`, which keeps `tel`.
5913
+ * It only picks the keyboard; it does not restrict what can be typed or pasted.
5914
+ */
5915
+ readonly inputmode: import("@angular/core").InputSignal<"" | "none" | "text" | "search" | "email" | "tel" | "url" | "decimal" | "numeric">;
5611
5916
  readonly readonly: import("@angular/core").InputSignal<boolean>;
5612
5917
  readonly hint: import("@angular/core").InputSignal<string>;
5613
5918
  readonly error: import("@angular/core").InputSignal<string>;
@@ -5619,6 +5924,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5619
5924
  readonly max: import("@angular/core").InputSignal<number | undefined>;
5620
5925
  /** `type="number"` only: most digits allowed after the decimal point. */
5621
5926
  readonly maxDecimals: import("@angular/core").InputSignal<number | undefined>;
5927
+ /**
5928
+ * What the control value is. `'string'` (default) = the typed text. `'number'` = `number | null`; requires `type="number"`
5929
+ * (otherwise a dev-mode warning is logged once and string behaviour is used).
5930
+ */
5931
+ readonly valueType: import("@angular/core").InputSignal<"string" | "number">;
5622
5932
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
5623
5933
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
5624
5934
  readonly focused: import("@angular/core").OutputEmitterRef<void>;
@@ -5630,6 +5940,9 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5630
5940
  protected readonly disabled: import("@angular/core").Signal<boolean>;
5631
5941
  protected readonly _value: import("@angular/core").WritableSignal<string>;
5632
5942
  private readonly _reveal;
5943
+ /** True when the control value is a number: `valueType="number"` on a `type="number"` field. */
5944
+ private readonly _numeric;
5945
+ private _warned;
5633
5946
  private readonly _formatting;
5634
5947
  private readonly _issue;
5635
5948
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -5645,13 +5958,15 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5645
5958
  constructor();
5646
5959
  /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */
5647
5960
  ngOnChanges(): void;
5648
- writeValue(val: string | number | null): void;
5961
+ writeValue(val: string | number | null | undefined): void;
5649
5962
  validate(control: AbstractControl): ValidationErrors | null;
5650
5963
  registerOnValidatorChange(fn: () => void): void;
5651
- registerOnChange(fn: (v: string) => void): void;
5964
+ registerOnChange(fn: (v: string | number | null) => void): void;
5652
5965
  registerOnTouched(fn: () => void): void;
5653
5966
  setDisabledState(d: boolean): void;
5654
5967
  protected onInput(event: Event): void;
5968
+ /** Typed text -> number, or null when blank / not a finite number. */
5969
+ private static _parse;
5655
5970
  protected onFocus(): void;
5656
5971
  protected onBlur(): void;
5657
5972
  /** First broken built-in rule for `value`, or null. */
@@ -5696,6 +6011,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5696
6011
  "required": false;
5697
6012
  "isSignal": true;
5698
6013
  };
6014
+ "inputmode": {
6015
+ "alias": "inputmode";
6016
+ "required": false;
6017
+ "isSignal": true;
6018
+ };
5699
6019
  "readonly": {
5700
6020
  "alias": "readonly";
5701
6021
  "required": false;
@@ -5731,6 +6051,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5731
6051
  "required": false;
5732
6052
  "isSignal": true;
5733
6053
  };
6054
+ "valueType": {
6055
+ "alias": "valueType";
6056
+ "required": false;
6057
+ "isSignal": true;
6058
+ };
5734
6059
  "showErrors": {
5735
6060
  "alias": "showErrors";
5736
6061
  "required": false;
@@ -5757,6 +6082,12 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5757
6082
  readonly required: import("@angular/core").InputSignal<boolean>;
5758
6083
  readonly readonly: import("@angular/core").InputSignal<boolean>;
5759
6084
  readonly hint: import("@angular/core").InputSignal<string>;
6085
+ /**
6086
+ * Opt-in. `false` (default) = unchanged: a labelled field's hint renders INSIDE the `<label>` ("Label (hint)" is the accessible name,
6087
+ * no aria-describedby for it). `true` = the hint is a sibling of the label (`.ta-head` row, id `<textarea id>-hint`), the accessible
6088
+ * name is the label only and the hint is the description (`aria-describedby`, then the error). See docs/textarea-input.md.
6089
+ */
6090
+ readonly hintAsDescription: import("@angular/core").InputSignal<boolean>;
5760
6091
  readonly error: import("@angular/core").InputSignal<string>;
5761
6092
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
5762
6093
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
@@ -5766,6 +6097,11 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5766
6097
  protected readonly _value: import("@angular/core").WritableSignal<string>;
5767
6098
  /** The hint is shown inline after the label; with no label there is nothing to hang it on, so it renders under the field. */
5768
6099
  protected readonly _unlabelledHint: import("@angular/core").Signal<boolean>;
6100
+ /**
6101
+ * aria-describedby: the hint (inline after the label, or under the field when there is no label; the under-field
6102
+ * hint is replaced by the error) followed by the error. The label is the accessible NAME; the hint is the DESCRIPTION.
6103
+ */
6104
+ protected readonly _describedBy: import("@angular/core").Signal<string | null>;
5769
6105
  private readonly _reveal;
5770
6106
  private readonly _issue;
5771
6107
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -5833,6 +6169,11 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5833
6169
  "required": false;
5834
6170
  "isSignal": true;
5835
6171
  };
6172
+ "hintAsDescription": {
6173
+ "alias": "hintAsDescription";
6174
+ "required": false;
6175
+ "isSignal": true;
6176
+ };
5836
6177
  "error": {
5837
6178
  "alias": "error";
5838
6179
  "required": false;
@@ -5853,7 +6194,8 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5853
6194
  }, never, never, true, never>;
5854
6195
  }
5855
6196
  /** Why a chosen file was refused. */
5856
- export type FileRejectReason = 'type' | 'size';
6197
+ /** 'type' = not an accepted type, 'size' = over `maxSize`, 'empty' = a 0-byte file (`rejectEmpty`), 'small' = under `minSize`. */
6198
+ export type FileRejectReason = 'type' | 'size' | 'empty' | 'small';
5857
6199
  /** A refused file with the message the field shows (also emitted by `(fileRejected)`). */
5858
6200
  export interface FileRejection {
5859
6201
  file: File;
@@ -5876,7 +6218,7 @@ export declare function acceptLabel(accept: string): string;
5876
6218
  * Built in (all opt-in, nothing changes unless an input is set):
5877
6219
  * - `clearable`: a Remove button next to the chosen file; `(fileRemoved)` fires after it clears the selection.
5878
6220
  * - `accept` is enforced on picked AND dropped files (`enforceAccept`, on by default), `maxSize` (bytes) refuses bigger
5879
- * files. A refused file is never added to the value: the field shows the message (`typeMessage` / `sizeMessage`
6221
+ * files, `rejectEmpty` refuses a 0-byte file (`emptyMessage`) and `minSize` (bytes) refuses smaller ones (`minSizeMessage`). A refused file is never added to the value: the field shows the message (`typeMessage` / `sizeMessage`
5880
6222
  * override the wording, also in picker-only mode), and `(fileRejected)` emits `{ file, reason, message }`.
5881
6223
  * - `required` is a real rule: Angular `Validator` + `UiFieldValidity` (`invalid`, `errorMessage`, `invalidChange`,
5882
6224
  * `showErrors`), like the other inputs. The message shows after the field was used or `[showErrors]="true"`.
@@ -5913,6 +6255,14 @@ export declare class FileInputComponent implements ControlValueAccessor, Validat
5913
6255
  readonly typeMessage: import("@angular/core").InputSignal<string>;
5914
6256
  /** Replaces the built-in "The file must be 5 MB or smaller." message. */
5915
6257
  readonly sizeMessage: import("@angular/core").InputSignal<string>;
6258
+ /** Refuse a file with no content (0 bytes). Off by default. */
6259
+ readonly rejectEmpty: import("@angular/core").InputSignal<boolean>;
6260
+ /** Replaces the built-in "The file is empty." message. */
6261
+ readonly emptyMessage: import("@angular/core").InputSignal<string>;
6262
+ /** Smallest allowed file in bytes; a smaller (non-empty) file is refused. null = no minimum. */
6263
+ readonly minSize: import("@angular/core").InputSignal<number | null>;
6264
+ /** Replaces the built-in "The file must be at least 10 KB." message. */
6265
+ readonly minSizeMessage: import("@angular/core").InputSignal<string>;
5916
6266
  /** The field accepts files dropped on it. */
5917
6267
  readonly dropzone: import("@angular/core").InputSignal<boolean>;
5918
6268
  /** Show the file size after the name. */
@@ -6066,6 +6416,26 @@ export declare class FileInputComponent implements ControlValueAccessor, Validat
6066
6416
  "required": false;
6067
6417
  "isSignal": true;
6068
6418
  };
6419
+ "rejectEmpty": {
6420
+ "alias": "rejectEmpty";
6421
+ "required": false;
6422
+ "isSignal": true;
6423
+ };
6424
+ "emptyMessage": {
6425
+ "alias": "emptyMessage";
6426
+ "required": false;
6427
+ "isSignal": true;
6428
+ };
6429
+ "minSize": {
6430
+ "alias": "minSize";
6431
+ "required": false;
6432
+ "isSignal": true;
6433
+ };
6434
+ "minSizeMessage": {
6435
+ "alias": "minSizeMessage";
6436
+ "required": false;
6437
+ "isSignal": true;
6438
+ };
6069
6439
  "dropzone": {
6070
6440
  "alias": "dropzone";
6071
6441
  "required": false;
@@ -6372,7 +6742,7 @@ export declare class TimeInputComponent implements ControlValueAccessor, Validat
6372
6742
  export declare class MultiSelectComponent implements ControlValueAccessor {
6373
6743
  readonly label: import("@angular/core").InputSignal<string>;
6374
6744
  readonly placeholder: import("@angular/core").InputSignal<string>;
6375
- readonly options: import("@angular/core").InputSignal<SelectOption[]>;
6745
+ readonly options: import("@angular/core").InputSignal<SelectOption<string>[]>;
6376
6746
  readonly searchable: import("@angular/core").InputSignal<boolean>;
6377
6747
  readonly searchPlaceholder: import("@angular/core").InputSignal<string>;
6378
6748
  readonly required: import("@angular/core").InputSignal<boolean>;
@@ -6388,8 +6758,8 @@ export declare class MultiSelectComponent implements ControlValueAccessor {
6388
6758
  protected readonly _open: import("@angular/core").WritableSignal<boolean>;
6389
6759
  protected readonly _query: import("@angular/core").WritableSignal<string>;
6390
6760
  protected readonly _selectedSet: import("@angular/core").Signal<Set<string>>;
6391
- protected readonly _selectedOptions: import("@angular/core").Signal<SelectOption[]>;
6392
- protected readonly _filteredOptions: import("@angular/core").Signal<SelectOption[]>;
6761
+ protected readonly _selectedOptions: import("@angular/core").Signal<SelectOption<string>[]>;
6762
+ protected readonly _filteredOptions: import("@angular/core").Signal<SelectOption<string>[]>;
6393
6763
  private _onChange;
6394
6764
  private _onTouched;
6395
6765
  writeValue(val: string[] | null): void;
@@ -6607,7 +6977,8 @@ export declare class FormRowComponent {
6607
6977
  /**
6608
6978
  * Fixed-layout list item with an optional leading radio/checkbox, a title + badge,
6609
6979
  * and a secondary detail row rendered from a string array. All layout is handled
6610
- * internally — pass inputs only, no ng-content.
6980
+ * internally — pass inputs only; the one optional projection is the row-end action slot
6981
+ * (`list-item-action`, alias `li-action`, see below).
6611
6982
  *
6612
6983
  * Usage:
6613
6984
  * <ui-list-item
@@ -6621,6 +6992,16 @@ export declare class FormRowComponent {
6621
6992
  * badgeVariant="green"
6622
6993
  * [details]="[c.email, c.phone, 'Added ' + (c.createdAt | date:'MMM d, y')]"
6623
6994
  * (select)="onSelect(c.id)" />
6995
+ *
6996
+ * Read-only row with actions (2026-10-19): `[interactive]="false"` renders a plain row (no label, no leading
6997
+ * input, no pointer cursor, no hover tint, nothing focusable) and `[alwaysShowBadge]="true"` shows the badge
6998
+ * without needing `checked`. Elements carrying `list-item-action` (alias `li-action`) are projected at the
6999
+ * row end, vertically centred, after the text; they sit outside the row's label so clicking them never emits
7000
+ * `select`. Absent = no extra DOM. Example:
7001
+ * <ui-list-item [interactive]="false" [bordered]="true" [title]="d.name" [details]="[d.fileName]"
7002
+ * badge="Archived" badgeVariant="gray" [alwaysShowBadge]="true">
7003
+ * <ui-button list-item-action variant="icon" ariaLabel="Remove" (click)="remove(d)">…</ui-button>
7004
+ * </ui-list-item>
6624
7005
  */
6625
7006
  export declare class ListItemComponent {
6626
7007
  /** Show a radio button, checkbox, or nothing on the left. */
@@ -6635,10 +7016,24 @@ export declare class ListItemComponent {
6635
7016
  readonly highlighted: import("@angular/core").InputSignal<boolean>;
6636
7017
  /** Wraps the item in a bordered card (border + border-radius). */
6637
7018
  readonly bordered: import("@angular/core").InputSignal<boolean>;
7019
+ /**
7020
+ * `true` (default) = today's behaviour: clickable label, optional radio/checkbox, hover tint, pointer cursor.
7021
+ * `false` = plain row: no label, no leading input (`leading` is ignored), no hover tint, no pointer, `select` never
7022
+ * fires. Title, details, badge, `bordered` and `highlighted` look the same. Only projected actions stay focusable.
7023
+ */
7024
+ readonly interactive: import("@angular/core").InputSignal<boolean>;
7025
+ /** Show the badge even when the row is not `checked` (for non-interactive rows with a status badge). Default false. */
7026
+ readonly alwaysShowBadge: import("@angular/core").InputSignal<boolean>;
7027
+ /**
7028
+ * Break long unbroken title / detail text (file names, e-mails) inside the text column instead of letting it overflow; use it
7029
+ * on a row with `list-item-action` projections so the actions stay on screen at 320px. Default `false` = today's rendering.
7030
+ * Rows with `[interactive]="false"` always wrap (new in 2026-10-19, nothing to preserve).
7031
+ */
7032
+ readonly wrapText: import("@angular/core").InputSignal<boolean>;
6638
7033
  get _borderedClass(): boolean;
6639
7034
  /** Primary label. */
6640
7035
  readonly title: import("@angular/core").InputSignal<string>;
6641
- /** Badge shown next to the title — only visible when the item is checked. */
7036
+ /** Badge shown next to the title — only visible when the item is checked, unless `alwaysShowBadge` is set. */
6642
7037
  readonly badge: import("@angular/core").InputSignal<string | null>;
6643
7038
  readonly badgeVariant: import("@angular/core").InputSignal<BadgeVariant>;
6644
7039
  /**
@@ -6682,6 +7077,21 @@ export declare class ListItemComponent {
6682
7077
  "required": false;
6683
7078
  "isSignal": true;
6684
7079
  };
7080
+ "interactive": {
7081
+ "alias": "interactive";
7082
+ "required": false;
7083
+ "isSignal": true;
7084
+ };
7085
+ "alwaysShowBadge": {
7086
+ "alias": "alwaysShowBadge";
7087
+ "required": false;
7088
+ "isSignal": true;
7089
+ };
7090
+ "wrapText": {
7091
+ "alias": "wrapText";
7092
+ "required": false;
7093
+ "isSignal": true;
7094
+ };
6685
7095
  "title": {
6686
7096
  "alias": "title";
6687
7097
  "required": false;
@@ -6704,7 +7114,227 @@ export declare class ListItemComponent {
6704
7114
  };
6705
7115
  }, {
6706
7116
  "select": "select";
6707
- }, never, never, true, never>;
7117
+ }, never, ["[list-item-action], [li-action]"], true, never>;
7118
+ }
7119
+ /**
7120
+ * Adds one app-wide toast outlet (a `<ui-toast>` appended to `<body>`) when the app starts, so toasts show on pages that have
7121
+ * no `<ui-data-table>` and no `<ui-toast />` of their own.
7122
+ *
7123
+ * providers: [provideForgerouteToast()]
7124
+ * providers: [provideForgerouteToast({ dismissible: true })] // every toast also gets a close button (default: none)
7125
+ *
7126
+ * It is created first, so it is the registry's host and any other `<ui-toast>` (a table's own, a shell's) stays silent: each
7127
+ * message renders and is announced once. Nothing happens on the server. The outlet is destroyed with the app.
7128
+ */
7129
+ export declare function provideForgerouteToast(options?: {
7130
+ dismissible?: boolean;
7131
+ }): EnvironmentProviders;
7132
+ /** Payload of `(reorder)`. `order[newIndex] = oldIndex`; apply it with `reorderItems(items, order)`. */
7133
+ export interface ListReorderEvent {
7134
+ /** Index the moved row had before the move. */
7135
+ from: number;
7136
+ /** Index the moved row has after the move. */
7137
+ to: number;
7138
+ /** The new index order of the whole list: `order[newIndex] = oldIndex`. */
7139
+ order: number[];
7140
+ }
7141
+ /** Context handed to the row template: `<ng-template let-item let-i="index" let-n="count">`. */
7142
+ export interface ListRowContext<T = unknown> {
7143
+ $implicit: T;
7144
+ index: number;
7145
+ count: number;
7146
+ }
7147
+ /** Returns a new array in the order given by a `ListReorderEvent.order` (the input array is not changed). */
7148
+ export declare function reorderItems<T>(items: readonly T[], order: readonly number[]): T[];
7149
+ interface Row {
7150
+ item: unknown;
7151
+ key: unknown;
7152
+ index: number;
7153
+ label: string;
7154
+ upLabel: string;
7155
+ downLabel: string;
7156
+ first: boolean;
7157
+ last: boolean;
7158
+ ctx: ListRowContext;
7159
+ }
7160
+ /**
7161
+ * Semantic list container. Renders a real `<ul>` (default) or `<ol>` (`[ordered]="true"`) whose children are real `<li>`
7162
+ * elements, one per entry of `items`. The row template (the single `<ng-template>` child) renders what is inside each
7163
+ * `<li>`, normally a `<ui-list-item>`. Screen readers announce "list, N items" and "item i of N" natively.
7164
+ *
7165
+ * Optional reordering (`[reorderable]="true"`, off by default): Move up / Move down icon buttons at the row end,
7166
+ * Alt+ArrowUp / Alt+ArrowDown from any focused element in the row, a polite live region and a `(reorder)` output.
7167
+ * The host owns the data: the list never changes `items`; apply the event (`reorderItems`) and pass the new array.
7168
+ *
7169
+ * <ui-list ariaLabel="Documents" [items]="docs()" [reorderable]="true" (reorder)="docs.set(reorderItems(docs(), $event.order))">
7170
+ * <ng-template let-doc let-i="index">
7171
+ * <ui-list-item [interactive]="false" [bordered]="true" [title]="doc.title" [details]="[doc.fileName]" />
7172
+ * </ng-template>
7173
+ * </ui-list>
7174
+ */
7175
+ export declare class ListComponent<T = unknown> {
7176
+ private readonly host;
7177
+ private readonly injector;
7178
+ /** The entries to render, one `<li>` each. The host owns this array; the list never changes it. */
7179
+ readonly items: import("@angular/core").InputSignal<readonly T[]>;
7180
+ /** `false` (default) renders a `<ul>`; `true` renders an `<ol>` with native numbering. */
7181
+ readonly ordered: import("@angular/core").InputSignal<boolean>;
7182
+ /** Shows Move up / Move down controls and enables Alt+ArrowUp / Alt+ArrowDown. Default `false`: no controls, no live region. */
7183
+ readonly reorderable: import("@angular/core").InputSignal<boolean>;
7184
+ /** Disables all move controls and the keyboard shortcut (the rows stay visible). */
7185
+ readonly disabled: import("@angular/core").InputSignal<boolean>;
7186
+ /** Accessible name of the list (`aria-label`). */
7187
+ readonly ariaLabel: import("@angular/core").InputSignal<string | undefined>;
7188
+ /** Id of the element naming the list (`aria-labelledby`). */
7189
+ readonly ariaLabelledby: import("@angular/core").InputSignal<string | undefined>;
7190
+ /**
7191
+ * Row label used in "Move <label> up" and the live announcement. Default: the string itself, else the item's
7192
+ * `title`, `label` or `name`, else "Item N".
7193
+ */
7194
+ readonly itemLabelFn: import("@angular/core").InputSignal<(item: T, index: number) => string>;
7195
+ /** Stable identity per row (default: the item object itself) so reordering moves DOM instead of recreating it. */
7196
+ readonly trackBy: import("@angular/core").InputSignal<(item: T, index: number) => unknown>;
7197
+ /** Fires when the user moves a row. The host applies it and passes the new `items`. */
7198
+ readonly reorder: import("@angular/core").OutputEmitterRef<ListReorderEvent>;
7199
+ protected readonly rowTemplate: import("@angular/core").Signal<TemplateRef<any> | undefined>;
7200
+ protected readonly announcement: import("@angular/core").WritableSignal<string>;
7201
+ protected readonly movedKey: import("@angular/core").WritableSignal<unknown>;
7202
+ private pendingFocus;
7203
+ private announceSeq;
7204
+ /** Stable disambiguation tokens for repeated keys (duplicates in a primitive list), reused across recomputes. */
7205
+ private dupTokens;
7206
+ protected readonly rows: import("@angular/core").Signal<Row[]>;
7207
+ private canReorder;
7208
+ /** The `<li>` (direct child of this list) that contains `el`, or null. */
7209
+ private rowIndexOf;
7210
+ protected onKeydown(e: KeyboardEvent): void;
7211
+ protected onClick(e: Event): void;
7212
+ protected onFocusOut(e: FocusEvent): void;
7213
+ /** Emits the reorder, announces it and restores focus to the moved row's control once the host has re-rendered. */
7214
+ private move;
7215
+ /** Identical text is not re-read by screen readers, so a repeat of the current message is cleared first and written after the next render. */
7216
+ private announce;
7217
+ private restoreFocus;
7218
+ static ɵfac: i0.ɵɵFactoryDeclaration<ListComponent<any>, never>;
7219
+ static ɵcmp: i0.ɵɵComponentDeclaration<ListComponent<any>, "ui-list", never, {
7220
+ "items": {
7221
+ "alias": "items";
7222
+ "required": false;
7223
+ "isSignal": true;
7224
+ };
7225
+ "ordered": {
7226
+ "alias": "ordered";
7227
+ "required": false;
7228
+ "isSignal": true;
7229
+ };
7230
+ "reorderable": {
7231
+ "alias": "reorderable";
7232
+ "required": false;
7233
+ "isSignal": true;
7234
+ };
7235
+ "disabled": {
7236
+ "alias": "disabled";
7237
+ "required": false;
7238
+ "isSignal": true;
7239
+ };
7240
+ "ariaLabel": {
7241
+ "alias": "ariaLabel";
7242
+ "required": false;
7243
+ "isSignal": true;
7244
+ };
7245
+ "ariaLabelledby": {
7246
+ "alias": "ariaLabelledby";
7247
+ "required": false;
7248
+ "isSignal": true;
7249
+ };
7250
+ "itemLabelFn": {
7251
+ "alias": "itemLabelFn";
7252
+ "required": false;
7253
+ "isSignal": true;
7254
+ };
7255
+ "trackBy": {
7256
+ "alias": "trackBy";
7257
+ "required": false;
7258
+ "isSignal": true;
7259
+ };
7260
+ }, {
7261
+ "reorder": "reorder";
7262
+ }, ["rowTemplate"], ["[list-empty]"], true, never>;
7263
+ }
7264
+ /**
7265
+ * One labelled bar row: label | bar | count (and an optional share text under the count).
7266
+ * Use it for category / share charts: `value` is the row's amount, `max` the largest amount (the bar is value / max),
7267
+ * `variant` a fixed colour (default blue, never completion based).
7268
+ *
7269
+ * The row has no list role of its own: put `role="list"` on the host container and `role="listitem"` on each `ui-bar-row`
7270
+ * (or wrap them in `ui-list`). The label names the inner progressbar (`aria-labelledby`) and `valueText`
7271
+ * (default: the count plus the share) is its `aria-valuetext`.
7272
+ * Layout: `minmax(0, 12rem) 1fr auto`; at 480px and below the label sits above the bar with the count at the end of the label line.
7273
+ */
7274
+ export declare class BarRowComponent {
7275
+ /** Row label (wraps; long unbroken text breaks instead of overflowing). */
7276
+ readonly label: import("@angular/core").InputSignal<string>;
7277
+ /** This row's amount. */
7278
+ readonly value: import("@angular/core").InputSignal<number>;
7279
+ /** The amount that fills the bar (e.g. the largest category's count). 0 / NaN / negative draws an empty bar. */
7280
+ readonly max: import("@angular/core").InputSignal<number>;
7281
+ /** Fixed bar colour. Default blue; never follows completion. */
7282
+ readonly variant: import("@angular/core").InputSignal<ProgressBarVariant>;
7283
+ /** Count text override, e.g. "$1.2M". Empty = `value` formatted with `locale`. */
7284
+ readonly count: import("@angular/core").InputSignal<string>;
7285
+ /** Secondary text under the count, e.g. "40%". Empty = not shown. */
7286
+ readonly share: import("@angular/core").InputSignal<string>;
7287
+ /** Screen-reader text for the bar, e.g. "12 documents, 40% of the largest category". Empty = count plus share. */
7288
+ readonly valueText: import("@angular/core").InputSignal<string>;
7289
+ /** BCP 47 locale for the formatted count. Explicit, never the browser's implicit one. */
7290
+ readonly locale: import("@angular/core").InputSignal<string>;
7291
+ /** Unique id tying the label to the progressbar. */
7292
+ protected readonly labelId: string;
7293
+ protected readonly countText: import("@angular/core").Signal<string>;
7294
+ protected readonly effectiveValueText: import("@angular/core").Signal<string>;
7295
+ static ɵfac: i0.ɵɵFactoryDeclaration<BarRowComponent, never>;
7296
+ static ɵcmp: i0.ɵɵComponentDeclaration<BarRowComponent, "ui-bar-row", never, {
7297
+ "label": {
7298
+ "alias": "label";
7299
+ "required": false;
7300
+ "isSignal": true;
7301
+ };
7302
+ "value": {
7303
+ "alias": "value";
7304
+ "required": false;
7305
+ "isSignal": true;
7306
+ };
7307
+ "max": {
7308
+ "alias": "max";
7309
+ "required": false;
7310
+ "isSignal": true;
7311
+ };
7312
+ "variant": {
7313
+ "alias": "variant";
7314
+ "required": false;
7315
+ "isSignal": true;
7316
+ };
7317
+ "count": {
7318
+ "alias": "count";
7319
+ "required": false;
7320
+ "isSignal": true;
7321
+ };
7322
+ "share": {
7323
+ "alias": "share";
7324
+ "required": false;
7325
+ "isSignal": true;
7326
+ };
7327
+ "valueText": {
7328
+ "alias": "valueText";
7329
+ "required": false;
7330
+ "isSignal": true;
7331
+ };
7332
+ "locale": {
7333
+ "alias": "locale";
7334
+ "required": false;
7335
+ "isSignal": true;
7336
+ };
7337
+ }, {}, never, never, true, never>;
6708
7338
  }
6709
7339
  /**
6710
7340
  * QR code for a URL (or any text), with a "Download QR Code" button (PNG). Projected content
@@ -6994,6 +7624,10 @@ export declare const errorContextInterceptor: HttpInterceptorFn;
6994
7624
  *
6995
7625
  * The fallback names what the user was doing: pass `action` explicitly ('send', 'export', …), or
6996
7626
  * let it come from the request method recorded by `errorContextInterceptor`. With neither it says "loading".
7627
+ *
7628
+ * `fallbackContext` is a bare noun phrase ("contact", "your invoices"); an empty or whitespace-only one becomes "data".
7629
+ * The 404 text starts with "The <context>", so one leading article ("the", "a", "an") on the context is dropped
7630
+ * there: "the contact" and "a contact" both read "The contact you're looking for could not be found.".
6997
7631
  */
6998
7632
  export declare function getErrorMessage(error: unknown, fallbackContext?: string, action?: ErrorAction): string;
6999
7633
  export interface UsStateOption {
@@ -7189,6 +7823,9 @@ export type IconName = 'add' | 'close' | 'check' | 'upload' | 'image' | 'chevron
7189
7823
  * `tooltip` is the text shown on hover / keyboard focus (any length) and, unless `ariaLabel` is set, the name
7190
7824
  * assistive tech reads — always set it on a clickable icon. An unknown `name` draws nothing.
7191
7825
  *
7826
+ * `[loading]="true"` replaces the picture with a spinner and disables the button while the request the click started is running
7827
+ * (the same busy state `ui-button` has) — e.g. `<ui-icon name="delete" [loading]="deleting()" … />`.
7828
+ *
7192
7829
  * `[interactive]="false"` draws just the picture (no button, no click) for an icon that sits next to text or inside another
7193
7830
  * button — e.g. the arrow in a "Back" button, a bell beside a reminder. It is hidden from assistive tech unless a `tooltip` or `ariaLabel` is set.
7194
7831
  *
@@ -7205,6 +7842,8 @@ export declare class IconComponent {
7205
7842
  readonly size: import("@angular/core").InputSignal<number>;
7206
7843
  readonly variant: import("@angular/core").InputSignal<"danger" | "default">;
7207
7844
  readonly disabled: import("@angular/core").InputSignal<boolean>;
7845
+ /** The host is running the request this icon started: a spinner replaces the picture and the button is disabled (no click) until it is false again. */
7846
+ readonly loading: import("@angular/core").InputSignal<boolean>;
7208
7847
  /** false = just the picture (no button, no click) — for an icon beside text or inside another button. */
7209
7848
  readonly interactive: import("@angular/core").InputSignal<boolean>;
7210
7849
  /** Fires on click (not while disabled); the host performs the action. */
@@ -7244,6 +7883,11 @@ export declare class IconComponent {
7244
7883
  "required": false;
7245
7884
  "isSignal": true;
7246
7885
  };
7886
+ "loading": {
7887
+ "alias": "loading";
7888
+ "required": false;
7889
+ "isSignal": true;
7890
+ };
7247
7891
  "interactive": {
7248
7892
  "alias": "interactive";
7249
7893
  "required": false;
@@ -7253,6 +7897,143 @@ export declare class IconComponent {
7253
7897
  "iconClick": "iconClick";
7254
7898
  }, never, never, true, never>;
7255
7899
  }
7900
+ /** What an item template receives (`let-item let-i="index"`). */
7901
+ export interface SortableItemContext<T> {
7902
+ $implicit: T;
7903
+ index: number;
7904
+ first: boolean;
7905
+ last: boolean;
7906
+ /** True while this item is being dragged. */
7907
+ dragging: boolean;
7908
+ }
7909
+ /** Marks the `<ng-template>` that renders one item: `<ng-template uiSortableItem let-item let-i="index"> … </ng-template>`. */
7910
+ export declare class SortableItemDirective<T = unknown> {
7911
+ readonly template: TemplateRef<SortableItemContext<T>>;
7912
+ static ngTemplateContextGuard<T>(_dir: SortableItemDirective<T>, _ctx: unknown): _ctx is SortableItemContext<T>;
7913
+ static ɵfac: i0.ɵɵFactoryDeclaration<SortableItemDirective<any>, never>;
7914
+ static ɵdir: i0.ɵɵDirectiveDeclaration<SortableItemDirective<any>, "ng-template[uiSortableItem]", never, {}, {}, never, never, true, never>;
7915
+ }
7916
+ /** Emitted after the order changed (a drop, a keyboard move or a move button). */
7917
+ export interface SortableOrderChange<T> {
7918
+ item: T;
7919
+ from: number;
7920
+ to: number;
7921
+ /** The new full order. */
7922
+ items: T[];
7923
+ }
7924
+ /**
7925
+ * A list (or wrapping grid) of items that can optionally be reordered. Reordering is OFF by default: with `sortable` false it only
7926
+ * renders the items (nothing is draggable or focusable and no controls show), so it is safe to use as a plain list.
7927
+ *
7928
+ * <ui-sortable-list [(items)]="images" [sortable]="canReorder" layout="grid" ariaLabel="Product images"
7929
+ * [itemLabel]="imageLabel" (orderChange)="saveOrder($event)">
7930
+ * <ng-template uiSortableItem let-url let-i="index"> <ui-image-preview [src]="url" /> </ng-template>
7931
+ * </ui-sortable-list>
7932
+ *
7933
+ * With `sortable` on, an item can be moved by
7934
+ * - dragging it onto another item (mouse and any browser with native drag and drop; Firefox / Safari quirks are handled),
7935
+ * - the keyboard: focus an item and press Shift + Arrow keys (Shift + Home / End = first / last),
7936
+ * - the optional move buttons (`moveControls`), the touch- and pointer-friendly way (and the one that needs no gesture).
7937
+ * Every change updates `items` (two-way `[(items)]`) and emits `orderChange`; the host saves it and can set `items` back if the
7938
+ * save fails. A polite live region announces each move ("Image 3 moved to position 1 of 4").
7939
+ */
7940
+ export declare class SortableListComponent<T = unknown> {
7941
+ /** The items in their current order (two-way: `[(items)]`). */
7942
+ readonly items: import("@angular/core").ModelSignal<T[]>;
7943
+ /** Reordering on/off. Default OFF: the list only renders its items. */
7944
+ readonly sortable: import("@angular/core").InputSignal<boolean>;
7945
+ /** 'list' = one column; 'grid' = wrapping columns of at least `gridMin` px. */
7946
+ readonly layout: import("@angular/core").InputSignal<"list" | "grid">;
7947
+ readonly gridMin: import("@angular/core").InputSignal<number>;
7948
+ /** Accessible name of the list. */
7949
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
7950
+ /** Name of an item for assistive tech and tooltips ("Image 3"). */
7951
+ readonly itemLabel: import("@angular/core").InputSignal<(item: T, index: number) => string>;
7952
+ /** Show Move earlier / Move later buttons on each item (needs `sortable`) — the touch-friendly way to reorder. */
7953
+ readonly moveControls: import("@angular/core").InputSignal<boolean>;
7954
+ readonly moveEarlierLabel: import("@angular/core").InputSignal<string>;
7955
+ readonly moveLaterLabel: import("@angular/core").InputSignal<string>;
7956
+ /** Screen-reader instruction for a focused sortable item. */
7957
+ readonly helpText: import("@angular/core").InputSignal<string>;
7958
+ /** Announcement after a move; receives the item label, the new position (1-based) and the list size. */
7959
+ readonly moveAnnouncement: import("@angular/core").InputSignal<(label: string, position: number, total: number) => string>;
7960
+ /** The order changed. */
7961
+ readonly orderChange: import("@angular/core").OutputEmitterRef<SortableOrderChange<T>>;
7962
+ protected readonly itemTemplate: import("@angular/core").Signal<SortableItemDirective<any>>;
7963
+ protected readonly _helpId: string;
7964
+ protected readonly _dragIndex: import("@angular/core").WritableSignal<number | null>;
7965
+ protected readonly _overIndex: import("@angular/core").WritableSignal<number | null>;
7966
+ protected readonly _announcement: import("@angular/core").WritableSignal<string>;
7967
+ protected readonly _count: import("@angular/core").Signal<number>;
7968
+ /** Moves the item at `from` to position `to` (clamped). No-op when nothing changes or sorting is off. */
7969
+ move(from: number, to: number): void;
7970
+ protected onDragStart(event: DragEvent, index: number): void;
7971
+ protected onDragOver(event: DragEvent, index: number): void;
7972
+ protected onDrop(event: DragEvent, index: number): void;
7973
+ protected onDragEnd(): void;
7974
+ protected onKeydown(event: KeyboardEvent, index: number): void;
7975
+ static ɵfac: i0.ɵɵFactoryDeclaration<SortableListComponent<any>, never>;
7976
+ static ɵcmp: i0.ɵɵComponentDeclaration<SortableListComponent<any>, "ui-sortable-list", never, {
7977
+ "items": {
7978
+ "alias": "items";
7979
+ "required": false;
7980
+ "isSignal": true;
7981
+ };
7982
+ "sortable": {
7983
+ "alias": "sortable";
7984
+ "required": false;
7985
+ "isSignal": true;
7986
+ };
7987
+ "layout": {
7988
+ "alias": "layout";
7989
+ "required": false;
7990
+ "isSignal": true;
7991
+ };
7992
+ "gridMin": {
7993
+ "alias": "gridMin";
7994
+ "required": false;
7995
+ "isSignal": true;
7996
+ };
7997
+ "ariaLabel": {
7998
+ "alias": "ariaLabel";
7999
+ "required": false;
8000
+ "isSignal": true;
8001
+ };
8002
+ "itemLabel": {
8003
+ "alias": "itemLabel";
8004
+ "required": false;
8005
+ "isSignal": true;
8006
+ };
8007
+ "moveControls": {
8008
+ "alias": "moveControls";
8009
+ "required": false;
8010
+ "isSignal": true;
8011
+ };
8012
+ "moveEarlierLabel": {
8013
+ "alias": "moveEarlierLabel";
8014
+ "required": false;
8015
+ "isSignal": true;
8016
+ };
8017
+ "moveLaterLabel": {
8018
+ "alias": "moveLaterLabel";
8019
+ "required": false;
8020
+ "isSignal": true;
8021
+ };
8022
+ "helpText": {
8023
+ "alias": "helpText";
8024
+ "required": false;
8025
+ "isSignal": true;
8026
+ };
8027
+ "moveAnnouncement": {
8028
+ "alias": "moveAnnouncement";
8029
+ "required": false;
8030
+ "isSignal": true;
8031
+ };
8032
+ }, {
8033
+ "items": "itemsChange";
8034
+ "orderChange": "orderChange";
8035
+ }, ["itemTemplate"], never, true, never>;
8036
+ }
7256
8037
  export type ImagePreviewShape = 'circle' | 'rounded';
7257
8038
  export type ImagePreviewSize = 'sm' | 'md' | 'lg';
7258
8039
  export type ImagePreviewPlaceholder = 'person' | 'image';
@@ -8244,4 +9025,331 @@ export declare class ColorInputComponent implements ControlValueAccessor, Valida
8244
9025
  "invalidChange": "invalidChange";
8245
9026
  }, never, never, true, never>;
8246
9027
  }
9028
+ /**
9029
+ * A heading for a block of a page or form: an optional icon in a rounded tile, a bold title, a grey description
9030
+ * and optional buttons on the right. Use it above a group of settings or fields, alone or inside a `ui-detail-card`.
9031
+ *
9032
+ * <ui-section-header title="Notifications" />
9033
+ * <ui-section-header title="Email" description="Send and receive email from your own address." icon="email"
9034
+ * background="#eaf3ff" />
9035
+ * <ui-section-header title="Team" [bordered]="true">
9036
+ * <ui-button section-header-actions size="xs" variant="secondary">Add member</ui-button>
9037
+ * </ui-section-header>
9038
+ * <ui-section-header title="Billing" background="#0f172a" textColor="#fff" iconColor="#93c5fd" iconBackground="#1e293b">
9039
+ * <svg section-header-icon width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
9040
+ * </ui-section-header>
9041
+ *
9042
+ * Inputs:
9043
+ * - `title` (required): the heading text.
9044
+ * - `description`: a grey line under the title. Empty = none. The heading is described by it (aria-describedby).
9045
+ * - `level` (1-6, default 2): the heading level for assistive tech (role="heading" + aria-level). Anything else becomes 2.
9046
+ * - `icon`: the name of a library icon, drawn in a rounded tile on the left (decorative, hidden from assistive tech).
9047
+ * Supported names (the same keys `<ui-icon>` accepts): add, close, check, upload, image, chevron-up, chevron-down, arrow-left,
9048
+ * bell, print, view, edit, delete, call, message, email, task, opportunity, assign-owner, assign-agent, view-contact, confirm,
9049
+ * decline, sphere, remove-sphere, portal-invite, archive, duplicate, download, export, share, approve, reject, activate,
9050
+ * deactivate, pause, cancel, restore, send-message, create-job, view-job, create-invoice, create-estimate, add-note, invite-portal.
9051
+ * An unknown name draws no tile and raises no error.
9052
+ * - `background`: any CSS colour for the whole header (default none/transparent). Setting it rounds the corners (10px).
9053
+ * - `textColor`: any CSS colour for the title (default slate #0f172a). The description then uses the same colour at 80% opacity.
9054
+ * Without it the description is #64748b, or #475569 when a `background` is set (keeps it readable on tinted backgrounds).
9055
+ * - `iconColor`: colour of the icon (default blue #2563eb).
9056
+ * - `iconBackground`: colour of the tile behind the icon (default white when a `background` is set, otherwise light grey #f1f5f9).
9057
+ * - `bordered`: 1px #e2e8f0 border and 10px radius (default false).
9058
+ * - `size`: 'md' (default; 1rem title, 28px tile) or 'sm' (0.875rem title, 24px tile).
9059
+ * Padding is built in (14px 18px; 12px 14px at 480px and narrower) - there is no padding input.
9060
+ *
9061
+ * Content slots:
9062
+ * - `[section-header-icon]`: your own icon (an `<svg>`, an `<img>` ...) shown in the tile instead of the `icon` input.
9063
+ * - `[section-header-actions]`: buttons or links on the right; they drop under the text when there is no room.
9064
+ *
9065
+ * Theming by CSS: the colours are the custom properties `--sh-bg`, `--sh-text`, `--sh-icon`, `--sh-icon-bg`; set them on
9066
+ * `ui-section-header` (or an ancestor) instead of using the inputs. An unset input adds no inline style at all.
9067
+ *
9068
+ * Accessibility: colours you choose are your responsibility - keep text at 4.5:1 against the background. The defaults
9069
+ * (#0f172a on white or #eaf3ff, #475569 description on tinted backgrounds) meet WCAG AA.
9070
+ */
9071
+ export declare class SectionHeaderComponent {
9072
+ /** The heading text. */
9073
+ readonly title: import("@angular/core").InputSignal<string>;
9074
+ /** Grey line under the title. Empty = none. */
9075
+ readonly description: import("@angular/core").InputSignal<string>;
9076
+ /** Name of a library icon (see the list above). Empty or unknown = no icon. */
9077
+ readonly icon: import("@angular/core").InputSignal<string>;
9078
+ /** Heading level 1-6 for assistive tech; anything else is treated as 2. */
9079
+ readonly level: import("@angular/core").InputSignal<number>;
9080
+ /** Header background as any CSS colour. Empty = none. */
9081
+ readonly background: import("@angular/core").InputSignal<string>;
9082
+ /** Title colour as any CSS colour. Empty = slate. */
9083
+ readonly textColor: import("@angular/core").InputSignal<string>;
9084
+ /** Icon colour as any CSS colour. Empty = blue. */
9085
+ readonly iconColor: import("@angular/core").InputSignal<string>;
9086
+ /** Icon tile colour as any CSS colour. Empty = white on a `background`, otherwise light grey. */
9087
+ readonly iconBackground: import("@angular/core").InputSignal<string>;
9088
+ /** 1px border and 10px corners. */
9089
+ readonly bordered: import("@angular/core").InputSignal<boolean>;
9090
+ /** 'md' (default) or 'sm'. */
9091
+ readonly size: import("@angular/core").InputSignal<"sm" | "md">;
9092
+ protected readonly titleId: string;
9093
+ protected readonly descriptionId: string;
9094
+ protected readonly headingLevel: import("@angular/core").Signal<number>;
9095
+ protected readonly path: import("@angular/core").Signal<string | null>;
9096
+ static ɵfac: i0.ɵɵFactoryDeclaration<SectionHeaderComponent, never>;
9097
+ static ɵcmp: i0.ɵɵComponentDeclaration<SectionHeaderComponent, "ui-section-header", never, {
9098
+ "title": {
9099
+ "alias": "title";
9100
+ "required": true;
9101
+ "isSignal": true;
9102
+ };
9103
+ "description": {
9104
+ "alias": "description";
9105
+ "required": false;
9106
+ "isSignal": true;
9107
+ };
9108
+ "icon": {
9109
+ "alias": "icon";
9110
+ "required": false;
9111
+ "isSignal": true;
9112
+ };
9113
+ "level": {
9114
+ "alias": "level";
9115
+ "required": false;
9116
+ "isSignal": true;
9117
+ };
9118
+ "background": {
9119
+ "alias": "background";
9120
+ "required": false;
9121
+ "isSignal": true;
9122
+ };
9123
+ "textColor": {
9124
+ "alias": "textColor";
9125
+ "required": false;
9126
+ "isSignal": true;
9127
+ };
9128
+ "iconColor": {
9129
+ "alias": "iconColor";
9130
+ "required": false;
9131
+ "isSignal": true;
9132
+ };
9133
+ "iconBackground": {
9134
+ "alias": "iconBackground";
9135
+ "required": false;
9136
+ "isSignal": true;
9137
+ };
9138
+ "bordered": {
9139
+ "alias": "bordered";
9140
+ "required": false;
9141
+ "isSignal": true;
9142
+ };
9143
+ "size": {
9144
+ "alias": "size";
9145
+ "required": false;
9146
+ "isSignal": true;
9147
+ };
9148
+ }, {}, never, ["[section-header-icon]", "[section-header-actions]"], true, never>;
9149
+ }
9150
+ /**
9151
+ * Shared spacing scale of the layout components (`ui-columns`, `ui-stack`, `ui-action-bar`):
9152
+ * none 0 · xs 4px · sm 8px · md 16px · lg 24px · xl 32px
9153
+ * Every `gap` input also accepts any CSS length string for a custom value, e.g. `gap="20px"` or `gap="1.25rem"`.
9154
+ */
9155
+ export type LayoutGap = 'none' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9156
+ export declare const LAYOUT_GAP_PX: Readonly<Record<LayoutGap, number>>;
9157
+ /**
9158
+ * Resolves a gap token or a custom CSS length to a CSS length. A token maps to its px value; a string that looks like a
9159
+ * CSS length (`20px`, `1.5rem`, `0`, `calc(1rem + 2px)`, `var(--x)`) passes through trimmed; anything else
9160
+ * ('' / null / undefined / an unknown word) falls back to `fallback`.
9161
+ */
9162
+ export declare function resolveLayoutGap(gap: LayoutGap | string | null | undefined, fallback: LayoutGap): string;
9163
+ export type ColumnsCount = 1 | 2 | 3 | 4;
9164
+ /** Width below which the columns stack into one: sm = 480px, md = 768px, lg = 1024px, never = always side by side. */
9165
+ export type ColumnsCollapse = 'sm' | 'md' | 'lg' | 'never';
9166
+ export type ColumnsAlign = 'start' | 'center' | 'stretch';
9167
+ /**
9168
+ * Equal-width columns for page content (the bigger sibling of `ui-form-row`).
9169
+ *
9170
+ * `ui-form-row` is for 1-3 form fields: a fixed 12px gap, and it stacks at 480px. `ui-columns` lays out whole blocks
9171
+ * (cards, sections, lists): 1-4 columns, a gap from the shared scale (default 24px), and it stacks at a width you choose
9172
+ * (default 768px, tablet portrait). Every direct child is one cell. Cells use `minmax(0, 1fr)` tracks, so a long
9173
+ * unbreakable word or a wide table never stretches the grid.
9174
+ *
9175
+ * - `count`: 1 | 2 | 3 | 4 equal columns (default 2). Other values are clamped to 1..4 (0 and negatives give 1, 5+ gives 4,
9176
+ * a non-number gives 2).
9177
+ * - `gap`: none | xs | sm | md | lg | xl (0/4/8/16/24/32px) or any CSS length such as `"20px"` (default `lg`).
9178
+ * - `collapseAt`: sm (480px) | md (768px) | lg (1024px) | never. Below that viewport width the columns become one.
9179
+ * Plain media queries are used (not container queries) because they behave the same in every supported browser.
9180
+ * - `align`: start | center | stretch (default start) - how cells of different heights line up.
9181
+ *
9182
+ * <ui-columns>
9183
+ * <ui-form-section label="Email"> ... </ui-form-section>
9184
+ * <ui-form-section label="Text"> ... </ui-form-section>
9185
+ * </ui-columns>
9186
+ * <ui-columns [count]="3" gap="md" collapseAt="lg" align="stretch"> ... </ui-columns>
9187
+ */
9188
+ export declare class ColumnsComponent {
9189
+ readonly count: import("@angular/core").InputSignal<ColumnsCount>;
9190
+ readonly gap: import("@angular/core").InputSignal<string>;
9191
+ readonly collapseAt: import("@angular/core").InputSignal<ColumnsCollapse>;
9192
+ readonly align: import("@angular/core").InputSignal<ColumnsAlign>;
9193
+ protected readonly cols: import("@angular/core").Signal<ColumnsCount>;
9194
+ protected readonly gapCss: import("@angular/core").Signal<string>;
9195
+ protected readonly collapseClass: import("@angular/core").Signal<string>;
9196
+ static ɵfac: i0.ɵɵFactoryDeclaration<ColumnsComponent, never>;
9197
+ static ɵcmp: i0.ɵɵComponentDeclaration<ColumnsComponent, "ui-columns", never, {
9198
+ "count": {
9199
+ "alias": "count";
9200
+ "required": false;
9201
+ "isSignal": true;
9202
+ };
9203
+ "gap": {
9204
+ "alias": "gap";
9205
+ "required": false;
9206
+ "isSignal": true;
9207
+ };
9208
+ "collapseAt": {
9209
+ "alias": "collapseAt";
9210
+ "required": false;
9211
+ "isSignal": true;
9212
+ };
9213
+ "align": {
9214
+ "alias": "align";
9215
+ "required": false;
9216
+ "isSignal": true;
9217
+ };
9218
+ }, {}, never, ["*"], true, never>;
9219
+ }
9220
+ export type StackDirection = 'vertical' | 'horizontal';
9221
+ export type StackAlign = 'start' | 'center' | 'end' | 'stretch';
9222
+ export type StackJustify = 'start' | 'center' | 'end' | 'between';
9223
+ /**
9224
+ * Stacks its children with ONE consistent gap (vertical by default, or in a row).
9225
+ *
9226
+ * The stack owns the spacing: it resets the margins of its direct children (with `!important`, so it also beats
9227
+ * rules such as the `margin-bottom: 16px` of `ui-detail-card`)
9228
+ * and uses flex `gap` instead. The distance between any two neighbours is exactly `gap`, whatever they are.
9229
+ * Do not put margins on children of a stack; wrap them in a nested `ui-stack` to group. A `ui-divider` keeps its space
9230
+ * inside itself, so give it `spaceAbove="none" spaceBelow="none"` in a stack.
9231
+ *
9232
+ * - `gap`: none | xs | sm | md | lg | xl (0/4/8/16/24/32px) or any CSS length (`gap="20px"`). Default `md`.
9233
+ * - `direction`: vertical (default) | horizontal.
9234
+ * - `align`: start | center | end | stretch (cross axis, default stretch).
9235
+ * - `justify`: start | center | end | between (main axis, default start).
9236
+ * - `wrap`: let a horizontal stack wrap onto more lines (default false).
9237
+ *
9238
+ * <ui-stack gap="20px">
9239
+ * <ui-detail-card title="EMAIL"> ... </ui-detail-card>
9240
+ * <ui-detail-card title="TEXT"> ... </ui-detail-card>
9241
+ * </ui-stack>
9242
+ * <ui-stack direction="horizontal" gap="sm" justify="between" [wrap]="true"> ...buttons... </ui-stack>
9243
+ */
9244
+ export declare class StackComponent {
9245
+ readonly gap: import("@angular/core").InputSignal<string>;
9246
+ readonly direction: import("@angular/core").InputSignal<StackDirection>;
9247
+ readonly align: import("@angular/core").InputSignal<StackAlign>;
9248
+ readonly justify: import("@angular/core").InputSignal<StackJustify>;
9249
+ readonly wrap: import("@angular/core").InputSignal<boolean>;
9250
+ protected readonly gapCss: import("@angular/core").Signal<string>;
9251
+ protected readonly alignCss: import("@angular/core").Signal<string>;
9252
+ protected readonly justifyCss: import("@angular/core").Signal<string>;
9253
+ static ɵfac: i0.ɵɵFactoryDeclaration<StackComponent, never>;
9254
+ static ɵcmp: i0.ɵɵComponentDeclaration<StackComponent, "ui-stack", never, {
9255
+ "gap": {
9256
+ "alias": "gap";
9257
+ "required": false;
9258
+ "isSignal": true;
9259
+ };
9260
+ "direction": {
9261
+ "alias": "direction";
9262
+ "required": false;
9263
+ "isSignal": true;
9264
+ };
9265
+ "align": {
9266
+ "alias": "align";
9267
+ "required": false;
9268
+ "isSignal": true;
9269
+ };
9270
+ "justify": {
9271
+ "alias": "justify";
9272
+ "required": false;
9273
+ "isSignal": true;
9274
+ };
9275
+ "wrap": {
9276
+ "alias": "wrap";
9277
+ "required": false;
9278
+ "isSignal": true;
9279
+ };
9280
+ }, {}, never, ["*"], true, never>;
9281
+ }
9282
+ export type ActionBarAlign = 'start' | 'center' | 'end' | 'between';
9283
+ /**
9284
+ * Bar for page-level actions (Save / Cancel) that stays at the bottom of the scrolling area.
9285
+ *
9286
+ * - `sticky` (default true): sticks to the bottom edge of the scroll container while the page content scrolls under it.
9287
+ * - `align`: start | center | end | between (default end).
9288
+ * - `fade` (default true): a gradient from the bar colour to transparent behind the bar so scrolled content fades out.
9289
+ * - `bordered` (default false): a 1px top border and a solid background instead of the fade; side padding 16px.
9290
+ * - `background`: the bar colour (any CSS colour; default white). Sets the CSS variable `--ab-bg`, which you can
9291
+ * also set from outside.
9292
+ * - `gap`: none | xs | sm | md | lg | xl or any CSS length (default `sm` = 8px).
9293
+ * - `ariaLabel`: when set, the bar is `role="group"` with this label; otherwise it has no role. (It is not a `toolbar`: that role promises
9294
+ * arrow-key navigation between the controls, which a plain Save / Cancel group does not implement.)
9295
+ *
9296
+ * Padding is 12px top and bottom (the bottom grows to the iOS safe-area inset), z-index 10 so it stays under the side
9297
+ * panel (90+) and modals (200+). Below 480px the actions wrap onto more lines; `ui-button` keeps 44px touch targets.
9298
+ * Sticky needs the bar to be a direct part of the scrolling content, with the scroll container as an ancestor.
9299
+ *
9300
+ * <ui-stack gap="20px"> ...cards... </ui-stack>
9301
+ * <ui-action-bar ariaLabel="Page actions">
9302
+ * <ui-button variant="secondary" (click)="cancel()">Cancel</ui-button>
9303
+ * <ui-button [loading]="saving()" (click)="save()">Save</ui-button>
9304
+ * </ui-action-bar>
9305
+ */
9306
+ export declare class ActionBarComponent {
9307
+ readonly sticky: import("@angular/core").InputSignal<boolean>;
9308
+ readonly align: import("@angular/core").InputSignal<ActionBarAlign>;
9309
+ readonly fade: import("@angular/core").InputSignal<boolean>;
9310
+ readonly bordered: import("@angular/core").InputSignal<boolean>;
9311
+ readonly background: import("@angular/core").InputSignal<string>;
9312
+ readonly gap: import("@angular/core").InputSignal<string>;
9313
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
9314
+ protected readonly justify: import("@angular/core").Signal<string>;
9315
+ protected readonly gapCss: import("@angular/core").Signal<string>;
9316
+ static ɵfac: i0.ɵɵFactoryDeclaration<ActionBarComponent, never>;
9317
+ static ɵcmp: i0.ɵɵComponentDeclaration<ActionBarComponent, "ui-action-bar", never, {
9318
+ "sticky": {
9319
+ "alias": "sticky";
9320
+ "required": false;
9321
+ "isSignal": true;
9322
+ };
9323
+ "align": {
9324
+ "alias": "align";
9325
+ "required": false;
9326
+ "isSignal": true;
9327
+ };
9328
+ "fade": {
9329
+ "alias": "fade";
9330
+ "required": false;
9331
+ "isSignal": true;
9332
+ };
9333
+ "bordered": {
9334
+ "alias": "bordered";
9335
+ "required": false;
9336
+ "isSignal": true;
9337
+ };
9338
+ "background": {
9339
+ "alias": "background";
9340
+ "required": false;
9341
+ "isSignal": true;
9342
+ };
9343
+ "gap": {
9344
+ "alias": "gap";
9345
+ "required": false;
9346
+ "isSignal": true;
9347
+ };
9348
+ "ariaLabel": {
9349
+ "alias": "ariaLabel";
9350
+ "required": false;
9351
+ "isSignal": true;
9352
+ };
9353
+ }, {}, never, ["*"], true, never>;
9354
+ }
8247
9355
  export type { CreateDocumentRequestBody, DocuSignSendBody, DocumentSentInfo, DocumentSentMessage, DocumentsPayloadExtras, ExistingEnvelopeSendBody, SendDocumentsPayload, SendEmailPayload, SendTextPayload, VideoDefaults, VideoLinkOptions };