@forgeroute/ui 0.3.10 → 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,6 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
- import { ChangeDetectorRef, InjectionToken, OnDestroy, OnInit, Signal, WritableSignal } from "@angular/core";
3
- import { AbstractControl, ControlValueAccessor, ValidationErrors, Validator } from "@angular/forms";
2
+ import { ChangeDetectorRef, EnvironmentProviders, InjectionToken, OnDestroy, OnInit, Signal, TemplateRef, WritableSignal } from "@angular/core";
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";
6
6
  import { SafeHtml } from "@angular/platform-browser";
@@ -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
  /**
@@ -2056,12 +2148,34 @@ export declare class ImageRepositionComponent {
2056
2148
  "y": "yChange";
2057
2149
  }, never, never, true, never>;
2058
2150
  }
2151
+ /** One checkbox of a group (`options`). */
2152
+ export interface CheckboxOption {
2153
+ value: string;
2154
+ label: string;
2155
+ /** Helper text under the option. */
2156
+ description?: string;
2157
+ disabled?: boolean;
2158
+ }
2059
2159
  export declare class CheckboxComponent implements ControlValueAccessor, Validator, UiFieldValidity {
2060
2160
  readonly label: import("@angular/core").InputSignal<string>;
2061
2161
  /** Accessible name for the native checkbox when there is no visible `label` (e.g. a table row's selector). */
2062
2162
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
2063
2163
  readonly indeterminate: import("@angular/core").InputSignal<boolean>;
2064
2164
  readonly checked: import("@angular/core").ModelSignal<boolean>;
2165
+ /**
2166
+ * GROUP MODE (optional): with `options` the component renders a native `<fieldset>` with a `<legend>` and one checkbox per option,
2167
+ * and its value (ngModel / forms) is the array of ticked option values (in option order). Without `options` it is the single
2168
+ * boolean checkbox it always was. In group mode `label` / `checked` / `indeterminate` are not used; `required` means at least one
2169
+ * option must be ticked ("<legend> is required.").
2170
+ */
2171
+ readonly options: import("@angular/core").InputSignal<CheckboxOption[]>;
2172
+ /** Group heading (the fieldset's legend). */
2173
+ readonly legend: import("@angular/core").InputSignal<string>;
2174
+ /** Indent this checkbox (or the whole group) under its parent option, aligned with the parent's label. */
2175
+ readonly indent: import("@angular/core").InputSignal<boolean>;
2176
+ protected readonly groupMode: import("@angular/core").Signal<boolean>;
2177
+ private readonly _selected;
2178
+ protected isSelected(value: string): boolean;
2065
2179
  /** The box must be ticked. */
2066
2180
  readonly required: import("@angular/core").InputSignal<boolean>;
2067
2181
  /** Replaces the built-in "<label> is required." message. */
@@ -2094,11 +2208,15 @@ export declare class CheckboxComponent implements ControlValueAccessor, Validato
2094
2208
  protected readonly disabled: import("@angular/core").Signal<boolean>;
2095
2209
  private onChange;
2096
2210
  private onTouched;
2097
- writeValue(val: boolean): void;
2098
- registerOnChange(fn: (v: boolean) => void): void;
2211
+ writeValue(val: boolean | string[] | null): void;
2212
+ registerOnChange(fn: (v: boolean | string[]) => void): void;
2099
2213
  registerOnTouched(fn: () => void): void;
2100
2214
  setDisabledState(d: boolean): void;
2101
2215
  onToggle(event: Event): void;
2216
+ /** The values keep the option order, and values that are not an option are dropped. */
2217
+ private inOptionOrder;
2218
+ /** Group mode: ticks / unticks one option; the form gets the full list of ticked values. */
2219
+ onToggleOption(value: string, event: Event): void;
2102
2220
  static ɵfac: i0.ɵɵFactoryDeclaration<CheckboxComponent, never>;
2103
2221
  static ɵcmp: i0.ɵɵComponentDeclaration<CheckboxComponent, "ui-checkbox", never, {
2104
2222
  "label": {
@@ -2121,6 +2239,21 @@ export declare class CheckboxComponent implements ControlValueAccessor, Validato
2121
2239
  "required": false;
2122
2240
  "isSignal": true;
2123
2241
  };
2242
+ "options": {
2243
+ "alias": "options";
2244
+ "required": false;
2245
+ "isSignal": true;
2246
+ };
2247
+ "legend": {
2248
+ "alias": "legend";
2249
+ "required": false;
2250
+ "isSignal": true;
2251
+ };
2252
+ "indent": {
2253
+ "alias": "indent";
2254
+ "required": false;
2255
+ "isSignal": true;
2256
+ };
2124
2257
  "required": {
2125
2258
  "alias": "required";
2126
2259
  "required": false;
@@ -2156,75 +2289,186 @@ export declare class CheckboxComponent implements ControlValueAccessor, Validato
2156
2289
  "invalidChange": "invalidChange";
2157
2290
  }, never, ["*"], true, never>;
2158
2291
  }
2159
- export declare class ButtonComponent {
2160
- readonly variant: import("@angular/core").InputSignal<"primary" | "secondary" | "danger" | "warning" | "link" | "ghost" | "tab" | "icon" | "icon-danger">;
2161
- /** Renders an anchor to this route instead of a button (use with `variant="link"` for an inline text link). `['/settings']` or `'/settings'`. */
2162
- readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
2163
- /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
2164
- readonly queryParams: import("@angular/core").InputSignal<Record<string, unknown> | null>;
2165
- readonly color: import("@angular/core").InputSignal<string | undefined>;
2166
- readonly active: import("@angular/core").InputSignal<boolean>;
2167
- readonly size: import("@angular/core").InputSignal<"sm" | "md" | "xs" | "lg">;
2168
- readonly type: import("@angular/core").InputSignal<"reset" | "submit" | "button">;
2169
- readonly disabled: import("@angular/core").InputSignal<boolean>;
2170
- readonly loading: import("@angular/core").InputSignal<boolean>;
2171
- readonly fullWidth: import("@angular/core").InputSignal<boolean>;
2172
- readonly ariaLabel: import("@angular/core").InputSignal<string | undefined>;
2173
- readonly ariaExpanded: import("@angular/core").InputSignal<boolean | undefined>;
2174
- readonly ariaPressed: import("@angular/core").InputSignal<boolean | undefined>;
2175
- /** Id of the element that describes this button (an error or hint rendered next to it), e.g. `<ui-error errorId="x">`. Absent when empty. */
2176
- readonly ariaDescribedBy: import("@angular/core").InputSignal<string | undefined>;
2177
- _footerLeft?: string;
2178
- _footerRight?: string;
2179
- static ɵfac: i0.ɵɵFactoryDeclaration<ButtonComponent, never>;
2180
- static ɵcmp: i0.ɵɵComponentDeclaration<ButtonComponent, "ui-button", never, {
2181
- "variant": {
2182
- "alias": "variant";
2292
+ /**
2293
+ * On/off switch for settings: a label and optional hint on the left, the switch on the right
2294
+ * (ControlValueAccessor with a boolean value; `[(checked)]` also works).
2295
+ *
2296
+ * <ui-toggle label="SMS Enabled" hint="Allow sending texts" [(ngModel)]="form.sms" />
2297
+ * <ui-toggle label="Autoplay" [checked]="on()" (checkedChange)="on.set($event)" />
2298
+ * <ui-toggle ariaLabel="Enable feature" [(checked)]="on" /> <!-- no visible label -->
2299
+ *
2300
+ * Built on a native `<input type="checkbox" role="switch">`, so Space toggles it, it is focusable, and a screen reader
2301
+ * announces it as a switch (on / off). The label and hint name and describe it. `disabled` (input or form control) dims it
2302
+ * and blocks changes. On touch devices and phones the switch has a >= 44px hit area.
2303
+ */
2304
+ export declare class ToggleComponent implements ControlValueAccessor {
2305
+ /** Bold line on the left. */
2306
+ readonly label: import("@angular/core").InputSignal<string>;
2307
+ /** Grey helper text under the label. */
2308
+ readonly hint: import("@angular/core").InputSignal<string>;
2309
+ /** Accessible name when there is no visible `label`. */
2310
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
2311
+ /** Whether the switch is on. Two-way: `[(checked)]`. */
2312
+ readonly checked: import("@angular/core").ModelSignal<boolean>;
2313
+ /** Public so a host template can bind `[disabled]` (a protected input fails strict template type-checking, TS2445). */
2314
+ readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
2315
+ private readonly _cvaDisabled;
2316
+ protected readonly disabled: import("@angular/core").Signal<boolean>;
2317
+ private readonly _id;
2318
+ protected readonly _inputId: string;
2319
+ protected readonly _labelId: string;
2320
+ protected readonly _hintId: string;
2321
+ private _onChange;
2322
+ private _onTouched;
2323
+ writeValue(val: boolean): void;
2324
+ registerOnChange(fn: (v: boolean) => void): void;
2325
+ registerOnTouched(fn: () => void): void;
2326
+ setDisabledState(d: boolean): void;
2327
+ protected onTouched(): void;
2328
+ protected onToggle(event: Event): void;
2329
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToggleComponent, never>;
2330
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToggleComponent, "ui-toggle", never, {
2331
+ "label": {
2332
+ "alias": "label";
2183
2333
  "required": false;
2184
2334
  "isSignal": true;
2185
2335
  };
2186
- "routerLink": {
2187
- "alias": "routerLink";
2336
+ "hint": {
2337
+ "alias": "hint";
2188
2338
  "required": false;
2189
2339
  "isSignal": true;
2190
2340
  };
2191
- "queryParams": {
2192
- "alias": "queryParams";
2341
+ "ariaLabel": {
2342
+ "alias": "ariaLabel";
2193
2343
  "required": false;
2194
2344
  "isSignal": true;
2195
2345
  };
2196
- "color": {
2197
- "alias": "color";
2346
+ "checked": {
2347
+ "alias": "checked";
2198
2348
  "required": false;
2199
2349
  "isSignal": true;
2200
2350
  };
2201
- "active": {
2202
- "alias": "active";
2351
+ "_disabledInput": {
2352
+ "alias": "disabled";
2203
2353
  "required": false;
2204
2354
  "isSignal": true;
2205
2355
  };
2206
- "size": {
2207
- "alias": "size";
2356
+ }, {
2357
+ "checked": "checkedChange";
2358
+ }, never, never, true, never>;
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";
2208
2452
  "required": false;
2209
2453
  "isSignal": true;
2210
2454
  };
2211
- "type": {
2212
- "alias": "type";
2455
+ "orientation": {
2456
+ "alias": "orientation";
2213
2457
  "required": false;
2214
2458
  "isSignal": true;
2215
2459
  };
2216
- "disabled": {
2217
- "alias": "disabled";
2460
+ "clickable": {
2461
+ "alias": "clickable";
2218
2462
  "required": false;
2219
2463
  "isSignal": true;
2220
2464
  };
2221
- "loading": {
2222
- "alias": "loading";
2465
+ "linear": {
2466
+ "alias": "linear";
2223
2467
  "required": false;
2224
2468
  "isSignal": true;
2225
2469
  };
2226
- "fullWidth": {
2227
- "alias": "fullWidth";
2470
+ "size": {
2471
+ "alias": "size";
2228
2472
  "required": false;
2229
2473
  "isSignal": true;
2230
2474
  };
@@ -2233,44 +2477,631 @@ export declare class ButtonComponent {
2233
2477
  "required": false;
2234
2478
  "isSignal": true;
2235
2479
  };
2236
- "ariaExpanded": {
2237
- "alias": "ariaExpanded";
2480
+ "labelsOnSmallScreens": {
2481
+ "alias": "labelsOnSmallScreens";
2238
2482
  "required": false;
2239
2483
  "isSignal": true;
2240
2484
  };
2241
- "ariaPressed": {
2242
- "alias": "ariaPressed";
2485
+ }, {
2486
+ "current": "currentChange";
2487
+ "stepClick": "stepClick";
2488
+ }, never, never, true, never>;
2489
+ }
2490
+ /** One weekday of the schedule. `open` / `close` are `HH:mm`; `open2` / `close2` are the optional second shift (split shift). */
2491
+ export interface BusinessHoursDay {
2492
+ /** 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | 'sun' */
2493
+ day: string;
2494
+ /** 'Monday' … */
2495
+ label: string;
2496
+ enabled: boolean;
2497
+ open: string;
2498
+ close: string;
2499
+ open2?: string;
2500
+ close2?: string;
2501
+ }
2502
+ /** Mon–Fri 9:00 AM – 5:00 PM, Sat/Sun closed (their stored times are 9:00 AM – 12:00 PM, used when the day is switched on). */
2503
+ export declare function buildDefaultSchedule(): BusinessHoursDay[];
2504
+ /**
2505
+ * Parses a stored schedule (`{"mon":{"enabled":true,"open":"09:00","close":"17:00"}, …}`, optionally with `open2` / `close2`)
2506
+ * into a full Mon–Sun array. Missing days / fields fall back to the defaults; anything that is not valid JSON gives the defaults.
2507
+ */
2508
+ export declare function parseBizHours(json: string | null | undefined): BusinessHoursDay[];
2509
+ /** The inverse of `parseBizHours`: `{ mon: { enabled, open, close[, open2, close2] }, … }` as a JSON string. */
2510
+ export declare function serializeBizHours(schedule: BusinessHoursDay[]): string;
2511
+ export type BusinessHoursStep = 15 | 30 | 60;
2512
+ /** `HH:mm` options for one day, every `step` minutes from 00:00 to the last slot of the day, labelled 12-hour ("9:00 AM") or 24-hour ("09:00"). */
2513
+ export declare function businessTimeOptions(step?: BusinessHoursStep, hour24?: boolean): {
2514
+ value: string;
2515
+ label: string;
2516
+ }[];
2517
+ /**
2518
+ * Weekly business-hours editor: one row per weekday with a checkbox (open / closed) and "opens … to … closes" selects.
2519
+ * ControlValueAccessor over `BusinessHoursDay[]` (`[(ngModel)]`, `formControl`, or `[(value)]`); the array is never mutated, each change emits a new array.
2520
+ *
2521
+ * <ui-business-hours label="Schedule" [(ngModel)]="schedule" />
2522
+ * <ui-business-hours label="Schedule" [(ngModel)]="schedule" [step]="15" [hour24]="true" [allowSplitShifts]="true" [showCopyToWeekdays]="true" />
2523
+ *
2524
+ * Inputs
2525
+ * - `label` (names the group), `hint`, `error` (host message, wins), `showErrors` (reveal built-in messages now; otherwise after the first change / blur), `disabled`.
2526
+ * - `step` 15 | 30 | 60 minutes between time options (default 30). A stored time that is not on the grid is still offered, so it never shows blank.
2527
+ * - `hour24` 24-hour labels ("17:00") instead of "5:00 PM".
2528
+ * - `allowSplitShifts` adds an "Add shift" / "Remove shift" link per open day for a second shift (`open2` / `close2`), e.g. 9–12 and 1–5.
2529
+ * - `showCopyToWeekdays` adds a "Copy Monday to weekdays" button that copies Monday's open flag and times (incl. the second shift) to Tue–Fri.
2530
+ * - `required` at least one day must be open ("<label> needs at least one open day.").
2531
+ *
2532
+ * Validation (built in; `Validator` + `UiFieldValidity` + `invalidChange`, like the other inputs): for every open day the closing time must be after
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.
2534
+ * Overnight ranges are not supported (a single `open < close` window per shift), the same as call routing.
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
+ *
2538
+ * Accessibility: a `role="group"` named by `label`; every select has its own name ("Monday opens", "Monday closes", "Monday second shift opens" …);
2539
+ * errors are announced (`role="alert"`) and linked with `aria-describedby`.
2540
+ */
2541
+ export declare class BusinessHoursComponent implements ControlValueAccessor, Validator, UiFieldValidity {
2542
+ /** The schedule (also what `ngModel` / `formControl` hold). Two-way: `[(value)]`. */
2543
+ readonly value: import("@angular/core").ModelSignal<BusinessHoursDay[]>;
2544
+ readonly label: import("@angular/core").InputSignal<string>;
2545
+ readonly hint: import("@angular/core").InputSignal<string>;
2546
+ /** Host-provided error message (wins over the built-in ones). */
2547
+ readonly error: import("@angular/core").InputSignal<string>;
2548
+ /** Reveal the built-in message now (e.g. after a failed Save click) instead of after the first change. */
2549
+ readonly showErrors: import("@angular/core").InputSignal<boolean>;
2550
+ /** At least one day must be open. */
2551
+ readonly required: import("@angular/core").InputSignal<boolean>;
2552
+ /** Minutes between time options. */
2553
+ readonly step: import("@angular/core").InputSignal<BusinessHoursStep>;
2554
+ /** 24-hour labels. */
2555
+ readonly hour24: import("@angular/core").InputSignal<boolean>;
2556
+ /** Allow a second shift per day (split shifts). */
2557
+ readonly allowSplitShifts: import("@angular/core").InputSignal<boolean>;
2558
+ /** Show "Copy Monday to weekdays". */
2559
+ readonly showCopyToWeekdays: import("@angular/core").InputSignal<boolean>;
2560
+ /** Public so a host template can bind `[disabled]` (a protected input fails strict template type-checking, TS2445). */
2561
+ readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
2562
+ private readonly _cvaDisabled;
2563
+ protected readonly disabled: import("@angular/core").Signal<boolean>;
2564
+ protected readonly _descId: string;
2565
+ private readonly _reveal;
2566
+ private readonly _issue;
2567
+ /** True while a built-in rule is broken or the host passed an `[error]`. */
2568
+ readonly invalid: import("@angular/core").Signal<boolean>;
2569
+ /** Message of the first broken built-in rule ('' when valid), shown or not. */
2570
+ readonly errorMessage: import("@angular/core").Signal<string>;
2571
+ /** Emits whenever the field becomes invalid / valid. */
2572
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
2573
+ protected readonly _activeError: import("@angular/core").Signal<string>;
2574
+ private _onValidatorChange;
2575
+ private readonly _options;
2576
+ constructor();
2577
+ /** Re-run the form control's validation when a rule input (`required`, `allowSplitShifts`) changes. */
2578
+ ngOnChanges(): void;
2579
+ validate(control: AbstractControl): ValidationErrors | null;
2580
+ registerOnValidatorChange(fn: () => void): void;
2581
+ private _onChange;
2582
+ private _onTouched;
2583
+ writeValue(val: BusinessHoursDay[] | null): void;
2584
+ registerOnChange(fn: (v: BusinessHoursDay[]) => void): void;
2585
+ registerOnTouched(fn: () => void): void;
2586
+ setDisabledState(d: boolean): void;
2587
+ /** The grid options plus the stored value when it is not on the grid (so a saved 09:15 never renders blank). */
2588
+ protected optionsFor(current: string): {
2589
+ value: string;
2590
+ label: string;
2591
+ }[];
2592
+ protected hasSecondShift(d: BusinessHoursDay): boolean;
2593
+ /** A second shift needs room: the first must end at least one hour before the last slot of the day. */
2594
+ protected canAddShift(d: BusinessHoursDay): boolean;
2595
+ protected update(day: string, patch: Partial<BusinessHoursDay>): void;
2596
+ protected addShift(day: string): void;
2597
+ protected removeShift(day: string): void;
2598
+ protected copyMondayToWeekdays(): void;
2599
+ private _emit;
2600
+ private _check;
2601
+ static ɵfac: i0.ɵɵFactoryDeclaration<BusinessHoursComponent, never>;
2602
+ static ɵcmp: i0.ɵɵComponentDeclaration<BusinessHoursComponent, "ui-business-hours", never, {
2603
+ "value": {
2604
+ "alias": "value";
2243
2605
  "required": false;
2244
2606
  "isSignal": true;
2245
2607
  };
2246
- "ariaDescribedBy": {
2247
- "alias": "ariaDescribedBy";
2608
+ "label": {
2609
+ "alias": "label";
2248
2610
  "required": false;
2249
2611
  "isSignal": true;
2250
2612
  };
2251
- "_footerLeft": {
2252
- "alias": "modal-footer-left";
2613
+ "hint": {
2614
+ "alias": "hint";
2253
2615
  "required": false;
2616
+ "isSignal": true;
2254
2617
  };
2255
- "_footerRight": {
2256
- "alias": "modal-footer-right";
2618
+ "error": {
2619
+ "alias": "error";
2257
2620
  "required": false;
2621
+ "isSignal": true;
2258
2622
  };
2259
- }, {}, never, ["*"], true, never>;
2260
- }
2261
- export declare class ToggleBadgeComponent {
2262
- readonly label: import("@angular/core").InputSignal<string>;
2263
- readonly active: import("@angular/core").InputSignal<boolean>;
2264
- readonly toggle: import("@angular/core").OutputEmitterRef<void>;
2265
- static ɵfac: i0.ɵɵFactoryDeclaration<ToggleBadgeComponent, never>;
2266
- static ɵcmp: i0.ɵɵComponentDeclaration<ToggleBadgeComponent, "ui-toggle-badge", never, {
2267
- "label": {
2268
- "alias": "label";
2623
+ "showErrors": {
2624
+ "alias": "showErrors";
2269
2625
  "required": false;
2270
2626
  "isSignal": true;
2271
2627
  };
2272
- "active": {
2273
- "alias": "active";
2628
+ "required": {
2629
+ "alias": "required";
2630
+ "required": false;
2631
+ "isSignal": true;
2632
+ };
2633
+ "step": {
2634
+ "alias": "step";
2635
+ "required": false;
2636
+ "isSignal": true;
2637
+ };
2638
+ "hour24": {
2639
+ "alias": "hour24";
2640
+ "required": false;
2641
+ "isSignal": true;
2642
+ };
2643
+ "allowSplitShifts": {
2644
+ "alias": "allowSplitShifts";
2645
+ "required": false;
2646
+ "isSignal": true;
2647
+ };
2648
+ "showCopyToWeekdays": {
2649
+ "alias": "showCopyToWeekdays";
2650
+ "required": false;
2651
+ "isSignal": true;
2652
+ };
2653
+ "_disabledInput": {
2654
+ "alias": "disabled";
2655
+ "required": false;
2656
+ "isSignal": true;
2657
+ };
2658
+ }, {
2659
+ "value": "valueChange";
2660
+ "invalidChange": "invalidChange";
2661
+ }, never, never, true, never>;
2662
+ }
2663
+ export interface RadioOption {
2664
+ value: string;
2665
+ label: string;
2666
+ description?: string;
2667
+ disabled?: boolean;
2668
+ }
2669
+ /**
2670
+ * Group of native radio buttons inside a `<fieldset>` (+ `<legend>` = `label`), a ControlValueAccessor (string value, `''`
2671
+ * = nothing selected). Arrow keys move and select natively because all radios share one `name`.
2672
+ * Built-in validation: `required` -> "<label> is required."; a non-empty value that is not one of the `options` ->
2673
+ * "Select a valid option." (not checked while `options` is empty). Disabled groups are never invalid.
2674
+ *
2675
+ * Validity API (same on every ui input): Angular `Validator`, signals `invalid` + `errorMessage`, `invalidChange` output,
2676
+ * provides `UiFieldValidity`. Messages show after the group lost focus or while `[showErrors]="true"`; a host `[error]` wins.
2677
+ */
2678
+ export declare class RadioGroupComponent implements ControlValueAccessor, Validator, UiFieldValidity {
2679
+ readonly label: import("@angular/core").InputSignal<string>;
2680
+ readonly options: import("@angular/core").InputSignal<RadioOption[]>;
2681
+ readonly required: import("@angular/core").InputSignal<boolean>;
2682
+ readonly hint: import("@angular/core").InputSignal<string>;
2683
+ readonly error: import("@angular/core").InputSignal<string>;
2684
+ /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
2685
+ readonly showErrors: import("@angular/core").InputSignal<boolean>;
2686
+ readonly orientation: import("@angular/core").InputSignal<"horizontal" | "vertical">;
2687
+ /** Native `name` shared by the radios; a unique one is generated when omitted. */
2688
+ readonly name: import("@angular/core").InputSignal<string>;
2689
+ protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
2690
+ private readonly _cvaDisabled;
2691
+ protected readonly disabled: import("@angular/core").Signal<boolean>;
2692
+ private readonly _generatedName;
2693
+ protected get _name(): string;
2694
+ protected readonly _descId: string;
2695
+ protected readonly _value: import("@angular/core").WritableSignal<string>;
2696
+ private readonly _reveal;
2697
+ private readonly _issue;
2698
+ /** True while the value breaks a built-in rule or the host passed an `[error]`. */
2699
+ readonly invalid: import("@angular/core").Signal<boolean>;
2700
+ /** Message of the first broken built-in rule ('' when valid), shown or not. */
2701
+ readonly errorMessage: import("@angular/core").Signal<string>;
2702
+ /** Emits whenever the field becomes invalid / valid. */
2703
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
2704
+ protected readonly _activeError: import("@angular/core").Signal<string>;
2705
+ private _onValidatorChange;
2706
+ constructor();
2707
+ /** Re-run the form control's validation when a rule input (required, options) changes. */
2708
+ ngOnChanges(): void;
2709
+ validate(control: AbstractControl): ValidationErrors | null;
2710
+ registerOnValidatorChange(fn: () => void): void;
2711
+ private _onChange;
2712
+ private _onTouched;
2713
+ writeValue(val: string): void;
2714
+ registerOnChange(fn: (v: string) => void): void;
2715
+ registerOnTouched(fn: () => void): void;
2716
+ setDisabledState(d: boolean): void;
2717
+ protected onSelect(value: string): void;
2718
+ /** Touched once focus leaves the whole group (moving between its radios does not count). */
2719
+ protected onFocusOut(event: FocusEvent): void;
2720
+ private _check;
2721
+ static ɵfac: i0.ɵɵFactoryDeclaration<RadioGroupComponent, never>;
2722
+ static ɵcmp: i0.ɵɵComponentDeclaration<RadioGroupComponent, "ui-radio-group", never, {
2723
+ "label": {
2724
+ "alias": "label";
2725
+ "required": false;
2726
+ "isSignal": true;
2727
+ };
2728
+ "options": {
2729
+ "alias": "options";
2730
+ "required": false;
2731
+ "isSignal": true;
2732
+ };
2733
+ "required": {
2734
+ "alias": "required";
2735
+ "required": false;
2736
+ "isSignal": true;
2737
+ };
2738
+ "hint": {
2739
+ "alias": "hint";
2740
+ "required": false;
2741
+ "isSignal": true;
2742
+ };
2743
+ "error": {
2744
+ "alias": "error";
2745
+ "required": false;
2746
+ "isSignal": true;
2747
+ };
2748
+ "showErrors": {
2749
+ "alias": "showErrors";
2750
+ "required": false;
2751
+ "isSignal": true;
2752
+ };
2753
+ "orientation": {
2754
+ "alias": "orientation";
2755
+ "required": false;
2756
+ "isSignal": true;
2757
+ };
2758
+ "name": {
2759
+ "alias": "name";
2760
+ "required": false;
2761
+ "isSignal": true;
2762
+ };
2763
+ "_disabledInput": {
2764
+ "alias": "disabled";
2765
+ "required": false;
2766
+ "isSignal": true;
2767
+ };
2768
+ }, {
2769
+ "invalidChange": "invalidChange";
2770
+ }, never, never, true, never>;
2771
+ }
2772
+ /** One phone number the host found for sale. */
2773
+ export interface PhoneNumberOption {
2774
+ /** E.164 number, e.g. '+15125550100'. */
2775
+ phoneNumber: string;
2776
+ /** What the user sees, e.g. '(512) 555-0100'. */
2777
+ friendlyName: string;
2778
+ /** City / state label shown as a badge, e.g. 'Austin, TX'. */
2779
+ region: string | null;
2780
+ }
2781
+ /** What the host's `search` function receives: exactly one of the two keys. */
2782
+ export interface PhoneNumberSearchCriteria {
2783
+ postalCode?: string;
2784
+ areaCode?: string;
2785
+ }
2786
+ /** What `selected` emits: the chosen number plus the criteria that found it. */
2787
+ export type PhoneNumberSelection = PhoneNumberOption & PhoneNumberSearchCriteria;
2788
+ export type PhoneNumberSearchMode = 'zip' | 'area-code';
2789
+ export declare const PHONE_NUMBER_ZIP_LENGTH = 5;
2790
+ export declare const PHONE_NUMBER_AREA_CODE_LENGTH = 3;
2791
+ /** Keeps only digits and cuts to `max` ("78701-1234" -> "78701"). */
2792
+ export declare function digitsOnly(value: string | null | undefined, max: number): string;
2793
+ /**
2794
+ * Side panel that walks the user through finding and choosing a phone number to buy. The HOST does the searching (the
2795
+ * library owns no HTTP and knows nothing about the phone provider): it passes a `search` function that returns the
2796
+ * numbers for a ZIP code or an area code.
2797
+ *
2798
+ * <ui-phone-number-picker
2799
+ * [open]="showPicker()"
2800
+ * defaultPostalCode="78701"
2801
+ * costNotice="Each number costs about $1.00/month."
2802
+ * [search]="searchNumbers"
2803
+ * (selected)="buy($event)"
2804
+ * (closed)="showPicker.set(false)" />
2805
+ *
2806
+ * searchNumbers = (c: PhoneNumberSearchCriteria) => this.api.get<PhoneNumberOption[]>('/phone-numbers/available', c);
2807
+ *
2808
+ * Inputs: `open`, `title` ('Get a Phone Number'), `subtitle`, `defaultPostalCode` (prefilled ZIP, may be empty),
2809
+ * `defaultMode` ('zip' | 'area-code'), `allowAreaCode` (true: the user can switch to an area code search), `search`
2810
+ * (required), `costNotice` (warning shown on steps 1 and 3), `confirmLabel` ('Get this number'), `maxResults` (5: caps what is shown).
2811
+ * Outputs: `selected` (PhoneNumberSelection, only from the final confirm button) and `closed`.
2812
+ *
2813
+ * Flow (three steps, nothing is ever searched or bought without a click):
2814
+ * 1. Confirm location - ZIP code (5 digits) or area code (3 digits), prefilled from `defaultPostalCode`. The primary button
2815
+ * "Confirm ZIP code and search" is disabled until the value is complete; Enter does the same. Non-digits are ignored while
2816
+ * typing, a pasted ZIP+4 keeps its first 5 digits.
2817
+ * 2. Results - `search(criteria)` runs ONCE per confirm; a newer search (or closing) drops the older response. Skeleton while
2818
+ * loading, an empty state, or an error with Retry. Picking a row only moves on to step 3.
2819
+ * 3. Confirm number - "You chose ...". Only the final button emits `selected`, then `closed`.
2820
+ * Every time `open` becomes true the panel starts again at step 1 with the defaults; closing discards everything.
2821
+ * `defaultPostalCode` may arrive after the panel opened (the host loads it asynchronously): while the panel is on step 1 in ZIP
2822
+ * mode and the user has NOT typed or pasted in the ZIP field (clearing it counts as an edit), a non-empty default fills the field;
2823
+ * typed input is never overwritten, an empty default is ignored, and a default that arrives on step 2 or 3 is applied when the user
2824
+ * returns to step 1 only if the field is still untouched.
2825
+ * The final confirm emits `selected` at most once per opening (double clicks / repeated Enter are ignored and the button is disabled
2826
+ * after the first click). The panel emits `closed` right after `selected`, so the host closes it; reopening re-enables the button.
2827
+ *
2828
+ * Accessibility: labelled fields with the library's inline errors, focus moves to the first control of each step (a text
2829
+ * field is not focused on touch devices so no keyboard pops up), a visually hidden polite live region announces searching,
2830
+ * result counts, errors and step changes, Escape closes the panel even from inside a field, 44px touch targets come from the
2831
+ * shared components.
2832
+ */
2833
+ export declare class PhoneNumberPickerComponent {
2834
+ readonly open: import("@angular/core").InputSignal<boolean>;
2835
+ readonly title: import("@angular/core").InputSignal<string>;
2836
+ readonly subtitle: import("@angular/core").InputSignal<string>;
2837
+ readonly defaultPostalCode: import("@angular/core").InputSignal<string>;
2838
+ readonly defaultMode: import("@angular/core").InputSignal<PhoneNumberSearchMode>;
2839
+ readonly allowAreaCode: import("@angular/core").InputSignal<boolean>;
2840
+ /** Host-supplied search. Called once per confirm; the library never fetches by itself. */
2841
+ readonly search: import("@angular/core").InputSignal<(criteria: PhoneNumberSearchCriteria) => Observable<PhoneNumberOption[]>>;
2842
+ readonly costNotice: import("@angular/core").InputSignal<string>;
2843
+ readonly confirmLabel: import("@angular/core").InputSignal<string>;
2844
+ readonly maxResults: import("@angular/core").InputSignal<number>;
2845
+ readonly closed: import("@angular/core").OutputEmitterRef<void>;
2846
+ readonly selected: import("@angular/core").OutputEmitterRef<PhoneNumberSelection>;
2847
+ protected readonly modeOptions: RadioOption[];
2848
+ protected readonly skeletonRows: number[];
2849
+ protected readonly modeCtl: FormControl<string>;
2850
+ protected readonly zipCtl: FormControl<string>;
2851
+ protected readonly areaCtl: FormControl<string>;
2852
+ protected readonly step: import("@angular/core").WritableSignal<1 | 2 | 3>;
2853
+ protected readonly mode: import("@angular/core").WritableSignal<PhoneNumberSearchMode>;
2854
+ private readonly _zip;
2855
+ private readonly _area;
2856
+ protected readonly touched: import("@angular/core").WritableSignal<boolean>;
2857
+ /** True once the user typed / pasted in the ZIP field this opening: a late default never overwrites it. */
2858
+ private readonly _zipEdited;
2859
+ /** True once the final confirm emitted `selected` this opening: further confirms are ignored. */
2860
+ protected readonly confirmed: import("@angular/core").WritableSignal<boolean>;
2861
+ protected readonly loading: import("@angular/core").WritableSignal<boolean>;
2862
+ protected readonly searchFailed: import("@angular/core").WritableSignal<boolean>;
2863
+ private readonly _results;
2864
+ private readonly _criteria;
2865
+ protected readonly chosen: import("@angular/core").WritableSignal<PhoneNumberOption | null>;
2866
+ private readonly _value;
2867
+ private readonly _length;
2868
+ protected readonly valid: import("@angular/core").Signal<boolean>;
2869
+ private readonly _fieldName;
2870
+ /** Own "wrong length" message (empty is left to the input's built-in "is required"); only once the field was touched. */
2871
+ protected readonly fieldError: import("@angular/core").Signal<string>;
2872
+ protected readonly visibleResults: import("@angular/core").Signal<PhoneNumberOption[]>;
2873
+ protected readonly primaryLabel: import("@angular/core").Signal<string>;
2874
+ protected readonly changeLabel: import("@angular/core").Signal<"Change area code" | "Change ZIP code">;
2875
+ protected readonly emptyTitle: import("@angular/core").Signal<string>;
2876
+ protected readonly criteriaText: import("@angular/core").Signal<string>;
2877
+ protected readonly announcement: import("@angular/core").Signal<string>;
2878
+ private readonly _destroyRef;
2879
+ private readonly _injector;
2880
+ private readonly _el;
2881
+ private _searchSub;
2882
+ constructor();
2883
+ protected onPrimary(): void;
2884
+ protected onEnter(event: Event): void;
2885
+ /** Typing a non-digit is blocked before it reaches the field. */
2886
+ protected onBeforeInput(event: Event): void;
2887
+ /** A pasted ZIP+4 / formatted text keeps its first 5 (or 3) digits and replaces the field's text. */
2888
+ protected onPaste(event: Event): void;
2889
+ protected submit(): void;
2890
+ protected retry(): void;
2891
+ protected choose(option: PhoneNumberOption): void;
2892
+ protected changeLocation(): void;
2893
+ protected backToResults(): void;
2894
+ protected confirm(): void;
2895
+ protected close(): void;
2896
+ private _run;
2897
+ private _abort;
2898
+ private _reset;
2899
+ /** A user edit: sanitise and write back into the field when something was stripped. */
2900
+ private _typed;
2901
+ private _write;
2902
+ /** Moves focus to the first control of the step that was just shown. */
2903
+ private _focusStep;
2904
+ static ɵfac: i0.ɵɵFactoryDeclaration<PhoneNumberPickerComponent, never>;
2905
+ static ɵcmp: i0.ɵɵComponentDeclaration<PhoneNumberPickerComponent, "ui-phone-number-picker", never, {
2906
+ "open": {
2907
+ "alias": "open";
2908
+ "required": false;
2909
+ "isSignal": true;
2910
+ };
2911
+ "title": {
2912
+ "alias": "title";
2913
+ "required": false;
2914
+ "isSignal": true;
2915
+ };
2916
+ "subtitle": {
2917
+ "alias": "subtitle";
2918
+ "required": false;
2919
+ "isSignal": true;
2920
+ };
2921
+ "defaultPostalCode": {
2922
+ "alias": "defaultPostalCode";
2923
+ "required": false;
2924
+ "isSignal": true;
2925
+ };
2926
+ "defaultMode": {
2927
+ "alias": "defaultMode";
2928
+ "required": false;
2929
+ "isSignal": true;
2930
+ };
2931
+ "allowAreaCode": {
2932
+ "alias": "allowAreaCode";
2933
+ "required": false;
2934
+ "isSignal": true;
2935
+ };
2936
+ "search": {
2937
+ "alias": "search";
2938
+ "required": true;
2939
+ "isSignal": true;
2940
+ };
2941
+ "costNotice": {
2942
+ "alias": "costNotice";
2943
+ "required": false;
2944
+ "isSignal": true;
2945
+ };
2946
+ "confirmLabel": {
2947
+ "alias": "confirmLabel";
2948
+ "required": false;
2949
+ "isSignal": true;
2950
+ };
2951
+ "maxResults": {
2952
+ "alias": "maxResults";
2953
+ "required": false;
2954
+ "isSignal": true;
2955
+ };
2956
+ }, {
2957
+ "closed": "closed";
2958
+ "selected": "selected";
2959
+ }, never, never, true, never>;
2960
+ }
2961
+ export declare class ButtonComponent {
2962
+ readonly variant: import("@angular/core").InputSignal<"primary" | "secondary" | "danger" | "warning" | "link" | "ghost" | "tab" | "icon" | "icon-danger">;
2963
+ /** Renders an anchor to this route instead of a button (use with `variant="link"` for an inline text link). `['/settings']` or `'/settings'`. */
2964
+ readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
2965
+ /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
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>;
2981
+ readonly color: import("@angular/core").InputSignal<string | undefined>;
2982
+ readonly active: import("@angular/core").InputSignal<boolean>;
2983
+ readonly size: import("@angular/core").InputSignal<"sm" | "md" | "xs" | "lg">;
2984
+ readonly type: import("@angular/core").InputSignal<"reset" | "submit" | "button">;
2985
+ readonly disabled: import("@angular/core").InputSignal<boolean>;
2986
+ readonly loading: import("@angular/core").InputSignal<boolean>;
2987
+ readonly fullWidth: import("@angular/core").InputSignal<boolean>;
2988
+ readonly ariaLabel: import("@angular/core").InputSignal<string | undefined>;
2989
+ readonly ariaExpanded: import("@angular/core").InputSignal<boolean | undefined>;
2990
+ readonly ariaPressed: import("@angular/core").InputSignal<boolean | undefined>;
2991
+ /** Id of the element that describes this button (an error or hint rendered next to it), e.g. `<ui-error errorId="x">`. Absent when empty. */
2992
+ readonly ariaDescribedBy: import("@angular/core").InputSignal<string | undefined>;
2993
+ _footerLeft?: string;
2994
+ _footerRight?: string;
2995
+ static ɵfac: i0.ɵɵFactoryDeclaration<ButtonComponent, never>;
2996
+ static ɵcmp: i0.ɵɵComponentDeclaration<ButtonComponent, "ui-button", never, {
2997
+ "variant": {
2998
+ "alias": "variant";
2999
+ "required": false;
3000
+ "isSignal": true;
3001
+ };
3002
+ "routerLink": {
3003
+ "alias": "routerLink";
3004
+ "required": false;
3005
+ "isSignal": true;
3006
+ };
3007
+ "queryParams": {
3008
+ "alias": "queryParams";
3009
+ "required": false;
3010
+ "isSignal": true;
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
+ };
3027
+ "color": {
3028
+ "alias": "color";
3029
+ "required": false;
3030
+ "isSignal": true;
3031
+ };
3032
+ "active": {
3033
+ "alias": "active";
3034
+ "required": false;
3035
+ "isSignal": true;
3036
+ };
3037
+ "size": {
3038
+ "alias": "size";
3039
+ "required": false;
3040
+ "isSignal": true;
3041
+ };
3042
+ "type": {
3043
+ "alias": "type";
3044
+ "required": false;
3045
+ "isSignal": true;
3046
+ };
3047
+ "disabled": {
3048
+ "alias": "disabled";
3049
+ "required": false;
3050
+ "isSignal": true;
3051
+ };
3052
+ "loading": {
3053
+ "alias": "loading";
3054
+ "required": false;
3055
+ "isSignal": true;
3056
+ };
3057
+ "fullWidth": {
3058
+ "alias": "fullWidth";
3059
+ "required": false;
3060
+ "isSignal": true;
3061
+ };
3062
+ "ariaLabel": {
3063
+ "alias": "ariaLabel";
3064
+ "required": false;
3065
+ "isSignal": true;
3066
+ };
3067
+ "ariaExpanded": {
3068
+ "alias": "ariaExpanded";
3069
+ "required": false;
3070
+ "isSignal": true;
3071
+ };
3072
+ "ariaPressed": {
3073
+ "alias": "ariaPressed";
3074
+ "required": false;
3075
+ "isSignal": true;
3076
+ };
3077
+ "ariaDescribedBy": {
3078
+ "alias": "ariaDescribedBy";
3079
+ "required": false;
3080
+ "isSignal": true;
3081
+ };
3082
+ "_footerLeft": {
3083
+ "alias": "modal-footer-left";
3084
+ "required": false;
3085
+ };
3086
+ "_footerRight": {
3087
+ "alias": "modal-footer-right";
3088
+ "required": false;
3089
+ };
3090
+ }, {}, never, ["*"], true, never>;
3091
+ }
3092
+ export declare class ToggleBadgeComponent {
3093
+ readonly label: import("@angular/core").InputSignal<string>;
3094
+ readonly active: import("@angular/core").InputSignal<boolean>;
3095
+ readonly toggle: import("@angular/core").OutputEmitterRef<void>;
3096
+ static ɵfac: i0.ɵɵFactoryDeclaration<ToggleBadgeComponent, never>;
3097
+ static ɵcmp: i0.ɵɵComponentDeclaration<ToggleBadgeComponent, "ui-toggle-badge", never, {
3098
+ "label": {
3099
+ "alias": "label";
3100
+ "required": false;
3101
+ "isSignal": true;
3102
+ };
3103
+ "active": {
3104
+ "alias": "active";
2274
3105
  "required": false;
2275
3106
  "isSignal": true;
2276
3107
  };
@@ -2962,115 +3793,6 @@ export declare class ComboboxComponent<T = unknown> implements ControlValueAcces
2962
3793
  "blurred": "blurred";
2963
3794
  }, never, never, true, never>;
2964
3795
  }
2965
- export interface RadioOption {
2966
- value: string;
2967
- label: string;
2968
- description?: string;
2969
- disabled?: boolean;
2970
- }
2971
- /**
2972
- * Group of native radio buttons inside a `<fieldset>` (+ `<legend>` = `label`), a ControlValueAccessor (string value, `''`
2973
- * = nothing selected). Arrow keys move and select natively because all radios share one `name`.
2974
- * Built-in validation: `required` -> "<label> is required."; a non-empty value that is not one of the `options` ->
2975
- * "Select a valid option." (not checked while `options` is empty). Disabled groups are never invalid.
2976
- *
2977
- * Validity API (same on every ui input): Angular `Validator`, signals `invalid` + `errorMessage`, `invalidChange` output,
2978
- * provides `UiFieldValidity`. Messages show after the group lost focus or while `[showErrors]="true"`; a host `[error]` wins.
2979
- */
2980
- export declare class RadioGroupComponent implements ControlValueAccessor, Validator, UiFieldValidity {
2981
- readonly label: import("@angular/core").InputSignal<string>;
2982
- readonly options: import("@angular/core").InputSignal<RadioOption[]>;
2983
- readonly required: import("@angular/core").InputSignal<boolean>;
2984
- readonly hint: import("@angular/core").InputSignal<string>;
2985
- readonly error: import("@angular/core").InputSignal<string>;
2986
- /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
2987
- readonly showErrors: import("@angular/core").InputSignal<boolean>;
2988
- readonly orientation: import("@angular/core").InputSignal<"vertical" | "horizontal">;
2989
- /** Native `name` shared by the radios; a unique one is generated when omitted. */
2990
- readonly name: import("@angular/core").InputSignal<string>;
2991
- protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
2992
- private readonly _cvaDisabled;
2993
- protected readonly disabled: import("@angular/core").Signal<boolean>;
2994
- private readonly _generatedName;
2995
- protected get _name(): string;
2996
- protected readonly _descId: string;
2997
- protected readonly _value: import("@angular/core").WritableSignal<string>;
2998
- private readonly _reveal;
2999
- private readonly _issue;
3000
- /** True while the value breaks a built-in rule or the host passed an `[error]`. */
3001
- readonly invalid: import("@angular/core").Signal<boolean>;
3002
- /** Message of the first broken built-in rule ('' when valid), shown or not. */
3003
- readonly errorMessage: import("@angular/core").Signal<string>;
3004
- /** Emits whenever the field becomes invalid / valid. */
3005
- readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
3006
- protected readonly _activeError: import("@angular/core").Signal<string>;
3007
- private _onValidatorChange;
3008
- constructor();
3009
- /** Re-run the form control's validation when a rule input (required, options) changes. */
3010
- ngOnChanges(): void;
3011
- validate(control: AbstractControl): ValidationErrors | null;
3012
- registerOnValidatorChange(fn: () => void): void;
3013
- private _onChange;
3014
- private _onTouched;
3015
- writeValue(val: string): void;
3016
- registerOnChange(fn: (v: string) => void): void;
3017
- registerOnTouched(fn: () => void): void;
3018
- setDisabledState(d: boolean): void;
3019
- protected onSelect(value: string): void;
3020
- /** Touched once focus leaves the whole group (moving between its radios does not count). */
3021
- protected onFocusOut(event: FocusEvent): void;
3022
- private _check;
3023
- static ɵfac: i0.ɵɵFactoryDeclaration<RadioGroupComponent, never>;
3024
- static ɵcmp: i0.ɵɵComponentDeclaration<RadioGroupComponent, "ui-radio-group", never, {
3025
- "label": {
3026
- "alias": "label";
3027
- "required": false;
3028
- "isSignal": true;
3029
- };
3030
- "options": {
3031
- "alias": "options";
3032
- "required": false;
3033
- "isSignal": true;
3034
- };
3035
- "required": {
3036
- "alias": "required";
3037
- "required": false;
3038
- "isSignal": true;
3039
- };
3040
- "hint": {
3041
- "alias": "hint";
3042
- "required": false;
3043
- "isSignal": true;
3044
- };
3045
- "error": {
3046
- "alias": "error";
3047
- "required": false;
3048
- "isSignal": true;
3049
- };
3050
- "showErrors": {
3051
- "alias": "showErrors";
3052
- "required": false;
3053
- "isSignal": true;
3054
- };
3055
- "orientation": {
3056
- "alias": "orientation";
3057
- "required": false;
3058
- "isSignal": true;
3059
- };
3060
- "name": {
3061
- "alias": "name";
3062
- "required": false;
3063
- "isSignal": true;
3064
- };
3065
- "_disabledInput": {
3066
- "alias": "disabled";
3067
- "required": false;
3068
- "isSignal": true;
3069
- };
3070
- }, {
3071
- "invalidChange": "invalidChange";
3072
- }, never, never, true, never>;
3073
- }
3074
3796
  /**
3075
3797
  * Types and pure helpers for the signer side of sending documents (docs/send-documents-contract.md, Addendum 2026-10-19).
3076
3798
  * Everything here is app-agnostic: the roles are `signer1 .. signer4`, labels / names come from the host.
@@ -3154,6 +3876,8 @@ export interface SignerDocLike {
3154
3876
  id: string;
3155
3877
  title: string;
3156
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;
3157
3881
  signerRoles?: SignerRole[];
3158
3882
  hasMapping?: boolean;
3159
3883
  hasDataFields?: boolean;
@@ -3175,6 +3899,14 @@ export interface DocumentSignerInfo {
3175
3899
  multiSigner: boolean;
3176
3900
  /** A document that is sent for signing requires the sender's countersignature. */
3177
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
+ }[];
3178
3910
  /** Several documents are chosen and one of them has several signers (it still goes to the one recipient). */
3179
3911
  multiDocumentNote: boolean;
3180
3912
  /** Under the document chip: what the mapping covers ('' when unknown). */
@@ -3218,6 +3950,8 @@ export declare function forwardContext(ctx: SendContext | null | undefined, type
3218
3950
  export declare function mergeFieldText(f: MergeCheckEmptyField): string;
3219
3951
  /** The warning lines of a merge-check answer: one line for one document, `<title>: …` per document for several. Nothing when nothing is empty. */
3220
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;
3221
3955
  /** What the sender wants done with the chosen documents. Same values as the backend (`ESIGN` | `ATTACH` | `BOTH`). */
3222
3956
  export type DocumentSendPurpose = 'ESIGN' | 'ATTACH' | 'BOTH';
3223
3957
  /** How the request reaches the recipient — derived from the channel: the Email tab sends `EMAIL`, the Text tab `TEXT`. */
@@ -3233,6 +3967,8 @@ export interface DocumentOption {
3233
3967
  hasFile: boolean;
3234
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". */
3235
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;
3236
3972
  /** Only with `documentSigners`: the document's customer roles from the `GET /documents` row (`signerRoles`). `[]` = no signature fields (informational). */
3237
3973
  signerRoles?: SignerRole[];
3238
3974
  /** The row says the document has saved placements of any kind (drives the "Fields placed for N signers" line and the merge check). */
@@ -3249,6 +3985,7 @@ export interface DocumentSeed {
3249
3985
  fileName?: string | null;
3250
3986
  hasFile?: boolean;
3251
3987
  requiresCountersign?: boolean;
3988
+ hasCountersignPlacement?: boolean;
3252
3989
  signerRoles?: SignerRole[];
3253
3990
  hasMapping?: boolean;
3254
3991
  hasDataFields?: boolean;
@@ -3273,8 +4010,27 @@ export interface DocumentPreviewTarget {
3273
4010
  title: string;
3274
4011
  fileName: string | null;
3275
4012
  }
3276
- /** Opens a document for preview (the host knows its own route / viewer). Called synchronously inside the click. */
3277
- export type DocumentPreviewHandler = (doc: DocumentPreviewTarget) => void;
4013
+ /** The recipient the panel currently holds (as the panel's `contactId` / `contactName` / `contactEmail` / `contactPhone` inputs). */
4014
+ export interface DocumentPreviewContact {
4015
+ id: string;
4016
+ name: string;
4017
+ email: string;
4018
+ phone: string;
4019
+ }
4020
+ /**
4021
+ * Second argument of `documentPreview`: who the document is being sent to, so the host can preview it filled for that recipient.
4022
+ * `contact` is `null` when the panel has no recipient (all four contact inputs blank); `sendContext` is the panel's `sendContext` input
4023
+ * as given (not filtered by `documentContextTypes`) or `null`.
4024
+ */
4025
+ export interface DocumentPreviewContext {
4026
+ contact: DocumentPreviewContact | null;
4027
+ sendContext: SendContext | null;
4028
+ }
4029
+ /**
4030
+ * Opens a document for preview (the host knows its own route / viewer). Called synchronously inside the click. The second argument
4031
+ * is new and optional to use: a handler that only declares `doc` keeps working.
4032
+ */
4033
+ export type DocumentPreviewHandler = (doc: DocumentPreviewTarget, context: DocumentPreviewContext) => void;
3278
4034
  /**
3279
4035
  * Turns on the "Expiry and Reminders" settings of the document section and sets their starting values (the keys you leave out start at
3280
4036
  * the defaults below). `null` / not passed = the settings are not shown and nothing about them is sent.
@@ -3402,6 +4158,10 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
3402
4158
  * (`window.open` after an await is blocked as a popup in Safari).
3403
4159
  */
3404
4160
  readonly documentPreview: import("@angular/core").InputSignal<DocumentPreviewHandler | null>;
4161
+ /** The recipient passed to `documentPreview` as `context.contact` (the send panel sets it from its contact inputs). `null` = none. */
4162
+ readonly previewContact: import("@angular/core").InputSignal<DocumentPreviewContact | null>;
4163
+ /** The send context passed to `documentPreview` as `context.sendContext` (the send panel sets it from its `sendContext`). `null` = none. */
4164
+ readonly previewSendContext: import("@angular/core").InputSignal<SendContext | null>;
3405
4165
  /**
3406
4166
  * Turns on the signer logic (`documentSigners` of the panel): a document's customer roles (from its row's `signerRoles`, or the config's
3407
4167
  * `resolveRoles`), informational documents (no signature fields), the one-signer DocuSign rule and the read-only countersign rule.
@@ -3415,7 +4175,7 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
3415
4175
  readonly envelope: import("@angular/core").InputSignal<DocumentEnvelopeTarget | null>;
3416
4176
  /** The document list is fixed: an existing envelope's. */
3417
4177
  readonly locked: import("@angular/core").Signal<boolean>;
3418
- protected readonly purposeOptions: import("@angular/core").Signal<SelectOption[]>;
4178
+ protected readonly purposeOptions: import("@angular/core").Signal<SelectOption<string>[]>;
3419
4179
  readonly search$: Subject<string>;
3420
4180
  readonly options: import("@angular/core").WritableSignal<ComboboxOption<DocumentOption>[]>;
3421
4181
  readonly loading: import("@angular/core").WritableSignal<boolean>;
@@ -3552,6 +4312,16 @@ export declare class DocumentSendOptionsComponent implements UiFieldValidity {
3552
4312
  "required": false;
3553
4313
  "isSignal": true;
3554
4314
  };
4315
+ "previewContact": {
4316
+ "alias": "previewContact";
4317
+ "required": false;
4318
+ "isSignal": true;
4319
+ };
4320
+ "previewSendContext": {
4321
+ "alias": "previewSendContext";
4322
+ "required": false;
4323
+ "isSignal": true;
4324
+ };
3555
4325
  "signers": {
3556
4326
  "alias": "signers";
3557
4327
  "required": false;
@@ -3829,6 +4599,8 @@ declare class SendEmailComponent implements OnInit {
3829
4599
  readonly docuSignConnected: import("@angular/core").InputSignal<boolean>;
3830
4600
  /** "Preview Document" button(s) in the Documents section call this; `null` hides them. */
3831
4601
  readonly documentPreview: import("@angular/core").InputSignal<DocumentPreviewHandler | null>;
4602
+ /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */
4603
+ readonly previewContact: import("@angular/core").Signal<DocumentPreviewContact | null>;
3832
4604
  /**
3833
4605
  * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting
3834
4606
  * from these defaults, and makes the video link carry them. `null` (default) = the attached video's plain share link is inserted, as before.
@@ -3841,6 +4613,11 @@ declare class SendEmailComponent implements OnInit {
3841
4613
  readonly validChange: import("@angular/core").OutputEmitterRef<boolean>;
3842
4614
  /** A document send was refused because the document / envelope changed meanwhile (404 / 409): the host refreshes its list. */
3843
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
+ }>;
3844
4621
  readonly EMAIL_DISABLED_MESSAGE = "Email sending is disabled, so this can't be sent. Enable it in Communication Settings.";
3845
4622
  mode: 'compose' | 'template';
3846
4623
  subject: string;
@@ -4147,6 +4924,7 @@ declare class SendEmailComponent implements OnInit {
4147
4924
  "emailAdded": "emailAdded";
4148
4925
  "validChange": "validChange";
4149
4926
  "rejected": "rejected";
4927
+ "mapFieldsClick": "mapFieldsClick";
4150
4928
  }, never, never, true, never>;
4151
4929
  }
4152
4930
  /** What the text form sends — the default `POST <baseUrl>/text-messages` body, or the host's `sendFn` argument. */
@@ -4214,6 +4992,8 @@ declare class SendTextComponent implements OnInit {
4214
4992
  readonly documentSigningOptions: import("@angular/core").InputSignal<DocumentSigningOptions | null | undefined>;
4215
4993
  /** "Preview Document" button(s) in the Documents section call this; `null` hides them. */
4216
4994
  readonly documentPreview: import("@angular/core").InputSignal<DocumentPreviewHandler | null>;
4995
+ /** The recipient handed to `documentPreview` (second argument): the panel's contact inputs, `null` while all four are blank. */
4996
+ readonly previewContact: import("@angular/core").Signal<DocumentPreviewContact | null>;
4217
4997
  /**
4218
4998
  * Turns on the video settings (Playback, Call-to-Action, Hide reactions, Watch Preview, Remove Video) once a video is attached, starting
4219
4999
  * from these defaults, and makes the video link carry them. `null` (default) = the plain share link is inserted, as before.
@@ -4588,6 +5368,15 @@ export declare class SendMessagePanelComponent {
4588
5368
  readonly emailAdded: import("@angular/core").OutputEmitterRef<string>;
4589
5369
  /** A document / envelope send was refused because it changed meanwhile (404 / 409): the host refreshes its list. */
4590
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
+ }>;
4591
5380
  readonly phoneAdded: import("@angular/core").OutputEmitterRef<string>;
4592
5381
  readonly sendEmailRef: import("@angular/core").Signal<SendEmailComponent | undefined>;
4593
5382
  readonly sendTextRef: import("@angular/core").Signal<SendTextComponent | undefined>;
@@ -4809,6 +5598,7 @@ export declare class SendMessagePanelComponent {
4809
5598
  "sent": "sent";
4810
5599
  "emailAdded": "emailAdded";
4811
5600
  "rejected": "rejected";
5601
+ "mapFieldsClick": "mapFieldsClick";
4812
5602
  "phoneAdded": "phoneAdded";
4813
5603
  }, never, never, true, never>;
4814
5604
  }
@@ -4835,14 +5625,27 @@ export declare class DocumentSignersComponent implements UiFieldValidity {
4835
5625
  /** The complete signer list (recipient first) or null while a role has no contact. Emitted on every change. */
4836
5626
  readonly signersChange: import("@angular/core").OutputEmitterRef<SendSigner[] | null>;
4837
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
+ }>;
4838
5633
  readonly picks: import("@angular/core").WritableSignal<Partial<Record<SignerRole, ContactLookup | null>>>;
4839
5634
  protected readonly rowsShown: import("@angular/core").Signal<boolean>;
4840
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
+ }[]>;
4841
5641
  readonly problems: import("@angular/core").Signal<Partial<Record<SignerRole, string>>>;
4842
5642
  readonly signers: import("@angular/core").Signal<SendSigner[] | null>;
4843
5643
  readonly errorMessage: import("@angular/core").Signal<string>;
4844
5644
  readonly invalid: import("@angular/core").Signal<boolean>;
4845
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;
4846
5649
  protected label(role: SignerRole): string;
4847
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`). */
4848
5651
  protected visibleProblem(role: SignerRole): string;
@@ -4877,6 +5680,7 @@ export declare class DocumentSignersComponent implements UiFieldValidity {
4877
5680
  }, {
4878
5681
  "signersChange": "signersChange";
4879
5682
  "invalidChange": "invalidChange";
5683
+ "mapFieldsClick": "mapFieldsClick";
4880
5684
  }, never, never, true, never>;
4881
5685
  }
4882
5686
  /** Quiet time after the last change before the check is requested. */
@@ -5099,10 +5903,16 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5099
5903
  /** Accessible name for a field with no visible label (e.g. an inline cell or title input). */
5100
5904
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
5101
5905
  readonly placeholder: import("@angular/core").InputSignal<string>;
5102
- 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">;
5103
5907
  readonly maxlength: import("@angular/core").InputSignal<number | undefined>;
5104
5908
  readonly required: import("@angular/core").InputSignal<boolean>;
5105
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">;
5106
5916
  readonly readonly: import("@angular/core").InputSignal<boolean>;
5107
5917
  readonly hint: import("@angular/core").InputSignal<string>;
5108
5918
  readonly error: import("@angular/core").InputSignal<string>;
@@ -5114,6 +5924,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5114
5924
  readonly max: import("@angular/core").InputSignal<number | undefined>;
5115
5925
  /** `type="number"` only: most digits allowed after the decimal point. */
5116
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">;
5117
5932
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
5118
5933
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
5119
5934
  readonly focused: import("@angular/core").OutputEmitterRef<void>;
@@ -5125,6 +5940,9 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5125
5940
  protected readonly disabled: import("@angular/core").Signal<boolean>;
5126
5941
  protected readonly _value: import("@angular/core").WritableSignal<string>;
5127
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;
5128
5946
  private readonly _formatting;
5129
5947
  private readonly _issue;
5130
5948
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -5140,13 +5958,15 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5140
5958
  constructor();
5141
5959
  /** Re-run the form control's validation when a rule input (required, maxlength, min, options, ...) changes. */
5142
5960
  ngOnChanges(): void;
5143
- writeValue(val: string | number | null): void;
5961
+ writeValue(val: string | number | null | undefined): void;
5144
5962
  validate(control: AbstractControl): ValidationErrors | null;
5145
5963
  registerOnValidatorChange(fn: () => void): void;
5146
- registerOnChange(fn: (v: string) => void): void;
5964
+ registerOnChange(fn: (v: string | number | null) => void): void;
5147
5965
  registerOnTouched(fn: () => void): void;
5148
5966
  setDisabledState(d: boolean): void;
5149
5967
  protected onInput(event: Event): void;
5968
+ /** Typed text -> number, or null when blank / not a finite number. */
5969
+ private static _parse;
5150
5970
  protected onFocus(): void;
5151
5971
  protected onBlur(): void;
5152
5972
  /** First broken built-in rule for `value`, or null. */
@@ -5191,6 +6011,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5191
6011
  "required": false;
5192
6012
  "isSignal": true;
5193
6013
  };
6014
+ "inputmode": {
6015
+ "alias": "inputmode";
6016
+ "required": false;
6017
+ "isSignal": true;
6018
+ };
5194
6019
  "readonly": {
5195
6020
  "alias": "readonly";
5196
6021
  "required": false;
@@ -5226,6 +6051,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5226
6051
  "required": false;
5227
6052
  "isSignal": true;
5228
6053
  };
6054
+ "valueType": {
6055
+ "alias": "valueType";
6056
+ "required": false;
6057
+ "isSignal": true;
6058
+ };
5229
6059
  "showErrors": {
5230
6060
  "alias": "showErrors";
5231
6061
  "required": false;
@@ -5252,6 +6082,12 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5252
6082
  readonly required: import("@angular/core").InputSignal<boolean>;
5253
6083
  readonly readonly: import("@angular/core").InputSignal<boolean>;
5254
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>;
5255
6091
  readonly error: import("@angular/core").InputSignal<string>;
5256
6092
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
5257
6093
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
@@ -5261,6 +6097,11 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5261
6097
  protected readonly _value: import("@angular/core").WritableSignal<string>;
5262
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. */
5263
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>;
5264
6105
  private readonly _reveal;
5265
6106
  private readonly _issue;
5266
6107
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -5328,6 +6169,11 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5328
6169
  "required": false;
5329
6170
  "isSignal": true;
5330
6171
  };
6172
+ "hintAsDescription": {
6173
+ "alias": "hintAsDescription";
6174
+ "required": false;
6175
+ "isSignal": true;
6176
+ };
5331
6177
  "error": {
5332
6178
  "alias": "error";
5333
6179
  "required": false;
@@ -5347,47 +6193,142 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
5347
6193
  "invalidChange": "invalidChange";
5348
6194
  }, never, never, true, never>;
5349
6195
  }
6196
+ /** Why a chosen file was refused. */
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';
6199
+ /** A refused file with the message the field shows (also emitted by `(fileRejected)`). */
6200
+ export interface FileRejection {
6201
+ file: File;
6202
+ reason: FileRejectReason;
6203
+ message: string;
6204
+ }
6205
+ /** "1.5 MB", "820 KB", "12 B" (binary units, one decimal for MB and above). */
6206
+ export declare function formatFileSize(bytes: number): string;
6207
+ /** True when `file` matches one token of a native `accept` string (`.pdf`, `image/png`, `image/*`). Empty `accept` accepts everything. */
6208
+ export declare function fileMatchesAccept(file: File, accept: string): boolean;
6209
+ /** "PDF", "PNG, JPEG and WEBP" from an `accept` string, for the default type message. */
6210
+ export declare function acceptLabel(accept: string): string;
5350
6211
  /**
5351
6212
  * File picker styled like the other `ui-*` inputs (label, hint, required marker, error, disabled).
5352
6213
  * Value (ngModel / forms) is `File | null`, or `File[]` when `multiple` is set.
5353
6214
  *
5354
6215
  * Set `[showField]="false"` for a picker with no visible field and call `open()` from the host
5355
6216
  * (e.g. an "Attach document" menu action). `(filesSelected)` fires in both modes.
6217
+ *
6218
+ * Built in (all opt-in, nothing changes unless an input is set):
6219
+ * - `clearable`: a Remove button next to the chosen file; `(fileRemoved)` fires after it clears the selection.
6220
+ * - `accept` is enforced on picked AND dropped files (`enforceAccept`, on by default), `maxSize` (bytes) refuses bigger
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`
6222
+ * override the wording, also in picker-only mode), and `(fileRejected)` emits `{ file, reason, message }`.
6223
+ * - `required` is a real rule: Angular `Validator` + `UiFieldValidity` (`invalid`, `errorMessage`, `invalidChange`,
6224
+ * `showErrors`), like the other inputs. The message shows after the field was used or `[showErrors]="true"`.
6225
+ * - `dropzone`: the field accepts files dropped on it.
6226
+ * - `showDetails`: file size after the name; `preview`: a thumbnail of a chosen image.
6227
+ * - `loading` (+ `loadingText`): a `ui-skeleton` replaces the file name while the host works on the file (scanning, uploading).
5356
6228
  */
5357
- export declare class FileInputComponent implements ControlValueAccessor {
6229
+ export declare class FileInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {
5358
6230
  readonly label: import("@angular/core").InputSignal<string>;
5359
6231
  /** Accessible name for a field with no visible label. */
5360
6232
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
5361
6233
  readonly placeholder: import("@angular/core").InputSignal<string>;
5362
6234
  readonly buttonLabel: import("@angular/core").InputSignal<string>;
5363
- /** Native `accept` string, e.g. ".pdf,.png". */
6235
+ /** Native `accept` string, e.g. ".pdf,.png". Also enforced on picked and dropped files unless `enforceAccept` is false. */
5364
6236
  readonly accept: import("@angular/core").InputSignal<string>;
5365
6237
  readonly multiple: import("@angular/core").InputSignal<boolean>;
6238
+ /** A file must be chosen (a real rule: Validator + UiFieldValidity, message shown after use or `showErrors`). */
5366
6239
  readonly required: import("@angular/core").InputSignal<boolean>;
5367
6240
  readonly hint: import("@angular/core").InputSignal<string>;
6241
+ /** Host-provided error message (wins over the built-in ones). Shown under a visible field only. */
5368
6242
  readonly error: import("@angular/core").InputSignal<string>;
5369
6243
  /** false = no visible field; open the picker from the host with `open()`. */
5370
6244
  readonly showField: import("@angular/core").InputSignal<boolean>;
5371
- /** Emits the chosen files (one or many) each time the user picks. */
6245
+ /** Reveal the built-in "required" message now (e.g. after a failed Save click). */
6246
+ readonly showErrors: import("@angular/core").InputSignal<boolean>;
6247
+ /** Show a Remove button next to the chosen file. */
6248
+ readonly clearable: import("@angular/core").InputSignal<boolean>;
6249
+ readonly removeLabel: import("@angular/core").InputSignal<string>;
6250
+ /** Largest allowed file in bytes; a bigger file is refused. null = no limit. */
6251
+ readonly maxSize: import("@angular/core").InputSignal<number | null>;
6252
+ /** Refuse files that do not match `accept` (picked or dropped). */
6253
+ readonly enforceAccept: import("@angular/core").InputSignal<boolean>;
6254
+ /** Replaces the built-in "Only PDF files are allowed." message. */
6255
+ readonly typeMessage: import("@angular/core").InputSignal<string>;
6256
+ /** Replaces the built-in "The file must be 5 MB or smaller." message. */
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>;
6266
+ /** The field accepts files dropped on it. */
6267
+ readonly dropzone: import("@angular/core").InputSignal<boolean>;
6268
+ /** Show the file size after the name. */
6269
+ readonly showDetails: import("@angular/core").InputSignal<boolean>;
6270
+ /** Show a thumbnail when the chosen file is an image. */
6271
+ readonly preview: import("@angular/core").InputSignal<boolean>;
6272
+ /** The host is working on the file (scanning, uploading): a skeleton replaces the name and Browse / Remove are disabled. */
6273
+ readonly loading: import("@angular/core").InputSignal<boolean>;
6274
+ /** Screen-reader text while `loading` (default "Processing…"). */
6275
+ readonly loadingText: import("@angular/core").InputSignal<string>;
6276
+ /** Emits the chosen (accepted) files (one or many) each time the user picks or drops. */
5372
6277
  readonly filesSelected: import("@angular/core").OutputEmitterRef<File[]>;
6278
+ /** A picked or dropped file was refused (wrong type or too big). */
6279
+ readonly fileRejected: import("@angular/core").OutputEmitterRef<FileRejection>;
6280
+ /** The Remove button cleared the selection. */
6281
+ readonly fileRemoved: import("@angular/core").OutputEmitterRef<void>;
6282
+ /** Emits whenever the field becomes invalid / valid. */
6283
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
5373
6284
  protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
5374
6285
  private readonly _cvaDisabled;
5375
6286
  protected readonly disabled: import("@angular/core").Signal<boolean>;
5376
6287
  protected readonly _files: import("@angular/core").WritableSignal<File[]>;
5377
6288
  protected readonly _inputId: string;
5378
6289
  private readonly fileEl;
6290
+ protected readonly _dragging: import("@angular/core").WritableSignal<boolean>;
6291
+ /** Message of the last refused file ('' when the last pick was fine); shown in every mode. */
6292
+ private readonly _rejection;
6293
+ private readonly _reveal;
6294
+ private readonly _issue;
6295
+ /** True while a required field is empty, the last pick was refused, or the host passed an `[error]`. */
6296
+ readonly invalid: import("@angular/core").Signal<boolean>;
6297
+ /** Message of the first broken built-in rule ('' when valid), shown or not. */
6298
+ readonly errorMessage: import("@angular/core").Signal<string>;
6299
+ /** What the field shows: the host error (visible field only), a refused file, or the revealed built-in message. */
6300
+ protected readonly _shownError: import("@angular/core").Signal<string>;
5379
6301
  protected readonly summary: import("@angular/core").Signal<string>;
6302
+ /** Object URL of the chosen image (single file) while `preview` is on. */
6303
+ protected readonly _previewUrl: import("@angular/core").WritableSignal<string | null>;
6304
+ private _urlToRevoke;
6305
+ private _onValidatorChange;
5380
6306
  protected _onChange: (_: File | File[] | null) => void;
5381
6307
  protected _onTouched: () => void;
6308
+ constructor();
6309
+ private revokePreview;
6310
+ /** Re-run the form control's validation when a rule input changes. */
6311
+ ngOnChanges(): void;
6312
+ validate(_control: AbstractControl): ValidationErrors | null;
6313
+ registerOnValidatorChange(fn: () => void): void;
5382
6314
  /** Opens the native file dialog. */
5383
6315
  open(): void;
5384
- /** Clears the selection (and the native input, so the same file can be picked again). */
6316
+ /** Clears the selection (and the native input, so the same file can be picked again) and any "refused file" message. */
5385
6317
  clear(): void;
6318
+ /** The Remove button: clears, then tells the host. */
6319
+ protected remove(): void;
5386
6320
  writeValue(val: File | File[] | null): void;
5387
6321
  registerOnChange(fn: (v: File | File[] | null) => void): void;
5388
6322
  registerOnTouched(fn: () => void): void;
5389
6323
  setDisabledState(d: boolean): void;
6324
+ protected _touch(): void;
5390
6325
  protected onChange(event: Event): void;
6326
+ protected onDragOver(event: DragEvent): void;
6327
+ protected onDragLeave(event: DragEvent): void;
6328
+ protected onDrop(event: DragEvent): void;
6329
+ private rejectionFor;
6330
+ /** Applies picked / dropped files: refused ones raise the message and `fileRejected`, accepted ones become the value. */
6331
+ private accept$;
5391
6332
  static ɵfac: i0.ɵɵFactoryDeclaration<FileInputComponent, never>;
5392
6333
  static ɵcmp: i0.ɵɵComponentDeclaration<FileInputComponent, "ui-file-input", never, {
5393
6334
  "label": {
@@ -5395,48 +6336,128 @@ export declare class FileInputComponent implements ControlValueAccessor {
5395
6336
  "required": false;
5396
6337
  "isSignal": true;
5397
6338
  };
5398
- "ariaLabel": {
5399
- "alias": "ariaLabel";
6339
+ "ariaLabel": {
6340
+ "alias": "ariaLabel";
6341
+ "required": false;
6342
+ "isSignal": true;
6343
+ };
6344
+ "placeholder": {
6345
+ "alias": "placeholder";
6346
+ "required": false;
6347
+ "isSignal": true;
6348
+ };
6349
+ "buttonLabel": {
6350
+ "alias": "buttonLabel";
6351
+ "required": false;
6352
+ "isSignal": true;
6353
+ };
6354
+ "accept": {
6355
+ "alias": "accept";
6356
+ "required": false;
6357
+ "isSignal": true;
6358
+ };
6359
+ "multiple": {
6360
+ "alias": "multiple";
6361
+ "required": false;
6362
+ "isSignal": true;
6363
+ };
6364
+ "required": {
6365
+ "alias": "required";
6366
+ "required": false;
6367
+ "isSignal": true;
6368
+ };
6369
+ "hint": {
6370
+ "alias": "hint";
6371
+ "required": false;
6372
+ "isSignal": true;
6373
+ };
6374
+ "error": {
6375
+ "alias": "error";
6376
+ "required": false;
6377
+ "isSignal": true;
6378
+ };
6379
+ "showField": {
6380
+ "alias": "showField";
6381
+ "required": false;
6382
+ "isSignal": true;
6383
+ };
6384
+ "showErrors": {
6385
+ "alias": "showErrors";
6386
+ "required": false;
6387
+ "isSignal": true;
6388
+ };
6389
+ "clearable": {
6390
+ "alias": "clearable";
6391
+ "required": false;
6392
+ "isSignal": true;
6393
+ };
6394
+ "removeLabel": {
6395
+ "alias": "removeLabel";
6396
+ "required": false;
6397
+ "isSignal": true;
6398
+ };
6399
+ "maxSize": {
6400
+ "alias": "maxSize";
5400
6401
  "required": false;
5401
6402
  "isSignal": true;
5402
6403
  };
5403
- "placeholder": {
5404
- "alias": "placeholder";
6404
+ "enforceAccept": {
6405
+ "alias": "enforceAccept";
5405
6406
  "required": false;
5406
6407
  "isSignal": true;
5407
6408
  };
5408
- "buttonLabel": {
5409
- "alias": "buttonLabel";
6409
+ "typeMessage": {
6410
+ "alias": "typeMessage";
5410
6411
  "required": false;
5411
6412
  "isSignal": true;
5412
6413
  };
5413
- "accept": {
5414
- "alias": "accept";
6414
+ "sizeMessage": {
6415
+ "alias": "sizeMessage";
5415
6416
  "required": false;
5416
6417
  "isSignal": true;
5417
6418
  };
5418
- "multiple": {
5419
- "alias": "multiple";
6419
+ "rejectEmpty": {
6420
+ "alias": "rejectEmpty";
5420
6421
  "required": false;
5421
6422
  "isSignal": true;
5422
6423
  };
5423
- "required": {
5424
- "alias": "required";
6424
+ "emptyMessage": {
6425
+ "alias": "emptyMessage";
5425
6426
  "required": false;
5426
6427
  "isSignal": true;
5427
6428
  };
5428
- "hint": {
5429
- "alias": "hint";
6429
+ "minSize": {
6430
+ "alias": "minSize";
5430
6431
  "required": false;
5431
6432
  "isSignal": true;
5432
6433
  };
5433
- "error": {
5434
- "alias": "error";
6434
+ "minSizeMessage": {
6435
+ "alias": "minSizeMessage";
5435
6436
  "required": false;
5436
6437
  "isSignal": true;
5437
6438
  };
5438
- "showField": {
5439
- "alias": "showField";
6439
+ "dropzone": {
6440
+ "alias": "dropzone";
6441
+ "required": false;
6442
+ "isSignal": true;
6443
+ };
6444
+ "showDetails": {
6445
+ "alias": "showDetails";
6446
+ "required": false;
6447
+ "isSignal": true;
6448
+ };
6449
+ "preview": {
6450
+ "alias": "preview";
6451
+ "required": false;
6452
+ "isSignal": true;
6453
+ };
6454
+ "loading": {
6455
+ "alias": "loading";
6456
+ "required": false;
6457
+ "isSignal": true;
6458
+ };
6459
+ "loadingText": {
6460
+ "alias": "loadingText";
5440
6461
  "required": false;
5441
6462
  "isSignal": true;
5442
6463
  };
@@ -5447,6 +6468,9 @@ export declare class FileInputComponent implements ControlValueAccessor {
5447
6468
  };
5448
6469
  }, {
5449
6470
  "filesSelected": "filesSelected";
6471
+ "fileRejected": "fileRejected";
6472
+ "fileRemoved": "fileRemoved";
6473
+ "invalidChange": "invalidChange";
5450
6474
  }, never, never, true, never>;
5451
6475
  }
5452
6476
  export declare class DateInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {
@@ -5718,7 +6742,7 @@ export declare class TimeInputComponent implements ControlValueAccessor, Validat
5718
6742
  export declare class MultiSelectComponent implements ControlValueAccessor {
5719
6743
  readonly label: import("@angular/core").InputSignal<string>;
5720
6744
  readonly placeholder: import("@angular/core").InputSignal<string>;
5721
- readonly options: import("@angular/core").InputSignal<SelectOption[]>;
6745
+ readonly options: import("@angular/core").InputSignal<SelectOption<string>[]>;
5722
6746
  readonly searchable: import("@angular/core").InputSignal<boolean>;
5723
6747
  readonly searchPlaceholder: import("@angular/core").InputSignal<string>;
5724
6748
  readonly required: import("@angular/core").InputSignal<boolean>;
@@ -5734,8 +6758,8 @@ export declare class MultiSelectComponent implements ControlValueAccessor {
5734
6758
  protected readonly _open: import("@angular/core").WritableSignal<boolean>;
5735
6759
  protected readonly _query: import("@angular/core").WritableSignal<string>;
5736
6760
  protected readonly _selectedSet: import("@angular/core").Signal<Set<string>>;
5737
- protected readonly _selectedOptions: import("@angular/core").Signal<SelectOption[]>;
5738
- 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>[]>;
5739
6763
  private _onChange;
5740
6764
  private _onTouched;
5741
6765
  writeValue(val: string[] | null): void;
@@ -5953,7 +6977,8 @@ export declare class FormRowComponent {
5953
6977
  /**
5954
6978
  * Fixed-layout list item with an optional leading radio/checkbox, a title + badge,
5955
6979
  * and a secondary detail row rendered from a string array. All layout is handled
5956
- * 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).
5957
6982
  *
5958
6983
  * Usage:
5959
6984
  * <ui-list-item
@@ -5967,6 +6992,16 @@ export declare class FormRowComponent {
5967
6992
  * badgeVariant="green"
5968
6993
  * [details]="[c.email, c.phone, 'Added ' + (c.createdAt | date:'MMM d, y')]"
5969
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>
5970
7005
  */
5971
7006
  export declare class ListItemComponent {
5972
7007
  /** Show a radio button, checkbox, or nothing on the left. */
@@ -5981,10 +7016,24 @@ export declare class ListItemComponent {
5981
7016
  readonly highlighted: import("@angular/core").InputSignal<boolean>;
5982
7017
  /** Wraps the item in a bordered card (border + border-radius). */
5983
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>;
5984
7033
  get _borderedClass(): boolean;
5985
7034
  /** Primary label. */
5986
7035
  readonly title: import("@angular/core").InputSignal<string>;
5987
- /** 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. */
5988
7037
  readonly badge: import("@angular/core").InputSignal<string | null>;
5989
7038
  readonly badgeVariant: import("@angular/core").InputSignal<BadgeVariant>;
5990
7039
  /**
@@ -6028,6 +7077,21 @@ export declare class ListItemComponent {
6028
7077
  "required": false;
6029
7078
  "isSignal": true;
6030
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
+ };
6031
7095
  "title": {
6032
7096
  "alias": "title";
6033
7097
  "required": false;
@@ -6050,7 +7114,227 @@ export declare class ListItemComponent {
6050
7114
  };
6051
7115
  }, {
6052
7116
  "select": "select";
6053
- }, 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>;
6054
7338
  }
6055
7339
  /**
6056
7340
  * QR code for a URL (or any text), with a "Download QR Code" button (PNG). Projected content
@@ -6340,6 +7624,10 @@ export declare const errorContextInterceptor: HttpInterceptorFn;
6340
7624
  *
6341
7625
  * The fallback names what the user was doing: pass `action` explicitly ('send', 'export', …), or
6342
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.".
6343
7631
  */
6344
7632
  export declare function getErrorMessage(error: unknown, fallbackContext?: string, action?: ErrorAction): string;
6345
7633
  export interface UsStateOption {
@@ -6499,84 +7787,252 @@ export declare class AlertComponent {
6499
7787
  "required": false;
6500
7788
  "isSignal": true;
6501
7789
  };
6502
- "title": {
6503
- "alias": "title";
7790
+ "title": {
7791
+ "alias": "title";
7792
+ "required": false;
7793
+ "isSignal": true;
7794
+ };
7795
+ "dismissible": {
7796
+ "alias": "dismissible";
7797
+ "required": false;
7798
+ "isSignal": true;
7799
+ };
7800
+ }, {
7801
+ "dismissed": "dismissed";
7802
+ }, never, ["*"], true, never>;
7803
+ }
7804
+ /**
7805
+ * Small muted helper text shown under a form or panel section.
7806
+ *
7807
+ * <ui-hint>This does not affect the scheduled send date.</ui-hint>
7808
+ */
7809
+ export declare class HintComponent {
7810
+ static ɵfac: i0.ɵɵFactoryDeclaration<HintComponent, never>;
7811
+ static ɵcmp: i0.ɵɵComponentDeclaration<HintComponent, "ui-hint", never, {}, {}, never, ["*"], true, never>;
7812
+ }
7813
+ /** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
7814
+ export declare function iconPath(name: string): string | undefined;
7815
+ /** Keys with an icon of their own (the data-table action keys are accepted too — see `builtInActionIcon`). */
7816
+ export type IconName = 'add' | 'close' | 'check' | 'upload' | 'image' | 'chevron-up' | 'chevron-down' | 'arrow-left' | 'bell' | 'print' | 'view' | 'edit' | 'delete' | 'download' | 'duplicate' | 'archive' | 'restore' | 'share' | 'email' | 'message' | 'call';
7817
+ /**
7818
+ * A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
7819
+ * `iconClick`, and the host does the actual work (delete the row, open a panel, …).
7820
+ *
7821
+ * <ui-icon name="delete" tooltip="Remove line item 1" variant="danger" (iconClick)="removeLineItem(i)" />
7822
+ *
7823
+ * `tooltip` is the text shown on hover / keyboard focus (any length) and, unless `ariaLabel` is set, the name
7824
+ * assistive tech reads — always set it on a clickable icon. An unknown `name` draws nothing.
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
+ *
7829
+ * `[interactive]="false"` draws just the picture (no button, no click) for an icon that sits next to text or inside another
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.
7831
+ *
7832
+ * <ui-button><ui-icon name="arrow-left" [interactive]="false" /> Back to Dashboard</ui-button>
7833
+ */
7834
+ export declare class IconComponent {
7835
+ /** Which icon to draw, e.g. `delete`, `edit`, `add`. */
7836
+ readonly name: import("@angular/core").InputSignal<IconName | (string & {})>;
7837
+ /** Hover / keyboard-focus tooltip text, e.g. "Remove line item 1". Any length. Also the accessible name unless `ariaLabel` is set. */
7838
+ readonly tooltip: import("@angular/core").InputSignal<string>;
7839
+ /** Screen-reader name. Leave empty to use `tooltip`; set it only when it should differ. */
7840
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
7841
+ /** Icon size in px. */
7842
+ readonly size: import("@angular/core").InputSignal<number>;
7843
+ readonly variant: import("@angular/core").InputSignal<"danger" | "default">;
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>;
7847
+ /** false = just the picture (no button, no click) — for an icon beside text or inside another button. */
7848
+ readonly interactive: import("@angular/core").InputSignal<boolean>;
7849
+ /** Fires on click (not while disabled); the host performs the action. */
7850
+ readonly iconClick: import("@angular/core").OutputEmitterRef<MouseEvent>;
7851
+ protected readonly accessibleName: import("@angular/core").Signal<string>;
7852
+ protected readonly path: import("@angular/core").Signal<string | undefined>;
7853
+ protected onClick(event: MouseEvent): void;
7854
+ static ɵfac: i0.ɵɵFactoryDeclaration<IconComponent, never>;
7855
+ static ɵcmp: i0.ɵɵComponentDeclaration<IconComponent, "ui-icon", never, {
7856
+ "name": {
7857
+ "alias": "name";
7858
+ "required": true;
7859
+ "isSignal": true;
7860
+ };
7861
+ "tooltip": {
7862
+ "alias": "tooltip";
7863
+ "required": false;
7864
+ "isSignal": true;
7865
+ };
7866
+ "ariaLabel": {
7867
+ "alias": "ariaLabel";
7868
+ "required": false;
7869
+ "isSignal": true;
7870
+ };
7871
+ "size": {
7872
+ "alias": "size";
7873
+ "required": false;
7874
+ "isSignal": true;
7875
+ };
7876
+ "variant": {
7877
+ "alias": "variant";
7878
+ "required": false;
7879
+ "isSignal": true;
7880
+ };
7881
+ "disabled": {
7882
+ "alias": "disabled";
7883
+ "required": false;
7884
+ "isSignal": true;
7885
+ };
7886
+ "loading": {
7887
+ "alias": "loading";
7888
+ "required": false;
7889
+ "isSignal": true;
7890
+ };
7891
+ "interactive": {
7892
+ "alias": "interactive";
7893
+ "required": false;
7894
+ "isSignal": true;
7895
+ };
7896
+ }, {
7897
+ "iconClick": "iconClick";
7898
+ }, never, never, true, never>;
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";
6504
7999
  "required": false;
6505
8000
  "isSignal": true;
6506
8001
  };
6507
- "dismissible": {
6508
- "alias": "dismissible";
8002
+ "itemLabel": {
8003
+ "alias": "itemLabel";
6509
8004
  "required": false;
6510
8005
  "isSignal": true;
6511
8006
  };
6512
- }, {
6513
- "dismissed": "dismissed";
6514
- }, never, ["*"], true, never>;
6515
- }
6516
- /**
6517
- * Small muted helper text shown under a form or panel section.
6518
- *
6519
- * <ui-hint>This does not affect the scheduled send date.</ui-hint>
6520
- */
6521
- export declare class HintComponent {
6522
- static ɵfac: i0.ɵɵFactoryDeclaration<HintComponent, never>;
6523
- static ɵcmp: i0.ɵɵComponentDeclaration<HintComponent, "ui-hint", never, {}, {}, never, ["*"], true, never>;
6524
- }
6525
- /** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
6526
- export declare function iconPath(name: string): string | undefined;
6527
- /** Keys with an icon of their own (the data-table action keys are accepted too — see `builtInActionIcon`). */
6528
- export type IconName = 'add' | 'close' | 'check' | 'upload' | 'image' | 'chevron-up' | 'chevron-down' | 'view' | 'edit' | 'delete' | 'download' | 'duplicate' | 'archive' | 'restore' | 'share' | 'email' | 'message' | 'call';
6529
- /**
6530
- * A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
6531
- * `iconClick`, and the host does the actual work (delete the row, open a panel, …).
6532
- *
6533
- * <ui-icon name="delete" label="Remove line item 1" variant="danger" (iconClick)="removeLineItem(i)" />
6534
- *
6535
- * `label` is the accessible name (an icon has no text) — always set it. An unknown `name` draws nothing.
6536
- */
6537
- export declare class IconComponent {
6538
- /** Which icon to draw, e.g. `delete`, `edit`, `add`. */
6539
- readonly name: import("@angular/core").InputSignal<IconName | (string & {})>;
6540
- /** Accessible name — what the click does ("Remove line item 1"). */
6541
- readonly label: import("@angular/core").InputSignal<string>;
6542
- /** Icon size in px. */
6543
- readonly size: import("@angular/core").InputSignal<number>;
6544
- readonly variant: import("@angular/core").InputSignal<"danger" | "default">;
6545
- readonly disabled: import("@angular/core").InputSignal<boolean>;
6546
- /** Fires on click (not while disabled); the host performs the action. */
6547
- readonly iconClick: import("@angular/core").OutputEmitterRef<MouseEvent>;
6548
- protected readonly path: import("@angular/core").Signal<string | undefined>;
6549
- protected onClick(event: MouseEvent): void;
6550
- static ɵfac: i0.ɵɵFactoryDeclaration<IconComponent, never>;
6551
- static ɵcmp: i0.ɵɵComponentDeclaration<IconComponent, "ui-icon", never, {
6552
- "name": {
6553
- "alias": "name";
6554
- "required": true;
8007
+ "moveControls": {
8008
+ "alias": "moveControls";
8009
+ "required": false;
6555
8010
  "isSignal": true;
6556
8011
  };
6557
- "label": {
6558
- "alias": "label";
8012
+ "moveEarlierLabel": {
8013
+ "alias": "moveEarlierLabel";
6559
8014
  "required": false;
6560
8015
  "isSignal": true;
6561
8016
  };
6562
- "size": {
6563
- "alias": "size";
8017
+ "moveLaterLabel": {
8018
+ "alias": "moveLaterLabel";
6564
8019
  "required": false;
6565
8020
  "isSignal": true;
6566
8021
  };
6567
- "variant": {
6568
- "alias": "variant";
8022
+ "helpText": {
8023
+ "alias": "helpText";
6569
8024
  "required": false;
6570
8025
  "isSignal": true;
6571
8026
  };
6572
- "disabled": {
6573
- "alias": "disabled";
8027
+ "moveAnnouncement": {
8028
+ "alias": "moveAnnouncement";
6574
8029
  "required": false;
6575
8030
  "isSignal": true;
6576
8031
  };
6577
8032
  }, {
6578
- "iconClick": "iconClick";
6579
- }, never, never, true, never>;
8033
+ "items": "itemsChange";
8034
+ "orderChange": "orderChange";
8035
+ }, ["itemTemplate"], never, true, never>;
6580
8036
  }
6581
8037
  export type ImagePreviewShape = 'circle' | 'rounded';
6582
8038
  export type ImagePreviewSize = 'sm' | 'md' | 'lg';
@@ -6931,23 +8387,238 @@ export declare class RichTextEditorComponent implements ControlValueAccessor {
6931
8387
  "required": false;
6932
8388
  "isSignal": true;
6933
8389
  };
6934
- "ariaLabel": {
6935
- "alias": "ariaLabel";
8390
+ "ariaLabel": {
8391
+ "alias": "ariaLabel";
8392
+ "required": false;
8393
+ "isSignal": true;
8394
+ };
8395
+ "placeholder": {
8396
+ "alias": "placeholder";
8397
+ "required": false;
8398
+ "isSignal": true;
8399
+ };
8400
+ "required": {
8401
+ "alias": "required";
8402
+ "required": false;
8403
+ "isSignal": true;
8404
+ };
8405
+ "readonly": {
8406
+ "alias": "readonly";
8407
+ "required": false;
8408
+ "isSignal": true;
8409
+ };
8410
+ "hint": {
8411
+ "alias": "hint";
8412
+ "required": false;
8413
+ "isSignal": true;
8414
+ };
8415
+ "error": {
8416
+ "alias": "error";
8417
+ "required": false;
8418
+ "isSignal": true;
8419
+ };
8420
+ "minHeight": {
8421
+ "alias": "minHeight";
8422
+ "required": false;
8423
+ "isSignal": true;
8424
+ };
8425
+ "maxHeight": {
8426
+ "alias": "maxHeight";
8427
+ "required": false;
8428
+ "isSignal": true;
8429
+ };
8430
+ "hide": {
8431
+ "alias": "hide";
8432
+ "required": false;
8433
+ "isSignal": true;
8434
+ };
8435
+ "_disabledInput": {
8436
+ "alias": "disabled";
8437
+ "required": false;
8438
+ "isSignal": true;
8439
+ };
8440
+ }, {}, never, never, true, never>;
8441
+ }
8442
+ /**
8443
+ * Returns the trimmed text when `value` is a plain, user-facing sentence from the server, otherwise `null`
8444
+ * (not a string, empty, longer than 300 chars, or technical: stack trace, exception/class name, JSON, HTML,
8445
+ * raw status text, "null"). Callers fall back to their own generated wording on `null`.
8446
+ */
8447
+ export declare function humanReadableMessage(value: unknown): string | null;
8448
+ /**
8449
+ * Single native radio button (visual tokens of `ui-checkbox`). Normally rendered by `ui-radio-group`; usable standalone
8450
+ * when the host manages the shared `name` and the `checked` state itself.
8451
+ */
8452
+ export declare class RadioComponent {
8453
+ readonly label: import("@angular/core").InputSignal<string>;
8454
+ readonly description: import("@angular/core").InputSignal<string>;
8455
+ /** Accessible name when there is no visible `label`. */
8456
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
8457
+ readonly value: import("@angular/core").InputSignal<string>;
8458
+ /** Radios with the same `name` form one keyboard (arrow key) group. */
8459
+ readonly name: import("@angular/core").InputSignal<string>;
8460
+ readonly checked: import("@angular/core").InputSignal<boolean>;
8461
+ readonly invalid: import("@angular/core").InputSignal<boolean>;
8462
+ /** Id(s) of the element(s) describing the group (hint / error). */
8463
+ readonly describedBy: import("@angular/core").InputSignal<string>;
8464
+ /** Public so a host template can bind `[disabled]` (a protected input fails strict template type-checking). */
8465
+ readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
8466
+ protected readonly disabled: import("@angular/core").InputSignal<boolean>;
8467
+ protected readonly _labelId: string;
8468
+ /** With a description inside the <label>, the accessible name would read "Email We send a message": name the input by the label text only. */
8469
+ protected readonly _labelledBy: import("@angular/core").Signal<string | null>;
8470
+ protected readonly _descriptionId: string;
8471
+ /** Group hint/error id plus the option's own description (kept out of the label so the accessible name is label-only). */
8472
+ protected readonly _describedByIds: import("@angular/core").Signal<string | null>;
8473
+ /** Emits the radio's `value` when the user selects it. */
8474
+ readonly selected: import("@angular/core").OutputEmitterRef<string>;
8475
+ protected onChange(event: Event): void;
8476
+ static ɵfac: i0.ɵɵFactoryDeclaration<RadioComponent, never>;
8477
+ static ɵcmp: i0.ɵɵComponentDeclaration<RadioComponent, "ui-radio", never, {
8478
+ "label": {
8479
+ "alias": "label";
8480
+ "required": false;
8481
+ "isSignal": true;
8482
+ };
8483
+ "description": {
8484
+ "alias": "description";
8485
+ "required": false;
8486
+ "isSignal": true;
8487
+ };
8488
+ "ariaLabel": {
8489
+ "alias": "ariaLabel";
8490
+ "required": false;
8491
+ "isSignal": true;
8492
+ };
8493
+ "value": {
8494
+ "alias": "value";
8495
+ "required": false;
8496
+ "isSignal": true;
8497
+ };
8498
+ "name": {
8499
+ "alias": "name";
8500
+ "required": false;
8501
+ "isSignal": true;
8502
+ };
8503
+ "checked": {
8504
+ "alias": "checked";
8505
+ "required": false;
8506
+ "isSignal": true;
8507
+ };
8508
+ "invalid": {
8509
+ "alias": "invalid";
8510
+ "required": false;
8511
+ "isSignal": true;
8512
+ };
8513
+ "describedBy": {
8514
+ "alias": "describedBy";
8515
+ "required": false;
8516
+ "isSignal": true;
8517
+ };
8518
+ "_disabledInput": {
8519
+ "alias": "disabled";
8520
+ "required": false;
8521
+ "isSignal": true;
8522
+ };
8523
+ }, {
8524
+ "selected": "selected";
8525
+ }, never, ["*"], true, never>;
8526
+ }
8527
+ /** One row of a `ui-radio-matrix`: `value` is the key in the model, `label` the row header. */
8528
+ export interface RadioMatrixRow {
8529
+ value: string;
8530
+ label: string;
8531
+ disabled?: boolean;
8532
+ }
8533
+ /**
8534
+ * Matrix of native radio buttons: one row per item, one column per choice, exactly one choice per row (e.g. a page-permission
8535
+ * grid: rows = pages, columns = Full Access / Read Only / Hidden). A ControlValueAccessor whose value is a record
8536
+ * `{ [row.value]: column.value }` (`''` / missing = nothing chosen for that row). Arrow keys move inside a row natively because the
8537
+ * radios of a row share one `name`. Every radio is named "<row>: <column>" for screen readers; rows are `<th scope="row">`, columns
8538
+ * `<th scope="col">`.
8539
+ *
8540
+ * Built-in validation: `required` -> every row needs a choice ("Choose an option in every row." / "<label>: choose an option in
8541
+ * every row."); a value that is not one of the `columns` -> "Select a valid option."; a key that is not one of the `rows` is
8542
+ * ignored (and kept untouched in the model). Disabled matrices are never invalid.
8543
+ *
8544
+ * Validity API (same on every ui input): Angular `Validator`, signals `invalid` + `errorMessage`, `invalidChange` output,
8545
+ * provides `UiFieldValidity`. Messages show after the matrix lost focus or while `[showErrors]="true"`; a host `[error]` wins.
8546
+ *
8547
+ * <ui-radio-matrix label="Page permissions" rowHeader="Page" [rows]="pages" [columns]="levels" [(ngModel)]="permissions" />
8548
+ */
8549
+ export declare class RadioMatrixComponent implements ControlValueAccessor, Validator, UiFieldValidity {
8550
+ readonly label: import("@angular/core").InputSignal<string>;
8551
+ /** Text of the top-left header cell (what the rows are, e.g. "Page"). */
8552
+ readonly rowHeader: import("@angular/core").InputSignal<string>;
8553
+ readonly rows: import("@angular/core").InputSignal<RadioMatrixRow[]>;
8554
+ /** The choices, one column each (same shape as `ui-radio-group` options; `description` is not shown). */
8555
+ readonly columns: import("@angular/core").InputSignal<RadioOption[]>;
8556
+ readonly required: import("@angular/core").InputSignal<boolean>;
8557
+ readonly hint: import("@angular/core").InputSignal<string>;
8558
+ readonly error: import("@angular/core").InputSignal<string>;
8559
+ /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
8560
+ readonly showErrors: import("@angular/core").InputSignal<boolean>;
8561
+ /** Prefix of the native radio `name`s (one name per row); a unique one is generated when omitted. */
8562
+ readonly name: import("@angular/core").InputSignal<string>;
8563
+ protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
8564
+ private readonly _cvaDisabled;
8565
+ protected readonly disabled: import("@angular/core").Signal<boolean>;
8566
+ private readonly _generatedName;
8567
+ protected get _name(): string;
8568
+ protected readonly _descId: string;
8569
+ protected readonly _value: import("@angular/core").WritableSignal<Record<string, string>>;
8570
+ private readonly _reveal;
8571
+ private readonly _issue;
8572
+ /** True while the value breaks a built-in rule or the host passed an `[error]`. */
8573
+ readonly invalid: import("@angular/core").Signal<boolean>;
8574
+ /** Message of the first broken built-in rule ('' when valid), shown or not. */
8575
+ readonly errorMessage: import("@angular/core").Signal<string>;
8576
+ /** Emits whenever the field becomes invalid / valid. */
8577
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
8578
+ protected readonly _activeError: import("@angular/core").Signal<string>;
8579
+ private _onValidatorChange;
8580
+ constructor();
8581
+ /** Re-run the form control's validation when a rule input (required, rows, columns) changes. */
8582
+ ngOnChanges(): void;
8583
+ validate(control: AbstractControl): ValidationErrors | null;
8584
+ registerOnValidatorChange(fn: () => void): void;
8585
+ private _onChange;
8586
+ private _onTouched;
8587
+ writeValue(val: Record<string, string> | null): void;
8588
+ registerOnChange(fn: (v: Record<string, string>) => void): void;
8589
+ registerOnTouched(fn: () => void): void;
8590
+ setDisabledState(d: boolean): void;
8591
+ protected onSelect(row: string, column: string): void;
8592
+ /** True when a required matrix has no valid choice in this row (shown on the row header and the dots). */
8593
+ protected _rowInvalid(row: RadioMatrixRow): boolean;
8594
+ /** Touched once focus leaves the whole matrix (moving between its radios does not count). */
8595
+ protected onFocusOut(event: FocusEvent): void;
8596
+ private _toRecord;
8597
+ private _check;
8598
+ static ɵfac: i0.ɵɵFactoryDeclaration<RadioMatrixComponent, never>;
8599
+ static ɵcmp: i0.ɵɵComponentDeclaration<RadioMatrixComponent, "ui-radio-matrix", never, {
8600
+ "label": {
8601
+ "alias": "label";
8602
+ "required": false;
8603
+ "isSignal": true;
8604
+ };
8605
+ "rowHeader": {
8606
+ "alias": "rowHeader";
6936
8607
  "required": false;
6937
8608
  "isSignal": true;
6938
8609
  };
6939
- "placeholder": {
6940
- "alias": "placeholder";
8610
+ "rows": {
8611
+ "alias": "rows";
6941
8612
  "required": false;
6942
8613
  "isSignal": true;
6943
8614
  };
6944
- "required": {
6945
- "alias": "required";
8615
+ "columns": {
8616
+ "alias": "columns";
6946
8617
  "required": false;
6947
8618
  "isSignal": true;
6948
8619
  };
6949
- "readonly": {
6950
- "alias": "readonly";
8620
+ "required": {
8621
+ "alias": "required";
6951
8622
  "required": false;
6952
8623
  "isSignal": true;
6953
8624
  };
@@ -6961,18 +8632,13 @@ export declare class RichTextEditorComponent implements ControlValueAccessor {
6961
8632
  "required": false;
6962
8633
  "isSignal": true;
6963
8634
  };
6964
- "minHeight": {
6965
- "alias": "minHeight";
6966
- "required": false;
6967
- "isSignal": true;
6968
- };
6969
- "maxHeight": {
6970
- "alias": "maxHeight";
8635
+ "showErrors": {
8636
+ "alias": "showErrors";
6971
8637
  "required": false;
6972
8638
  "isSignal": true;
6973
8639
  };
6974
- "hide": {
6975
- "alias": "hide";
8640
+ "name": {
8641
+ "alias": "name";
6976
8642
  "required": false;
6977
8643
  "isSignal": true;
6978
8644
  };
@@ -6981,183 +8647,342 @@ export declare class RichTextEditorComponent implements ControlValueAccessor {
6981
8647
  "required": false;
6982
8648
  "isSignal": true;
6983
8649
  };
6984
- }, {}, never, never, true, never>;
8650
+ }, {
8651
+ "invalidChange": "invalidChange";
8652
+ }, never, never, true, never>;
6985
8653
  }
6986
8654
  /**
6987
- * Returns the trimmed text when `value` is a plain, user-facing sentence from the server, otherwise `null`
6988
- * (not a string, empty, longer than 300 chars, or technical: stack trace, exception/class name, JSON, HTML,
6989
- * raw status text, "null"). Callers fall back to their own generated wording on `null`.
6990
- */
6991
- export declare function humanReadableMessage(value: unknown): string | null;
6992
- /**
6993
- * Single native radio button (visual tokens of `ui-checkbox`). Normally rendered by `ui-radio-group`; usable standalone
6994
- * when the host manages the shared `name` and the `checked` state itself.
8655
+ * Card for detail pages: an optional header and a body, both filled by the page that uses it.
8656
+ *
8657
+ * Header (all optional - set `[showHeader]="false"` and the card is just the body):
8658
+ * - `title` / `subtitle`: the bold uppercase title and the grey line under it.
8659
+ * - `titleId`: an id for the title text, which also names the card for assistive tech (aria-labelledby).
8660
+ * - `<div card-header-leading>`: something before the title block, such as a logo or an icon.
8661
+ * - `<ng-container card-header>`: your own header content, shown under the title (or instead of it when no title is set).
8662
+ * - `<ng-container card-header-action>`: buttons or links on the right side of the header.
8663
+ * - `showEdit` + `editLabel` + `(editClick)`: the built-in Edit button.
8664
+ * - `background`: the card's background colour (any CSS colour; default white).
8665
+ * - `textColor`: the colour of the title, subtitle, chevron and body text (any CSS colour; default: the normal slate colours). Use it for light text on a dark `background`. Text you colour yourself inside the body keeps its own colour.
8666
+ * - The body has built-in padding (16px 18px, matching the header's 18px side padding); there is no padding input.
8667
+ * - `collapsible` (default true): a chevron toggle and a clickable header; `[(expanded)]` is two-way.
8668
+ * Without a header, or with `collapsible` false, the body is always open.
8669
+ *
8670
+ * The title is always shown in capitals; the subtitle keeps the case you give it. The body is the default content slot. A collapsed body is removed from the tab order and from assistive tech.
8671
+ *
8672
+ * <ui-detail-card title="COMMUNICATIONS" subtitle="All calls, texts, and emails"> ...body... </ui-detail-card>
8673
+ * <ui-detail-card title="CONTACT INFO" [showEdit]="true" (editClick)="openEdit()"> ... </ui-detail-card>
8674
+ * <ui-detail-card [showHeader]="false"> ...body only... </ui-detail-card>
8675
+ * <ui-detail-card [collapsible]="false">
8676
+ * <div card-header>Any markup</div>
8677
+ * <ui-button card-header-action size="xs" variant="secondary">Add</ui-button>
8678
+ * ...body...
8679
+ * </ui-detail-card>
6995
8680
  */
6996
- export declare class RadioComponent {
6997
- readonly label: import("@angular/core").InputSignal<string>;
6998
- readonly description: import("@angular/core").InputSignal<string>;
6999
- /** Accessible name when there is no visible `label`. */
7000
- readonly ariaLabel: import("@angular/core").InputSignal<string>;
7001
- readonly value: import("@angular/core").InputSignal<string>;
7002
- /** Radios with the same `name` form one keyboard (arrow key) group. */
7003
- readonly name: import("@angular/core").InputSignal<string>;
7004
- readonly checked: import("@angular/core").InputSignal<boolean>;
7005
- readonly invalid: import("@angular/core").InputSignal<boolean>;
7006
- /** Id(s) of the element(s) describing the group (hint / error). */
7007
- readonly describedBy: import("@angular/core").InputSignal<string>;
7008
- /** Public so a host template can bind `[disabled]` (a protected input fails strict template type-checking). */
7009
- readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
7010
- protected readonly disabled: import("@angular/core").InputSignal<boolean>;
7011
- protected readonly _labelId: string;
7012
- /** With a description inside the <label>, the accessible name would read "Email We send a message": name the input by the label text only. */
7013
- protected readonly _labelledBy: import("@angular/core").Signal<string | null>;
7014
- protected readonly _descriptionId: string;
7015
- /** Group hint/error id plus the option's own description (kept out of the label so the accessible name is label-only). */
7016
- protected readonly _describedByIds: import("@angular/core").Signal<string | null>;
7017
- /** Emits the radio's `value` when the user selects it. */
7018
- readonly selected: import("@angular/core").OutputEmitterRef<string>;
7019
- protected onChange(event: Event): void;
7020
- static ɵfac: i0.ɵɵFactoryDeclaration<RadioComponent, never>;
7021
- static ɵcmp: i0.ɵɵComponentDeclaration<RadioComponent, "ui-radio", never, {
7022
- "label": {
7023
- "alias": "label";
8681
+ export declare class DetailCardComponent {
8682
+ /** Bold uppercase title. Empty = no title text. */
8683
+ readonly title: import("@angular/core").InputSignal<string>;
8684
+ /** 1-6 makes the title a real heading for screen readers (role=heading, aria-level); null keeps it plain text. */
8685
+ readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 3 | 4 | 5 | 6 | null>;
8686
+ /** Optional id for the title text; the card is then labelled by it. Without it the title is the card's aria-label. */
8687
+ readonly titleId: import("@angular/core").InputSignal<string>;
8688
+ /** Grey line under the title. */
8689
+ readonly subtitle: import("@angular/core").InputSignal<string>;
8690
+ /** Card background as any CSS colour (`#f8fafc`, `var(--surface)` …). Empty = white. */
8691
+ readonly background: import("@angular/core").InputSignal<string>;
8692
+ /** Text colour as any CSS colour (`#fff`, `var(--on-dark)` …). Empty = the default colours. Colours the title, subtitle, chevron and body text. */
8693
+ readonly textColor: import("@angular/core").InputSignal<string>;
8694
+ /** false = no header at all (no title, edit button, toggle or divider): the card is only the body, always open. */
8695
+ readonly showHeader: import("@angular/core").InputSignal<boolean>;
8696
+ /** Chevron toggle + clickable header. false = the body is always open. */
8697
+ readonly collapsible: import("@angular/core").InputSignal<boolean>;
8698
+ /** Two-way: whether the body is open. */
8699
+ readonly expanded: import("@angular/core").ModelSignal<boolean>;
8700
+ /** Show the built-in Edit button in the header. */
8701
+ readonly showEdit: import("@angular/core").InputSignal<boolean>;
8702
+ readonly editLabel: import("@angular/core").InputSignal<string>;
8703
+ readonly editClick: import("@angular/core").OutputEmitterRef<void>;
8704
+ /** The body is open unless the card has a header that is collapsible and was collapsed. */
8705
+ isOpen(): boolean;
8706
+ toggle(): void;
8707
+ /** A click on the header toggles, except on a control inside it (Edit, your own header actions, links, inputs). */
8708
+ onHeaderClick(event: Event): void;
8709
+ static ɵfac: i0.ɵɵFactoryDeclaration<DetailCardComponent, never>;
8710
+ static ɵcmp: i0.ɵɵComponentDeclaration<DetailCardComponent, "ui-detail-card", never, {
8711
+ "title": {
8712
+ "alias": "title";
7024
8713
  "required": false;
7025
8714
  "isSignal": true;
7026
8715
  };
7027
- "description": {
7028
- "alias": "description";
8716
+ "headingLevel": {
8717
+ "alias": "headingLevel";
7029
8718
  "required": false;
7030
8719
  "isSignal": true;
7031
8720
  };
7032
- "ariaLabel": {
7033
- "alias": "ariaLabel";
8721
+ "titleId": {
8722
+ "alias": "titleId";
7034
8723
  "required": false;
7035
8724
  "isSignal": true;
7036
8725
  };
7037
- "value": {
7038
- "alias": "value";
8726
+ "subtitle": {
8727
+ "alias": "subtitle";
7039
8728
  "required": false;
7040
8729
  "isSignal": true;
7041
8730
  };
7042
- "name": {
7043
- "alias": "name";
8731
+ "background": {
8732
+ "alias": "background";
7044
8733
  "required": false;
7045
8734
  "isSignal": true;
7046
8735
  };
7047
- "checked": {
7048
- "alias": "checked";
8736
+ "textColor": {
8737
+ "alias": "textColor";
7049
8738
  "required": false;
7050
8739
  "isSignal": true;
7051
8740
  };
7052
- "invalid": {
7053
- "alias": "invalid";
8741
+ "showHeader": {
8742
+ "alias": "showHeader";
7054
8743
  "required": false;
7055
8744
  "isSignal": true;
7056
8745
  };
7057
- "describedBy": {
7058
- "alias": "describedBy";
8746
+ "collapsible": {
8747
+ "alias": "collapsible";
7059
8748
  "required": false;
7060
8749
  "isSignal": true;
7061
8750
  };
7062
- "_disabledInput": {
7063
- "alias": "disabled";
8751
+ "expanded": {
8752
+ "alias": "expanded";
8753
+ "required": false;
8754
+ "isSignal": true;
8755
+ };
8756
+ "showEdit": {
8757
+ "alias": "showEdit";
8758
+ "required": false;
8759
+ "isSignal": true;
8760
+ };
8761
+ "editLabel": {
8762
+ "alias": "editLabel";
7064
8763
  "required": false;
7065
8764
  "isSignal": true;
7066
8765
  };
7067
8766
  }, {
7068
- "selected": "selected";
7069
- }, never, ["*"], true, never>;
8767
+ "expanded": "expandedChange";
8768
+ "editClick": "editClick";
8769
+ }, never, ["[card-header-leading]", "[card-header]", "[card-header-action]", "*"], true, never>;
7070
8770
  }
7071
- /** One row of a `ui-radio-matrix`: `value` is the key in the model, `label` the row header. */
7072
- export interface RadioMatrixRow {
8771
+ export type DividerSpace = 'none' | 'sm' | 'md' | 'lg';
8772
+ /**
8773
+ * Horizontal divider line (an `<hr>`) with the app's standard colour and optional space above / below.
8774
+ * It is a real separator for assistive tech by default; set `decorative` when it only adds visual rhythm and the
8775
+ * content around it already reads as separate (the line is then hidden from screen readers).
8776
+ *
8777
+ * <section class="kpi-grid"> ... </section>
8778
+ * <ui-divider /> // line with 16px below
8779
+ * <ui-divider spaceAbove="md" spaceBelow="lg" />
8780
+ * <ui-divider [decorative]="true" />
8781
+ */
8782
+ export declare class DividerComponent {
8783
+ /** Space above the line: none (0), sm (8px), md (16px), lg (24px). */
8784
+ readonly spaceAbove: import("@angular/core").InputSignal<DividerSpace>;
8785
+ /** Space below the line: none (0), sm (8px), md (16px), lg (24px). */
8786
+ readonly spaceBelow: import("@angular/core").InputSignal<DividerSpace>;
8787
+ /** true = purely visual: hidden from assistive tech. */
8788
+ readonly decorative: import("@angular/core").InputSignal<boolean>;
8789
+ protected readonly above: import("@angular/core").Signal<number>;
8790
+ protected readonly below: import("@angular/core").Signal<number>;
8791
+ static ɵfac: i0.ɵɵFactoryDeclaration<DividerComponent, never>;
8792
+ static ɵcmp: i0.ɵɵComponentDeclaration<DividerComponent, "ui-divider", never, {
8793
+ "spaceAbove": {
8794
+ "alias": "spaceAbove";
8795
+ "required": false;
8796
+ "isSignal": true;
8797
+ };
8798
+ "spaceBelow": {
8799
+ "alias": "spaceBelow";
8800
+ "required": false;
8801
+ "isSignal": true;
8802
+ };
8803
+ "decorative": {
8804
+ "alias": "decorative";
8805
+ "required": false;
8806
+ "isSignal": true;
8807
+ };
8808
+ }, {}, never, never, true, never>;
8809
+ }
8810
+ /**
8811
+ * Wraps a block of form controls (a settings page, a configuration card) so a view-only user can look at the current
8812
+ * values but change nothing. While `readOnly` is true the wrapper's native `<fieldset>` is `disabled`, and the browser
8813
+ * disables every native form control inside it: text inputs, checkboxes, selects, textareas, radios and the `<button>`
8814
+ * rendered by `ui-button`, plus the native controls inside the `ui-*` inputs.
8815
+ *
8816
+ * **Default: `readOnly` is false.** With no input nothing is locked and every control is fully editable. The wrapper only
8817
+ * locks when the host passes `[readOnly]="true"` (typically `perm() !== 'full'`). Flipping it live re-enables / disables
8818
+ * the same projected DOM nodes; values, `ngModel` state and focus targets are preserved.
8819
+ *
8820
+ * Limits (native `<fieldset disabled>` semantics):
8821
+ * - Only NATIVE form controls are disabled. Non-native clickable things (divs with click handlers, links, drag handles,
8822
+ * `ui-data-table` toolbars and row menus) are NOT affected: hide or guard them in the host.
8823
+ * - A `ui-data-table` has its own `[permission]` input. Keep tables outside the wrapper or use that input.
8824
+ * - Nesting: a nested wrapper with `readOnly=false` inside a locked wrapper stays disabled, because a disabled
8825
+ * ancestor fieldset always disables its descendants. A nested wrapper cannot "unlock" a region.
8826
+ *
8827
+ * Layout: the fieldset is reset (border 0, margin 0, padding 0, min-width 0) and is a plain flex column, not
8828
+ * `display: contents` (unreliable on `<fieldset>` in some browsers). Children therefore lay out as direct children;
8829
+ * set `gap` when the parent spaced them with a flex gap.
8830
+ *
8831
+ * Accessibility: optional `ariaLabel` names the group; optional `reason` ("You have view-only access") is rendered as
8832
+ * visually-hidden text linked via `aria-describedby`, only while locked.
8833
+ *
8834
+ * Usage: `<ui-readonly-fieldset [readOnly]="perm() !== 'full'" gap="20px"> ...controls... </ui-readonly-fieldset>`
8835
+ */
8836
+ export declare class ReadonlyFieldsetComponent {
8837
+ /** Lock every native control inside. Default false: nothing is locked. */
8838
+ readonly readOnly: import("@angular/core").InputSignal<boolean>;
8839
+ /** CSS `gap` between the children (default none). */
8840
+ readonly gap: import("@angular/core").InputSignal<string>;
8841
+ /** Optional accessible name for the group. */
8842
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
8843
+ /** Optional explanation announced while locked (visually hidden), e.g. "You have view-only access". */
8844
+ readonly reason: import("@angular/core").InputSignal<string>;
8845
+ protected readonly _reasonId: string;
8846
+ protected readonly _showReason: import("@angular/core").Signal<boolean>;
8847
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReadonlyFieldsetComponent, never>;
8848
+ static ɵcmp: i0.ɵɵComponentDeclaration<ReadonlyFieldsetComponent, "ui-readonly-fieldset", never, {
8849
+ "readOnly": {
8850
+ "alias": "readOnly";
8851
+ "required": false;
8852
+ "isSignal": true;
8853
+ };
8854
+ "gap": {
8855
+ "alias": "gap";
8856
+ "required": false;
8857
+ "isSignal": true;
8858
+ };
8859
+ "ariaLabel": {
8860
+ "alias": "ariaLabel";
8861
+ "required": false;
8862
+ "isSignal": true;
8863
+ };
8864
+ "reason": {
8865
+ "alias": "reason";
8866
+ "required": false;
8867
+ "isSignal": true;
8868
+ };
8869
+ }, {}, never, ["*"], true, never>;
8870
+ }
8871
+ /** A preset swatch offered under the field. `label` is its accessible name. */
8872
+ export interface ColorPreset {
7073
8873
  value: string;
7074
8874
  label: string;
7075
- disabled?: boolean;
7076
8875
  }
7077
8876
  /**
7078
- * Matrix of native radio buttons: one row per item, one column per choice, exactly one choice per row (e.g. a page-permission
7079
- * grid: rows = pages, columns = Full Access / Read Only / Hidden). A ControlValueAccessor whose value is a record
7080
- * `{ [row.value]: column.value }` (`''` / missing = nothing chosen for that row). Arrow keys move inside a row natively because the
7081
- * radios of a row share one `name`. Every radio is named "<row>: <column>" for screen readers; rows are `<th scope="row">`, columns
7082
- * `<th scope="col">`.
8877
+ * Normalises a CSS hex colour: `#fff` / `FFF` -> `#ffffff`, `#RRGGBB` -> lower case. With `allowAlpha`, `#rrggbbaa`
8878
+ * is accepted and kept at 8 digits (shorthand `#rgba` is never accepted). Returns null for anything else
8879
+ * (named colours, `rgb()`, wrong lengths, non-hex digits). The leading `#` is optional on input.
8880
+ */
8881
+ export declare function normalizeHexColor(value: string | null | undefined, allowAlpha?: boolean): string | null;
8882
+ /**
8883
+ * Colour input: ControlValueAccessor over a string holding a CSS hex colour. A swatch opens the operating system's
8884
+ * colour picker (native `<input type="color">`) and a text field next to it shows / edits the hex; both stay in sync.
7083
8885
  *
7084
- * Built-in validation: `required` -> every row needs a choice ("Choose an option in every row." / "<label>: choose an option in
7085
- * every row."); a value that is not one of the `columns` -> "Select a valid option."; a key that is not one of the `rows` is
7086
- * ignored (and kept untouched in the model). Disabled matrices are never invalid.
8886
+ * <ui-color-input label="Brand colour" [(ngModel)]="brand" />
8887
+ * <ui-color-input label="Accent" [presets]="presets" [allowAlpha]="true" [formControl]="accent" />
7087
8888
  *
7088
- * Validity API (same on every ui input): Angular `Validator`, signals `invalid` + `errorMessage`, `invalidChange` output,
7089
- * provides `UiFieldValidity`. Messages show after the matrix lost focus or while `[showErrors]="true"`; a host `[error]` wins.
8889
+ * Value contract
8890
+ * - A valid colour is emitted normalised to lower-case `#rrggbb` (`#FFF` -> `#ffffff`; 8-digit `#rrggbbaa` only with
8891
+ * `allowAlpha`, kept at 8 digits). The user may type with or without the leading `#`; the control prepends it.
8892
+ * - Text the user typed that is not a valid colour is emitted as typed (with `#` prepended, `''` when cleared) so the host
8893
+ * sees what the user entered; the control is then invalid ("Enter a hex colour like #3b82f6.") and the form control gets
8894
+ * the `color` error. While typing, the text is not rewritten (`#fff` is emitted as `#ffffff` but stays `#fff` on screen)
8895
+ * and is normalised on blur.
8896
+ * - A value WRITTEN BY THE HOST (`writeValue` / `[ngModel]` / `[formControl]`) is never flagged as an invalid colour, even
8897
+ * when it is not a hex (`red`, `#fff8`, a legacy value), until the user changes it, so legacy data never blocks Save.
8898
+ * (Required is still checked for an empty value.) The native picker gets `#000000` for a non-hex value so it never throws.
8899
+ * - Picking in the native picker with an 8-digit value and `allowAlpha` keeps the existing alpha digits.
7090
8900
  *
7091
- * <ui-radio-matrix label="Page permissions" rowHeader="Page" [rows]="pages" [columns]="levels" [(ngModel)]="permissions" />
8901
+ * Validity API (same as the other ui inputs): `Validator` (error keys `required`, `color`), signals `invalid` /
8902
+ * `errorMessage`, `invalidChange` output, `UiFieldValidity` token. Messages show after blur or while
8903
+ * `[showErrors]="true"`; a host `[error]` always wins. Disabled controls are never invalid.
8904
+ *
8905
+ * Cross-browser: the native swatch is styled with `-webkit-appearance` and the `::-webkit-color-swatch` / `::-moz-color-swatch`
8906
+ * pseudo-elements and sits invisibly over our own preview square, so it looks the same in Chrome, Edge, Firefox and Safari.
8907
+ * Preset swatches are real `<button>`s (Enter / Space work natively, `aria-pressed` marks the selected one).
7092
8908
  */
7093
- export declare class RadioMatrixComponent implements ControlValueAccessor, Validator, UiFieldValidity {
8909
+ export declare class ColorInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {
7094
8910
  readonly label: import("@angular/core").InputSignal<string>;
7095
- /** Text of the top-left header cell (what the rows are, e.g. "Page"). */
7096
- readonly rowHeader: import("@angular/core").InputSignal<string>;
7097
- readonly rows: import("@angular/core").InputSignal<RadioMatrixRow[]>;
7098
- /** The choices, one column each (same shape as `ui-radio-group` options; `description` is not shown). */
7099
- readonly columns: import("@angular/core").InputSignal<RadioOption[]>;
8911
+ /** Accessible name for a field with no visible label. */
8912
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
8913
+ readonly placeholder: import("@angular/core").InputSignal<string>;
7100
8914
  readonly required: import("@angular/core").InputSignal<boolean>;
7101
8915
  readonly hint: import("@angular/core").InputSignal<string>;
8916
+ /** Host error message; always wins over the built-in message. */
7102
8917
  readonly error: import("@angular/core").InputSignal<string>;
7103
8918
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
7104
8919
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
7105
- /** Prefix of the native radio `name`s (one name per row); a unique one is generated when omitted. */
7106
- readonly name: import("@angular/core").InputSignal<string>;
8920
+ /** Accept (and keep) 8-digit `#rrggbbaa` values. */
8921
+ readonly allowAlpha: import("@angular/core").InputSignal<boolean>;
8922
+ /** Optional row of preset swatches. */
8923
+ readonly presets: import("@angular/core").InputSignal<ColorPreset[]>;
8924
+ /** Emits whenever the field becomes invalid / valid. */
8925
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
7107
8926
  protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
7108
8927
  private readonly _cvaDisabled;
7109
8928
  protected readonly disabled: import("@angular/core").Signal<boolean>;
7110
- private readonly _generatedName;
7111
- protected get _name(): string;
7112
- protected readonly _descId: string;
7113
- protected readonly _value: import("@angular/core").WritableSignal<Record<string, string>>;
8929
+ /** Current model value (what the form control holds). */
8930
+ private readonly _model;
8931
+ /** Text shown in the text field. */
8932
+ protected readonly _text: import("@angular/core").WritableSignal<string>;
8933
+ /** True once the user changed the value; host-written values are not format-checked before that. */
8934
+ private readonly _edited;
7114
8935
  private readonly _reveal;
7115
8936
  private readonly _issue;
7116
- /** True while the value breaks a built-in rule or the host passed an `[error]`. */
7117
8937
  readonly invalid: import("@angular/core").Signal<boolean>;
7118
- /** Message of the first broken built-in rule ('' when valid), shown or not. */
7119
8938
  readonly errorMessage: import("@angular/core").Signal<string>;
7120
- /** Emits whenever the field becomes invalid / valid. */
7121
- readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
7122
8939
  protected readonly _activeError: import("@angular/core").Signal<string>;
8940
+ private readonly _valid;
8941
+ /** Colour of the preview square: the value when it is a usable hex, otherwise transparent. */
8942
+ protected readonly _swatchColor: import("@angular/core").Signal<string>;
8943
+ /** Value for the native picker: always `#rrggbb`. */
8944
+ protected readonly _nativeValue: import("@angular/core").Signal<string>;
8945
+ private readonly _uid;
8946
+ protected readonly _textId: string;
8947
+ protected readonly _nativeId: string;
8948
+ protected readonly _descId: string;
8949
+ private _onChange;
8950
+ private _onTouched;
7123
8951
  private _onValidatorChange;
7124
8952
  constructor();
7125
- /** Re-run the form control's validation when a rule input (required, rows, columns) changes. */
8953
+ /** Re-run the form control's validation when a rule input (required, allowAlpha, ...) changes. */
7126
8954
  ngOnChanges(): void;
7127
- validate(control: AbstractControl): ValidationErrors | null;
7128
- registerOnValidatorChange(fn: () => void): void;
7129
- private _onChange;
7130
- private _onTouched;
7131
- writeValue(val: Record<string, string> | null): void;
7132
- registerOnChange(fn: (v: Record<string, string>) => void): void;
8955
+ writeValue(val: string | null): void;
8956
+ registerOnChange(fn: (v: string) => void): void;
7133
8957
  registerOnTouched(fn: () => void): void;
8958
+ registerOnValidatorChange(fn: () => void): void;
7134
8959
  setDisabledState(d: boolean): void;
7135
- protected onSelect(row: string, column: string): void;
7136
- /** True when a required matrix has no valid choice in this row (shown on the row header and the dots). */
7137
- protected _rowInvalid(row: RadioMatrixRow): boolean;
7138
- /** Touched once focus leaves the whole matrix (moving between its radios does not count). */
7139
- protected onFocusOut(event: FocusEvent): void;
7140
- private _toRecord;
8960
+ validate(control: AbstractControl): ValidationErrors | null;
8961
+ protected onTextInput(event: Event): void;
8962
+ protected onNativeInput(event: Event): void;
8963
+ protected selectPreset(p: ColorPreset): void;
8964
+ protected _isSelected(p: ColorPreset): boolean;
8965
+ protected _presetColor(p: ColorPreset): string;
8966
+ protected onTextBlur(): void;
8967
+ /** Blur of the native picker only marks the field touched; it never rewrites the text. */
8968
+ protected onNativeBlur(): void;
8969
+ private _touch;
8970
+ private _commit;
7141
8971
  private _check;
7142
- static ɵfac: i0.ɵɵFactoryDeclaration<RadioMatrixComponent, never>;
7143
- static ɵcmp: i0.ɵɵComponentDeclaration<RadioMatrixComponent, "ui-radio-matrix", never, {
8972
+ static ɵfac: i0.ɵɵFactoryDeclaration<ColorInputComponent, never>;
8973
+ static ɵcmp: i0.ɵɵComponentDeclaration<ColorInputComponent, "ui-color-input", never, {
7144
8974
  "label": {
7145
8975
  "alias": "label";
7146
8976
  "required": false;
7147
8977
  "isSignal": true;
7148
8978
  };
7149
- "rowHeader": {
7150
- "alias": "rowHeader";
7151
- "required": false;
7152
- "isSignal": true;
7153
- };
7154
- "rows": {
7155
- "alias": "rows";
8979
+ "ariaLabel": {
8980
+ "alias": "ariaLabel";
7156
8981
  "required": false;
7157
8982
  "isSignal": true;
7158
8983
  };
7159
- "columns": {
7160
- "alias": "columns";
8984
+ "placeholder": {
8985
+ "alias": "placeholder";
7161
8986
  "required": false;
7162
8987
  "isSignal": true;
7163
8988
  };
@@ -7181,8 +9006,13 @@ export declare class RadioMatrixComponent implements ControlValueAccessor, Valid
7181
9006
  "required": false;
7182
9007
  "isSignal": true;
7183
9008
  };
7184
- "name": {
7185
- "alias": "name";
9009
+ "allowAlpha": {
9010
+ "alias": "allowAlpha";
9011
+ "required": false;
9012
+ "isSignal": true;
9013
+ };
9014
+ "presets": {
9015
+ "alias": "presets";
7186
9016
  "required": false;
7187
9017
  "isSignal": true;
7188
9018
  };
@@ -7196,142 +9026,330 @@ export declare class RadioMatrixComponent implements ControlValueAccessor, Valid
7196
9026
  }, never, never, true, never>;
7197
9027
  }
7198
9028
  /**
7199
- * Card for detail pages: an optional header and a body, both filled by the page that uses it.
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`.
7200
9031
  *
7201
- * Header (all optional - set `[showHeader]="false"` and the card is just the body):
7202
- * - `title` / `subtitle`: the bold uppercase title and the grey line under it.
7203
- * - `titleId`: an id for the title text, which also names the card for assistive tech (aria-labelledby).
7204
- * - `<div card-header-leading>`: something before the title block, such as a logo or an icon.
7205
- * - `<ng-container card-header>`: your own header content, shown under the title (or instead of it when no title is set).
7206
- * - `<ng-container card-header-action>`: buttons or links on the right side of the header.
7207
- * - `showEdit` + `editLabel` + `(editClick)`: the built-in Edit button.
7208
- * - `collapsible` (default true): a chevron toggle and a clickable header; `[(expanded)]` is two-way.
7209
- * Without a header, or with `collapsible` false, the body is always open.
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>
7210
9041
  *
7211
- * The title is always shown in capitals; the subtitle keeps the case you give it. The body is the default content slot. A collapsed body is removed from the tab order and from assistive tech.
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.
7212
9060
  *
7213
- * <ui-detail-card title="COMMUNICATIONS" subtitle="All calls, texts, and emails"> ...body... </ui-detail-card>
7214
- * <ui-detail-card title="CONTACT INFO" [showEdit]="true" (editClick)="openEdit()"> ... </ui-detail-card>
7215
- * <ui-detail-card [showHeader]="false"> ...body only... </ui-detail-card>
7216
- * <ui-detail-card [collapsible]="false">
7217
- * <div card-header>Any markup</div>
7218
- * <ui-button card-header-action size="xs" variant="secondary">Add</ui-button>
7219
- * ...body...
7220
- * </ui-detail-card>
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.
7221
9070
  */
7222
- export declare class DetailCardComponent {
7223
- /** Bold uppercase title. Empty = no title text. */
9071
+ export declare class SectionHeaderComponent {
9072
+ /** The heading text. */
7224
9073
  readonly title: import("@angular/core").InputSignal<string>;
7225
- /** 1-6 makes the title a real heading for screen readers (role=heading, aria-level); null keeps it plain text. */
7226
- readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 3 | 4 | 5 | 6 | null>;
7227
- /** Optional id for the title text; the card is then labelled by it. Without it the title is the card's aria-label. */
7228
- readonly titleId: import("@angular/core").InputSignal<string>;
7229
- /** Grey line under the title. */
7230
- readonly subtitle: import("@angular/core").InputSignal<string>;
7231
- /** false = no header at all (no title, edit button, toggle or divider): the card is only the body, always open. */
7232
- readonly showHeader: import("@angular/core").InputSignal<boolean>;
7233
- /** Chevron toggle + clickable header. false = the body is always open. */
7234
- readonly collapsible: import("@angular/core").InputSignal<boolean>;
7235
- /** Two-way: whether the body is open. */
7236
- readonly expanded: import("@angular/core").ModelSignal<boolean>;
7237
- /** Show the built-in Edit button in the header. */
7238
- readonly showEdit: import("@angular/core").InputSignal<boolean>;
7239
- readonly editLabel: import("@angular/core").InputSignal<string>;
7240
- readonly editClick: import("@angular/core").OutputEmitterRef<void>;
7241
- /** The body is open unless the card has a header that is collapsible and was collapsed. */
7242
- isOpen(): boolean;
7243
- toggle(): void;
7244
- /** A click on the header toggles, except on a control inside it (Edit, your own header actions, links, inputs). */
7245
- onHeaderClick(event: Event): void;
7246
- static ɵfac: i0.ɵɵFactoryDeclaration<DetailCardComponent, never>;
7247
- static ɵcmp: i0.ɵɵComponentDeclaration<DetailCardComponent, "ui-detail-card", never, {
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, {
7248
9098
  "title": {
7249
9099
  "alias": "title";
9100
+ "required": true;
9101
+ "isSignal": true;
9102
+ };
9103
+ "description": {
9104
+ "alias": "description";
7250
9105
  "required": false;
7251
9106
  "isSignal": true;
7252
9107
  };
7253
- "headingLevel": {
7254
- "alias": "headingLevel";
9108
+ "icon": {
9109
+ "alias": "icon";
7255
9110
  "required": false;
7256
9111
  "isSignal": true;
7257
9112
  };
7258
- "titleId": {
7259
- "alias": "titleId";
9113
+ "level": {
9114
+ "alias": "level";
7260
9115
  "required": false;
7261
9116
  "isSignal": true;
7262
9117
  };
7263
- "subtitle": {
7264
- "alias": "subtitle";
9118
+ "background": {
9119
+ "alias": "background";
7265
9120
  "required": false;
7266
9121
  "isSignal": true;
7267
9122
  };
7268
- "showHeader": {
7269
- "alias": "showHeader";
9123
+ "textColor": {
9124
+ "alias": "textColor";
7270
9125
  "required": false;
7271
9126
  "isSignal": true;
7272
9127
  };
7273
- "collapsible": {
7274
- "alias": "collapsible";
9128
+ "iconColor": {
9129
+ "alias": "iconColor";
7275
9130
  "required": false;
7276
9131
  "isSignal": true;
7277
9132
  };
7278
- "expanded": {
7279
- "alias": "expanded";
9133
+ "iconBackground": {
9134
+ "alias": "iconBackground";
7280
9135
  "required": false;
7281
9136
  "isSignal": true;
7282
9137
  };
7283
- "showEdit": {
7284
- "alias": "showEdit";
9138
+ "bordered": {
9139
+ "alias": "bordered";
7285
9140
  "required": false;
7286
9141
  "isSignal": true;
7287
9142
  };
7288
- "editLabel": {
7289
- "alias": "editLabel";
9143
+ "size": {
9144
+ "alias": "size";
7290
9145
  "required": false;
7291
9146
  "isSignal": true;
7292
9147
  };
7293
- }, {
7294
- "expanded": "expandedChange";
7295
- "editClick": "editClick";
7296
- }, never, ["[card-header-leading]", "[card-header]", "[card-header-action]", "*"], true, never>;
9148
+ }, {}, never, ["[section-header-icon]", "[section-header-actions]"], true, never>;
7297
9149
  }
7298
- export type DividerSpace = 'none' | 'sm' | 'md' | 'lg';
7299
9150
  /**
7300
- * Horizontal divider line (an `<hr>`) with the app's standard colour and optional space above / below.
7301
- * It is a real separator for assistive tech by default; set `decorative` when it only adds visual rhythm and the
7302
- * content around it already reads as separate (the line is then hidden from screen readers).
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`).
7303
9169
  *
7304
- * <section class="kpi-grid"> ... </section>
7305
- * <ui-divider /> // line with 16px below
7306
- * <ui-divider spaceAbove="md" spaceBelow="lg" />
7307
- * <ui-divider [decorative]="true" />
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>
7308
9187
  */
7309
- export declare class DividerComponent {
7310
- /** Space above the line: none (0), sm (8px), md (16px), lg (24px). */
7311
- readonly spaceAbove: import("@angular/core").InputSignal<DividerSpace>;
7312
- /** Space below the line: none (0), sm (8px), md (16px), lg (24px). */
7313
- readonly spaceBelow: import("@angular/core").InputSignal<DividerSpace>;
7314
- /** true = purely visual: hidden from assistive tech. */
7315
- readonly decorative: import("@angular/core").InputSignal<boolean>;
7316
- protected readonly above: import("@angular/core").Signal<number>;
7317
- protected readonly below: import("@angular/core").Signal<number>;
7318
- static ɵfac: i0.ɵɵFactoryDeclaration<DividerComponent, never>;
7319
- static ɵcmp: i0.ɵɵComponentDeclaration<DividerComponent, "ui-divider", never, {
7320
- "spaceAbove": {
7321
- "alias": "spaceAbove";
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";
7322
9200
  "required": false;
7323
9201
  "isSignal": true;
7324
9202
  };
7325
- "spaceBelow": {
7326
- "alias": "spaceBelow";
9203
+ "gap": {
9204
+ "alias": "gap";
7327
9205
  "required": false;
7328
9206
  "isSignal": true;
7329
9207
  };
7330
- "decorative": {
7331
- "alias": "decorative";
9208
+ "collapseAt": {
9209
+ "alias": "collapseAt";
7332
9210
  "required": false;
7333
9211
  "isSignal": true;
7334
9212
  };
7335
- }, {}, never, never, true, never>;
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>;
7336
9354
  }
7337
9355
  export type { CreateDocumentRequestBody, DocuSignSendBody, DocumentSentInfo, DocumentSentMessage, DocumentsPayloadExtras, ExistingEnvelopeSendBody, SendDocumentsPayload, SendEmailPayload, SendTextPayload, VideoDefaults, VideoLinkOptions };