@forgeroute/ui 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { ChangeDetectorRef, EnvironmentProviders, InjectionToken, OnDestroy, OnInit, Signal, TemplateRef, WritableSignal } from "@angular/core";
3
+ import { UrlTree } from "@angular/router";
3
4
  import { AbstractControl, ControlValueAccessor, FormControl, ValidationErrors, Validator } from "@angular/forms";
4
5
  import { Observable, Subject } from "rxjs";
5
6
  import { HttpClient, HttpInterceptorFn } from "@angular/common/http";
@@ -99,7 +100,38 @@ export interface RowAction {
99
100
  /** Page-level permission passed to `<ui-data-table [permission]>`. Anything other than 'full' is read-only
100
101
  * (apps use values like 'read-only' / 'hidden'). */
101
102
  export type TablePermission = 'full' | 'read' | 'read-only' | 'none' | 'hidden';
102
- export type CellType = 'text' | 'badge' | 'date' | 'phone' | 'number' | 'avatar-name' | 'days-ago' | 'progress' | 'tags' | 'image' | 'actions';
103
+ export type CellType = 'text' | 'badge' | 'date' | 'phone' | 'number' | 'avatar-name' | 'days-ago' | 'progress' | 'tags' | 'image' | 'link' | 'actions';
104
+ /**
105
+ * Where a `cellType: 'link'` cell points. Return a plain string from `linkFn` as shorthand for `{ href }`.
106
+ *
107
+ * - `routerLink` (+ `queryParams`) is an in-app route: the anchor gets the real URL as its `href` (so right-click,
108
+ * middle-click, ctrl/cmd-click, "open in new tab" and "copy link address" all work) and a plain left click
109
+ * navigates through the Angular `Router` without a page reload. `routerLink` wins when `href` is also set.
110
+ * - `href` accepts ONLY `mailto:`, `tel:`, `http:`, `https:` and root-relative paths (`/cases/12`). Anything else
111
+ * (`javascript:`, `data:`, `//host`, empty, control characters) is never rendered as an href: the cell shows plain
112
+ * text. A root-relative `href` is a normal link (full page load); use `routerLink` for in-app navigation.
113
+ * - `target: '_blank'` (default `'_self'`) adds `rel="noopener noreferrer"` and a "(opens in a new tab)" screen-reader
114
+ * hint. `http(s):` hrefs always get the same `rel`.
115
+ * - `label` is the link text when the cell has no text of its own (`render` and `item[key]` are empty).
116
+ * - `ariaLabel` replaces the link's accessible name (default: its text).
117
+ * - `download` adds the `download` attribute.
118
+ */
119
+ export interface LinkTarget {
120
+ href?: string;
121
+ routerLink?: string | readonly any[];
122
+ queryParams?: Record<string, any>;
123
+ target?: '_self' | '_blank';
124
+ label?: string;
125
+ ariaLabel?: string;
126
+ download?: boolean;
127
+ }
128
+ /** Payload of `(linkClick)`: a link cell's anchor was activated by a click (the row click is NOT also emitted). */
129
+ export interface LinkClickEvent<T = any> {
130
+ item: T;
131
+ column: ColumnDef;
132
+ /** The anchor's href (null only for a routerLink that could not be resolved, which renders no anchor anyway). */
133
+ href: string | null;
134
+ }
103
135
  export interface ColumnDef {
104
136
  key: string;
105
137
  label: string;
@@ -133,6 +165,13 @@ export interface ColumnDef {
133
165
  * Return null to render a plain '—' instead of a badge (e.g. for an empty value).
134
166
  */
135
167
  badgeFn?: (value: any, item?: any) => BadgeConfig | null;
168
+ /**
169
+ * Puts a gold `<ui-star>` to the LEFT of the cell's text when it returns a non-empty string; that string is the star's tooltip
170
+ * (hover / keyboard focus) and accessible name. Return null / undefined / '' for no star. The star is always gold.
171
+ * Applies to plain text cells (and the title of the phone card); the row's accessible name is unchanged (`nameFn` / `render`).
172
+ * Example: `{ key: 'name', label: 'Name', starFn: (doc) => doc.isAuthForm ? 'Authorization Form' : null }`
173
+ */
174
+ starFn?: (item: any) => string | null | undefined;
136
175
  /** Date format string. Default: 'MMM d, y' */
137
176
  dateFormat?: string;
138
177
  /** Returns the display name for the row. Default: item[col.key] */
@@ -158,6 +197,15 @@ export interface ColumnDef {
158
197
  /** Key in item holding the tags array. Defaults to col.key.
159
198
  * Expected shape: { name: string; variant?: BadgeVariant }[] */
160
199
  tagsKey?: string;
200
+ /**
201
+ * `cellType: 'link'` — returns where the cell points for a row (`value` is `item[key]`), or null / undefined /
202
+ * '' for no link (the cell then shows plain text). A string is shorthand for `{ href }`. See {@link LinkTarget}
203
+ * for the contract and the allowed schemes. Text shown: `render(item)` if set, else `item[key]`, else the
204
+ * target's `label`, else '—'. The text is always escaped. A throwing `linkFn` renders plain text.
205
+ * Sorting, search, filters and export are independent of the link (export uses the host's `exportRow`).
206
+ * Clicking or keying the anchor never emits `rowClick`; it emits `(linkClick)`.
207
+ */
208
+ linkFn?: (item: any, value: any) => LinkTarget | string | null | undefined;
161
209
  /** Returns the image URL for the row. A falsy value (null / undefined / '') shows the placeholder icon. */
162
210
  imageFn?: (item: any) => string | null | undefined;
163
211
  /** Focal point (percent, 50/50 = centered) of the cropped image, applied as CSS object-position. */
@@ -887,6 +935,11 @@ export declare function formatTime(value: string | null | undefined): string;
887
935
  export interface SelectOption<V extends string | number = string> {
888
936
  value: V;
889
937
  label: string;
938
+ /**
939
+ * Optional longer explanation of this choice. The select shows the SELECTED option's description under the field (linked with
940
+ * `aria-describedby`) and sets it as the option's `title` tooltip where the browser shows one. Options without it are unchanged.
941
+ */
942
+ description?: string;
890
943
  }
891
944
  export declare class SelectComponent implements ControlValueAccessor, Validator, UiFieldValidity {
892
945
  readonly label: import("@angular/core").InputSignal<string>;
@@ -908,6 +961,10 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
908
961
  protected readonly _value: import("@angular/core").WritableSignal<string>;
909
962
  /** Index of the first option matching the written / chosen value, or -1 (so only ONE option is selected even with duplicate keys). */
910
963
  protected readonly _selectedIndex: import("@angular/core").Signal<number>;
964
+ /** The selected option's `description` ('' when none is selected or it has none); hidden while an error shows. */
965
+ protected readonly _selectedDescription: import("@angular/core").Signal<string>;
966
+ /** ids of the texts that describe the select: the error / hint, then the selected option's description. */
967
+ protected readonly _describedBy: import("@angular/core").Signal<string>;
911
968
  private readonly _reveal;
912
969
  private readonly _issue;
913
970
  /** True while the value breaks a built-in rule or the host passed an `[error]`. */
@@ -1114,6 +1171,15 @@ export declare class ImportExportComponent {
1114
1171
  }
1115
1172
  /** Simple English plural: "property" → "properties", "box" → "boxes", "contact" → "contacts". */
1116
1173
  export declare function pluralize(word: string): string;
1174
+ /** Resolved anchor data of a link cell (internal). */
1175
+ interface ResolvedLink {
1176
+ href: string;
1177
+ tree: UrlTree | null;
1178
+ blank: boolean;
1179
+ rel: string | null;
1180
+ download: boolean;
1181
+ ariaLabel: string | null;
1182
+ }
1117
1183
  export declare class DataTableComponent<T extends Record<string, any>> implements OnInit {
1118
1184
  readonly endpoint: import("@angular/core").InputSignal<string>;
1119
1185
  readonly columns: import("@angular/core").InputSignal<ColumnDef[]>;
@@ -1121,6 +1187,12 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1121
1187
  readonly pageSize: import("@angular/core").InputSignal<number>;
1122
1188
  readonly tableLabel: import("@angular/core").InputSignal<string>;
1123
1189
  readonly emptyMessage: import("@angular/core").InputSignal<string>;
1190
+ /**
1191
+ * Text of the retry button in the failed-load state (desktop table and phone cards). Plain text.
1192
+ * Empty / blank keeps the error state's own default ("Try again").
1193
+ */
1194
+ readonly retryLabel: import("@angular/core").InputSignal<string>;
1195
+ protected readonly _retryLabel: Signal<string>;
1124
1196
  readonly lockedColumn: import("@angular/core").InputSignal<string>;
1125
1197
  readonly savedViews: import("@angular/core").InputSignal<SavedView[]>;
1126
1198
  /**
@@ -1131,6 +1203,14 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1131
1203
  readonly page: import("@angular/core").InputSignal<string>;
1132
1204
  /** Show column config + saved views toolbar. Defaults true. Set false to hide both. */
1133
1205
  readonly showToolbar: import("@angular/core").InputSignal<boolean>;
1206
+ /**
1207
+ * Show the search box above the table. Defaults true. Set false for small fixed tables where searching is meaningless
1208
+ * (a handful of summary rows). Quick-filter tabs and the toolbar are unaffected; when the search box, the tabs and the
1209
+ * toolbar are all off, the header row above the table is not rendered at all.
1210
+ */
1211
+ readonly showSearch: import("@angular/core").InputSignal<boolean>;
1212
+ /** False when the header row would be empty (no search box, no toolbar, no quick-filter tabs). */
1213
+ protected readonly _showHeader: Signal<boolean>;
1134
1214
  /** Optional endpoint that returns Record<string,number> counts per quick-filter value. */
1135
1215
  readonly countsEndpoint: import("@angular/core").InputSignal<string>;
1136
1216
  /**
@@ -1179,6 +1259,12 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1179
1259
  */
1180
1260
  readonly bulkUpdateOptions: import("@angular/core").InputSignal<BulkUpdateOption<T>[]>;
1181
1261
  readonly rowClick: import("@angular/core").OutputEmitterRef<T>;
1262
+ /**
1263
+ * A `cellType: 'link'` anchor was clicked (plain or with ctrl/cmd/shift; a middle-button click is an `auxclick`, not emitted). Fires instead of
1264
+ * `rowClick`: the anchor stops the click from reaching the row. For an in-app `routerLink` the table has already
1265
+ * called `Router.navigateByUrl` on a plain left click (modifier / `_blank` clicks are left to the browser).
1266
+ */
1267
+ readonly linkClick: import("@angular/core").OutputEmitterRef<LinkClickEvent<T>>;
1182
1268
  /**
1183
1269
  * Optional per-row opt-out for (rowClick): rows where it returns false get no pointer cursor,
1184
1270
  * no tab stop and no click/keyboard activation (e.g. log rows with no linked record).
@@ -1447,6 +1533,13 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1447
1533
  activeValues: string[];
1448
1534
  };
1449
1535
  }[]>;
1536
+ /**
1537
+ * The sort buttons / icons and the column-filter icons only show while there is something to sort or filter: not after a failed
1538
+ * load, and not when the table is empty because nothing exists or only a search / quick tab matched nothing (clear the search to
1539
+ * get back). Once the user has used a column header control (a column filter or a sort is active) the controls stay, even with
1540
+ * zero rows, so they can change or clear it. Headers are plain labels otherwise.
1541
+ */
1542
+ protected readonly _showColumnControls: Signal<boolean>;
1450
1543
  protected readonly _hasPhoneControls: Signal<boolean>;
1451
1544
  /** Phone sort select → sets the sort directly (same state, page reset and load as the header's onSort). */
1452
1545
  protected onPhoneSort(value: string): void;
@@ -1505,6 +1598,16 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1505
1598
  protected imageClass(col: ColumnDef, small: boolean): string;
1506
1599
  protected onImageError(url: string): void;
1507
1600
  protected phoneCell(value: any): string;
1601
+ private readonly _injector;
1602
+ /** Safe hrefs only: mailto:, tel:, http(s):// and root-relative paths. Everything else is refused (null). */
1603
+ private _safeHref;
1604
+ /** Text and (when there is a safe target) the anchor data of a `cellType: 'link'` cell. Never throws. */
1605
+ protected linkView(col: ColumnDef, item: T): {
1606
+ text: string;
1607
+ link: ResolvedLink | null;
1608
+ };
1609
+ /** Anchor click: never reaches the row; a plain left click on an in-app route navigates via the Router (SPA, no reload). */
1610
+ protected onLinkClick(e: MouseEvent, link: ResolvedLink, col: ColumnDef, item: T): void;
1508
1611
  protected getInitials(name: string): string;
1509
1612
  protected isCardOverdue(col: ColumnDef, item: T): boolean;
1510
1613
  /** Renders any non-badge column value as a plain string for the mobile card view. */
@@ -1561,6 +1664,11 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1561
1664
  "required": false;
1562
1665
  "isSignal": true;
1563
1666
  };
1667
+ "retryLabel": {
1668
+ "alias": "retryLabel";
1669
+ "required": false;
1670
+ "isSignal": true;
1671
+ };
1564
1672
  "lockedColumn": {
1565
1673
  "alias": "lockedColumn";
1566
1674
  "required": false;
@@ -1581,6 +1689,11 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1581
1689
  "required": false;
1582
1690
  "isSignal": true;
1583
1691
  };
1692
+ "showSearch": {
1693
+ "alias": "showSearch";
1694
+ "required": false;
1695
+ "isSignal": true;
1696
+ };
1584
1697
  "countsEndpoint": {
1585
1698
  "alias": "countsEndpoint";
1586
1699
  "required": false;
@@ -1648,6 +1761,7 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1648
1761
  };
1649
1762
  }, {
1650
1763
  "rowClick": "rowClick";
1764
+ "linkClick": "linkClick";
1651
1765
  "tableButtonClick": "tableButtonClick";
1652
1766
  "panelActionClick": "panelActionClick";
1653
1767
  "saveViewClick": "saveViewClick";
@@ -2117,11 +2231,27 @@ export declare class AudioPlayerComponent {
2117
2231
  "playbackError": "playbackError";
2118
2232
  }, never, never, true, never>;
2119
2233
  }
2234
+ /**
2235
+ * Shimmering placeholder for content that is loading.
2236
+ *
2237
+ * By default it is purely decorative (hidden from assistive tech), like before. Give ONE skeleton - or the wrapper of a group of them - an
2238
+ * `ariaLabel` to announce the loading state: the host then becomes a polite live region (`role="status"`, `aria-live="polite"`,
2239
+ * `aria-busy="true"`, named by the label) and is no longer hidden. Remove the skeleton (or the label) when the content has loaded.
2240
+ *
2241
+ * <ui-skeleton [height]="44" ariaLabel="Loading submissions" />
2242
+ * <div role="status" aria-label="Loading report"> <ui-skeleton /> <ui-skeleton /> </div> <!-- decorative skeletons inside a labelled wrapper -->
2243
+ *
2244
+ * Do not label every skeleton of a group: each labelled skeleton is its own live region and would be announced separately.
2245
+ */
2120
2246
  export declare class SkeletonComponent {
2121
2247
  readonly height: import("@angular/core").InputSignal<number>;
2122
2248
  readonly width: import("@angular/core").InputSignal<string>;
2123
2249
  readonly radius: import("@angular/core").InputSignal<number>;
2124
2250
  readonly mb: import("@angular/core").InputSignal<number>;
2251
+ /** Accessible name that turns the skeleton into a polite status region ("Loading ..."). Empty (default) = decorative, hidden from assistive tech. */
2252
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
2253
+ /** The trimmed label ('' = none). */
2254
+ label(): string;
2125
2255
  static ɵfac: i0.ɵɵFactoryDeclaration<SkeletonComponent, never>;
2126
2256
  static ɵcmp: i0.ɵɵComponentDeclaration<SkeletonComponent, "ui-skeleton", never, {
2127
2257
  "height": {
@@ -2144,6 +2274,11 @@ export declare class SkeletonComponent {
2144
2274
  "required": false;
2145
2275
  "isSignal": true;
2146
2276
  };
2277
+ "ariaLabel": {
2278
+ "alias": "ariaLabel";
2279
+ "required": false;
2280
+ "isSignal": true;
2281
+ };
2147
2282
  }, {}, never, never, true, never>;
2148
2283
  }
2149
2284
  /**
@@ -2177,6 +2312,33 @@ export declare class TooltipDirective {
2177
2312
  };
2178
2313
  }, {}, never, never, true, never>;
2179
2314
  }
2315
+ /**
2316
+ * A small gold star with a hover / focus tooltip: `<ui-star tooltip="Authorization Form" />`.
2317
+ *
2318
+ * The star is always gold (there is no color input). `tooltip` is the text the user sees on hover or keyboard focus (library `uiTooltip`);
2319
+ * it is also the star's accessible name, so a screen reader reads it too. Without a tooltip the star is decorative (hidden from assistive tech).
2320
+ * In `<ui-data-table>` use `ColumnDef.starFn` to put one to the left of a text cell (e.g. the name column).
2321
+ */
2322
+ export declare class StarComponent {
2323
+ /** Tooltip text (also the accessible name). Empty = no tooltip and the star is decorative. */
2324
+ readonly tooltip: import("@angular/core").InputSignal<string>;
2325
+ /** Star size in px. Default 16. */
2326
+ readonly size: import("@angular/core").InputSignal<number>;
2327
+ protected readonly hasTooltip: import("@angular/core").Signal<boolean>;
2328
+ static ɵfac: i0.ɵɵFactoryDeclaration<StarComponent, never>;
2329
+ static ɵcmp: i0.ɵɵComponentDeclaration<StarComponent, "ui-star", never, {
2330
+ "tooltip": {
2331
+ "alias": "tooltip";
2332
+ "required": false;
2333
+ "isSignal": true;
2334
+ };
2335
+ "size": {
2336
+ "alias": "size";
2337
+ "required": false;
2338
+ "isSignal": true;
2339
+ };
2340
+ }, {}, never, never, true, never>;
2341
+ }
2180
2342
  export type ImageRepositionShape = 'circle' | 'rounded';
2181
2343
  /**
2182
2344
  * Drag-to-center photo frame. The image fills the frame (`object-fit: cover`) and the user drags it, or uses the arrow
@@ -3073,7 +3235,7 @@ export declare class ButtonComponent {
3073
3235
  */
3074
3236
  readonly href: import("@angular/core").InputSignal<string | null>;
3075
3237
  /** Where to open the `href` (`_blank` = a new tab). */
3076
- readonly target: import("@angular/core").InputSignal<"_blank" | "_self" | "_parent" | "_top" | null>;
3238
+ readonly target: import("@angular/core").InputSignal<"_self" | "_blank" | "_parent" | "_top" | null>;
3077
3239
  /** `rel` of the `href` link. A `_blank` target always gets `noopener noreferrer` added. */
3078
3240
  readonly rel: import("@angular/core").InputSignal<string | null>;
3079
3241
  /** The href when it is allowed (http, https, mailto, tel, sms or a relative URL), else null. */
@@ -6025,6 +6187,12 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
6025
6187
  readonly max: import("@angular/core").InputSignal<number | undefined>;
6026
6188
  /** `type="number"` only: most digits allowed after the decimal point. */
6027
6189
  readonly maxDecimals: import("@angular/core").InputSignal<number | undefined>;
6190
+ /**
6191
+ * `type="number"` only: how much the browser's spinner arrows and the Up / Down keys change the value (e.g. `0.01` for money).
6192
+ * Leave it unset for the browser's default step of 1. A value that is not a number above 0 is ignored (also the default step).
6193
+ * It only picks the increment; it does not restrict what can be typed (use `maxDecimals` for that).
6194
+ */
6195
+ readonly step: import("@angular/core").InputSignal<number | undefined>;
6028
6196
  /**
6029
6197
  * What the control value is. `'string'` (default) = the typed text. `'number'` = `number | null`; requires `type="number"`
6030
6198
  * (otherwise a dev-mode warning is logged once and string behaviour is used).
@@ -6043,6 +6211,8 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
6043
6211
  private readonly _reveal;
6044
6212
  /** True when the control value is a number: `valueType="number"` on a `type="number"` field. */
6045
6213
  private readonly _numeric;
6214
+ /** The native `step` attribute: only for `type="number"` and only for a finite number above 0; otherwise none (the browser's step of 1). */
6215
+ protected readonly _stepAttr: import("@angular/core").Signal<string | null>;
6046
6216
  private _warned;
6047
6217
  private readonly _formatting;
6048
6218
  private readonly _issue;
@@ -6152,6 +6322,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
6152
6322
  "required": false;
6153
6323
  "isSignal": true;
6154
6324
  };
6325
+ "step": {
6326
+ "alias": "step";
6327
+ "required": false;
6328
+ "isSignal": true;
6329
+ };
6155
6330
  "valueType": {
6156
6331
  "alias": "valueType";
6157
6332
  "required": false;
@@ -6190,6 +6365,8 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
6190
6365
  */
6191
6366
  readonly hintAsDescription: import("@angular/core").InputSignal<boolean>;
6192
6367
  readonly error: import("@angular/core").InputSignal<string>;
6368
+ /** Opt-in. `true` shows the text in a monospace font (HTML / code / merge-tag fields). Default `false` = unchanged. */
6369
+ readonly monospace: import("@angular/core").InputSignal<boolean>;
6193
6370
  /** Reveal the built-in error messages now (e.g. after a failed Save click), without waiting for blur. */
6194
6371
  readonly showErrors: import("@angular/core").InputSignal<boolean>;
6195
6372
  protected readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
@@ -6280,6 +6457,11 @@ export declare class TextareaInputComponent implements ControlValueAccessor, Val
6280
6457
  "required": false;
6281
6458
  "isSignal": true;
6282
6459
  };
6460
+ "monospace": {
6461
+ "alias": "monospace";
6462
+ "required": false;
6463
+ "isSignal": true;
6464
+ };
6283
6465
  "showErrors": {
6284
6466
  "alias": "showErrors";
6285
6467
  "required": false;
@@ -7026,6 +7208,8 @@ export interface DropdownItem {
7026
7208
  label: string;
7027
7209
  value: string;
7028
7210
  count?: number;
7211
+ /** Greyed-out, not selectable and skipped by keyboard navigation. */
7212
+ disabled?: boolean;
7029
7213
  }
7030
7214
  export declare class DropdownComponent {
7031
7215
  readonly label: import("@angular/core").InputSignal<string>;
@@ -7036,16 +7220,54 @@ export declare class DropdownComponent {
7036
7220
  readonly activeValue: import("@angular/core").InputSignal<string>;
7037
7221
  readonly variant: import("@angular/core").InputSignal<"badge" | "button">;
7038
7222
  readonly menuWidth: import("@angular/core").InputSignal<string>;
7223
+ /**
7224
+ * Accessible name. Set it when the trigger has no meaningful visible text. Applied as `aria-label` on the
7225
+ * trigger and on the menu; when empty (default) no `aria-label` is rendered, the trigger is named by its
7226
+ * visible label and the menu by `aria-labelledby` pointing at the trigger. Surrounding whitespace is trimmed.
7227
+ */
7228
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
7039
7229
  readonly itemClick: import("@angular/core").OutputEmitterRef<DropdownItem>;
7040
7230
  protected readonly open: import("@angular/core").WritableSignal<boolean>;
7041
7231
  protected readonly loading: import("@angular/core").WritableSignal<boolean>;
7042
7232
  private readonly _loaded;
7043
7233
  private readonly _id;
7234
+ private readonly _host;
7235
+ private readonly _cdr;
7236
+ private _pendingFocus;
7237
+ private _typeahead;
7238
+ private _typeaheadTimer;
7239
+ private _destroyed;
7240
+ protected readonly triggerId: string;
7241
+ protected readonly menuId: string;
7242
+ protected readonly _ariaLabel: import("@angular/core").Signal<string | null>;
7044
7243
  protected readonly displayItems: import("@angular/core").Signal<DropdownItem[]>;
7045
7244
  constructor();
7046
7245
  toggle(event: Event): void;
7047
7246
  select(item: DropdownItem): void;
7048
7247
  close(): void;
7248
+ /**
7249
+ * Keyboard contract (WAI-ARIA menu button).
7250
+ * Trigger: Enter / Space toggle (opening focuses the first item), ArrowDown opens + first item,
7251
+ * ArrowUp opens + last item, ArrowDown/ArrowUp while open move into the menu, Tab closes (focus moves on naturally).
7252
+ */
7253
+ protected onTriggerKeydown(event: KeyboardEvent): void;
7254
+ /** Firefox fires the button click on Space keyup even when keydown was prevented. */
7255
+ protected onTriggerKeyup(event: KeyboardEvent): void;
7256
+ /**
7257
+ * Menu keys (focus on an item): ArrowDown / ArrowUp move with wrap, Home / End jump to first / last,
7258
+ * Enter / Space activate the focused item (native button click), Tab / Shift+Tab close and let focus move on from the trigger,
7259
+ * printable characters jump to the next item whose label starts with the typed text (typeahead, reset after 500 ms).
7260
+ * Disabled items are skipped. Other keys are ignored. Escape is handled on the host.
7261
+ */
7262
+ protected onMenuKeydown(event: KeyboardEvent): void;
7263
+ protected onEscape(event: Event): void;
7264
+ private _setOpen;
7265
+ private _openAndFocus;
7266
+ /** Renders the menu synchronously, then focuses the pending first / last enabled item. */
7267
+ private _focusPending;
7268
+ private _typeaheadFocus;
7269
+ private _enabledItems;
7270
+ private _focusTrigger;
7049
7271
  static ɵfac: i0.ɵɵFactoryDeclaration<DropdownComponent, never>;
7050
7272
  static ɵcmp: i0.ɵɵComponentDeclaration<DropdownComponent, "ui-dropdown", never, {
7051
7273
  "label": {
@@ -7083,6 +7305,11 @@ export declare class DropdownComponent {
7083
7305
  "required": false;
7084
7306
  "isSignal": true;
7085
7307
  };
7308
+ "ariaLabel": {
7309
+ "alias": "ariaLabel";
7310
+ "required": false;
7311
+ "isSignal": true;
7312
+ };
7086
7313
  }, {
7087
7314
  "itemClick": "itemClick";
7088
7315
  }, never, never, true, never>;
@@ -7524,6 +7751,12 @@ export declare class ListComponent<T = unknown> {
7524
7751
  * The row has no list role of its own: put `role="list"` on the host container and `role="listitem"` on each `ui-bar-row`
7525
7752
  * (or wrap them in `ui-list`). The label names the inner progressbar (`aria-labelledby`) and `valueText`
7526
7753
  * (default: the count plus the share) is its `aria-valuetext`.
7754
+ *
7755
+ * Out-of-range input: only the BAR is clamped, never the text the user reads. The fill is always 0-100% of the track:
7756
+ * `value` below 0, NaN, -Infinity or not a number (string, null, undefined) draws no fill; `value` above `max`
7757
+ * (including +Infinity) fills 100%; `max` of 0, negative, NaN, +/-Infinity or not a number draws an empty bar. The progressbar's
7758
+ * `aria-valuenow` is clamped to [0, max] and `aria-valuemax` is 0 for an invalid max. The count text keeps the real `value`
7759
+ * (formatted; a non-finite or non-number value reads "0"), and `count` / `share` / `valueText` are shown and announced as given.
7527
7760
  * 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.
7528
7761
  */
7529
7762
  export declare class BarRowComponent {
@@ -7531,7 +7764,7 @@ export declare class BarRowComponent {
7531
7764
  readonly label: import("@angular/core").InputSignal<string>;
7532
7765
  /** This row's amount. */
7533
7766
  readonly value: import("@angular/core").InputSignal<number>;
7534
- /** The amount that fills the bar (e.g. the largest category's count). 0 / NaN / negative draws an empty bar. */
7767
+ /** The amount that fills the bar (e.g. the largest category's count). 0 / NaN / negative / infinite / not a number draws an empty bar. */
7535
7768
  readonly max: import("@angular/core").InputSignal<number>;
7536
7769
  /** Fixed bar colour. Default blue; never follows completion. */
7537
7770
  readonly variant: import("@angular/core").InputSignal<ProgressBarVariant>;
@@ -7545,6 +7778,10 @@ export declare class BarRowComponent {
7545
7778
  readonly locale: import("@angular/core").InputSignal<string>;
7546
7779
  /** Unique id tying the label to the progressbar. */
7547
7780
  protected readonly labelId: string;
7781
+ /** `value` made safe for the bar: anything that is not a real number becomes 0 (the bar clamps the rest to 0..max). */
7782
+ protected readonly barValue: import("@angular/core").Signal<number>;
7783
+ /** `max` made safe for the bar: only a finite number above 0 counts, everything else is 0 (empty bar). */
7784
+ protected readonly barMax: import("@angular/core").Signal<number>;
7548
7785
  protected readonly countText: import("@angular/core").Signal<string>;
7549
7786
  protected readonly effectiveValueText: import("@angular/core").Signal<string>;
7550
7787
  static ɵfac: i0.ɵɵFactoryDeclaration<BarRowComponent, never>;
@@ -8289,13 +8526,17 @@ export declare class ErrorComponent {
8289
8526
  }, {}, never, never, true, never>;
8290
8527
  }
8291
8528
  export type KpiCardColor = 'green' | 'blue' | 'amber' | 'orange' | 'red' | 'slate';
8292
- export type KpiCardValueColor = 'default' | 'green' | 'blue';
8529
+ /** Colour of the big value: `default` (dark slate), `green`, `blue`, or `red` (for figures that need attention: overdue, outstanding, failed). */
8530
+ export type KpiCardValueColor = 'default' | 'green' | 'blue' | 'red';
8293
8531
  /**
8294
8532
  * Compact KPI summary card: optional icon, uppercase label, large value, optional subtitle.
8295
8533
  * Set `clickable` to show a hover state + chevron (bind `(click)` on the host). A clickable card is a keyboard button:
8296
8534
  * role="button", focusable (tabindex 0, visible focus ring), and Enter / Space fire the host's (click). Give it an
8297
8535
  * accessible name with [attr.aria-label] when the label alone isn't enough (e.g. "Net Commission: $12.5K — show details").
8298
8536
  *
8537
+ * Optional background: `background="#fef2f2"` fills the card with any CSS colour (default white), e.g. a soft red for money going out; the
8538
+ * text keeps its own colours, so pick a light shade.
8539
+ *
8299
8540
  * Optional ring: `[progress]="67"` (+ `progressMax`, `progressVariant`, `progressSize`) draws a ui-progress-ring beside the label; extra lines
8300
8541
  * (a pace badge, a projection…) go in an element with the `kpi-footer` attribute.
8301
8542
  *
@@ -8315,6 +8556,8 @@ export declare class KpiCardComponent {
8315
8556
  readonly highlight: import("@angular/core").InputSignal<boolean>;
8316
8557
  readonly clickable: import("@angular/core").InputSignal<boolean>;
8317
8558
  readonly valueColor: import("@angular/core").InputSignal<KpiCardValueColor>;
8559
+ /** Card background as any CSS colour (`#fef2f2`, `var(--surface)` …). Empty (default) = white. The text colours do not change, so use a light shade. */
8560
+ readonly background: import("@angular/core").InputSignal<string>;
8318
8561
  /** Completed amount shown as a `ui-progress-ring` beside the label. `null` (default) = no ring. */
8319
8562
  readonly progress: import("@angular/core").InputSignal<number | null>;
8320
8563
  /** Total for `progress` (default 100, so `progress` is a percentage). */
@@ -8374,6 +8617,11 @@ export declare class KpiCardComponent {
8374
8617
  "required": false;
8375
8618
  "isSignal": true;
8376
8619
  };
8620
+ "background": {
8621
+ "alias": "background";
8622
+ "required": false;
8623
+ "isSignal": true;
8624
+ };
8377
8625
  "progress": {
8378
8626
  "alias": "progress";
8379
8627
  "required": false;
@@ -8411,6 +8659,167 @@ export declare class KpiCardComponent {
8411
8659
  };
8412
8660
  }, {}, never, ["[kpi-footer]"], true, never>;
8413
8661
  }
8662
+ interface StateView {
8663
+ id: string;
8664
+ name: string;
8665
+ path: string;
8666
+ value: number;
8667
+ /** "12 pickups" (or just "12"). */
8668
+ valueText: string;
8669
+ /** "Texas, 12 pickups": the accessible name, the tooltip text and the hidden-list entry. */
8670
+ label: string;
8671
+ fill: string;
8672
+ }
8673
+ /**
8674
+ * US choropleth map: the 50 states plus Washington, DC, each coloured from `colorLow` (value 0) to `colorHigh` (the largest
8675
+ * value) by its number in `data`.
8676
+ *
8677
+ * Interaction and accessibility
8678
+ * - Every state is a button (`role="button"`, name "Texas, 12 pickups", `aria-pressed` for the selected one). The group has a
8679
+ * single tab stop (roving tabindex: last focused state, else the selected one, else Alabama); arrow keys,
8680
+ * Home and End move between states in alphabetical order, Enter / Space emit `stateClick` with the abbreviation.
8681
+ * - The tooltip (state name + value) shows on hover AND on keyboard focus; it is real text, `aria-hidden` because the
8682
+ * state's accessible name already says the same. Colour is never the only signal: values are in names, tooltip and the
8683
+ * visually hidden list of all states.
8684
+ * - A selected state gets an orange outline and the others are dimmed; the focus ring is drawn as an overlay outline
8685
+ * (works the same in Chrome, Edge, Firefox and Safari) and only for keyboard focus.
8686
+ * - `prefers-reduced-motion` turns the transitions off. Strokes use `vector-effect: non-scaling-stroke`, so outlines keep
8687
+ * their pixel width when `focusState` zooms in.
8688
+ *
8689
+ * Known limitation: the very small states (CT, DC, DE, HI, MA, MD, NH, NJ, RI, VT) are tiny touch targets on a phone. They
8690
+ * are reachable by keyboard and screen reader like any other; set `showSmallStateButtons` for a row of 44px buttons for them.
8691
+ * Territories are not on the map.
8692
+ */
8693
+ export declare class UsStateMapComponent {
8694
+ /** State abbreviation -> value. Keys are case-insensitive; unknown keys are ignored; numbers and numeric strings (`'5'`) are read as numbers, anything else (negative, NaN, Infinity, text, booleans, null) counts as 0; repeated keys (`tx` and `TX`) are added up. */
8695
+ readonly data: import("@angular/core").InputSignal<Record<string, number>>;
8696
+ /** Abbreviation of the selected state (orange outline, the others are dimmed). Unknown / null = none. */
8697
+ readonly selectedState: import("@angular/core").InputSignal<string | null>;
8698
+ /** Abbreviation of a state to zoom the map to (bounding box plus 15% padding). Unknown / null = the whole map. */
8699
+ readonly focusState: import("@angular/core").InputSignal<string | null>;
8700
+ /** Unit for one, e.g. "pickup": "Texas, 1 pickup". Empty = just the number. */
8701
+ readonly unitSingular: import("@angular/core").InputSignal<string>;
8702
+ /** Unit for other counts, e.g. "pickups". Empty = the singular text is used for every count. */
8703
+ readonly unitPlural: import("@angular/core").InputSignal<string>;
8704
+ /** Colour of a value of 0 (CSS hex or rgb()). Invalid values fall back to #e5e7eb. */
8705
+ readonly colorLow: import("@angular/core").InputSignal<string>;
8706
+ /** Colour of the largest value (CSS hex or rgb()). Invalid values fall back to #1e40af. */
8707
+ readonly colorHigh: import("@angular/core").InputSignal<string>;
8708
+ /** Shows the colour scale (0 to the largest value). */
8709
+ readonly showLegend: import("@angular/core").InputSignal<boolean>;
8710
+ /** Shows a row of buttons for the very small states (touch friendly, 44px). Off by default. */
8711
+ readonly showSmallStateButtons: import("@angular/core").InputSignal<boolean>;
8712
+ /** Accessible name of the whole map. */
8713
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
8714
+ /** CSS maximum width of the map (it always shrinks to its container). */
8715
+ readonly maxWidth: import("@angular/core").InputSignal<string>;
8716
+ /** Optional CSS height of the map, e.g. "420px". Empty = follows the width (aspect ratio of the map). */
8717
+ readonly height: import("@angular/core").InputSignal<string>;
8718
+ /** Emits the state's abbreviation (upper case) on click, tap, Enter or Space. */
8719
+ readonly stateClick: import("@angular/core").OutputEmitterRef<string>;
8720
+ private readonly host;
8721
+ /** True between a pointer press and the click that follows: a mouse / touch focus must not draw the keyboard ring. */
8722
+ protected pointerDown: boolean;
8723
+ private readonly hoveredId;
8724
+ private readonly focusedId;
8725
+ private readonly lastFocusedId;
8726
+ private readonly ringVisible;
8727
+ protected readonly tipPos: import("@angular/core").WritableSignal<{
8728
+ x: number;
8729
+ y: number;
8730
+ } | null>;
8731
+ private readonly low;
8732
+ private readonly high;
8733
+ private readonly values;
8734
+ private readonly max;
8735
+ private unit;
8736
+ private valueText;
8737
+ protected readonly views: import("@angular/core").Signal<StateView[]>;
8738
+ protected readonly smallViews: import("@angular/core").Signal<StateView[]>;
8739
+ protected readonly selectedId: import("@angular/core").Signal<string | null>;
8740
+ protected readonly selectedView: import("@angular/core").Signal<StateView | null>;
8741
+ protected readonly focusRingView: import("@angular/core").Signal<StateView | null>;
8742
+ protected readonly viewBox: import("@angular/core").Signal<string>;
8743
+ /** The one state in the tab order: last focused, else selected, else the first. */
8744
+ protected readonly tabStop: import("@angular/core").Signal<string>;
8745
+ protected readonly tip: import("@angular/core").Signal<StateView | null>;
8746
+ protected readonly gradient: import("@angular/core").Signal<string>;
8747
+ protected readonly maxText: import("@angular/core").Signal<string>;
8748
+ protected readonly legendLabel: import("@angular/core").Signal<string>;
8749
+ protected opacityOf(id: string): string | null;
8750
+ protected onHover(ev: PointerEvent, id: string): void;
8751
+ protected onLeave(id: string): void;
8752
+ protected clearHover(): void;
8753
+ protected onClick(id: string): void;
8754
+ protected onFocus(id: string): void;
8755
+ protected onBlur(id: string): void;
8756
+ protected onKey(ev: KeyboardEvent, id: string): void;
8757
+ static ɵfac: i0.ɵɵFactoryDeclaration<UsStateMapComponent, never>;
8758
+ static ɵcmp: i0.ɵɵComponentDeclaration<UsStateMapComponent, "ui-us-state-map", never, {
8759
+ "data": {
8760
+ "alias": "data";
8761
+ "required": false;
8762
+ "isSignal": true;
8763
+ };
8764
+ "selectedState": {
8765
+ "alias": "selectedState";
8766
+ "required": false;
8767
+ "isSignal": true;
8768
+ };
8769
+ "focusState": {
8770
+ "alias": "focusState";
8771
+ "required": false;
8772
+ "isSignal": true;
8773
+ };
8774
+ "unitSingular": {
8775
+ "alias": "unitSingular";
8776
+ "required": false;
8777
+ "isSignal": true;
8778
+ };
8779
+ "unitPlural": {
8780
+ "alias": "unitPlural";
8781
+ "required": false;
8782
+ "isSignal": true;
8783
+ };
8784
+ "colorLow": {
8785
+ "alias": "colorLow";
8786
+ "required": false;
8787
+ "isSignal": true;
8788
+ };
8789
+ "colorHigh": {
8790
+ "alias": "colorHigh";
8791
+ "required": false;
8792
+ "isSignal": true;
8793
+ };
8794
+ "showLegend": {
8795
+ "alias": "showLegend";
8796
+ "required": false;
8797
+ "isSignal": true;
8798
+ };
8799
+ "showSmallStateButtons": {
8800
+ "alias": "showSmallStateButtons";
8801
+ "required": false;
8802
+ "isSignal": true;
8803
+ };
8804
+ "ariaLabel": {
8805
+ "alias": "ariaLabel";
8806
+ "required": false;
8807
+ "isSignal": true;
8808
+ };
8809
+ "maxWidth": {
8810
+ "alias": "maxWidth";
8811
+ "required": false;
8812
+ "isSignal": true;
8813
+ };
8814
+ "height": {
8815
+ "alias": "height";
8816
+ "required": false;
8817
+ "isSignal": true;
8818
+ };
8819
+ }, {
8820
+ "stateClick": "stateClick";
8821
+ }, never, never, true, never>;
8822
+ }
8414
8823
  export type DatePreset = 'today' | '7days' | '30days' | 'month' | 'quarter' | 'ytd' | 'all' | 'custom';
8415
8824
  export interface DateRange {
8416
8825
  preset: DatePreset;
@@ -8848,6 +9257,7 @@ export declare class RadioMatrixComponent implements ControlValueAccessor, Valid
8848
9257
  * Header (all optional - set `[showHeader]="false"` and the card is just the body):
8849
9258
  * - `title` / `subtitle`: the bold uppercase title and the grey line under it.
8850
9259
  * - `titleId`: an id for the title text, which also names the card for assistive tech (aria-labelledby).
9260
+ * - `ariaLabel`: the card's accessible name when the header does not provide one (`[showHeader]="false"`, or a header with no title). A visible title always wins; `titleId` still names the card by aria-labelledby.
8851
9261
  * - `<div card-header-leading>`: something before the title block, such as a logo or an icon.
8852
9262
  * - `<ng-container card-header>`: your own header content, shown under the title (or instead of it when no title is set).
8853
9263
  * - `<ng-container card-header-action>`: buttons or links on the right side of the header.
@@ -8863,6 +9273,7 @@ export declare class RadioMatrixComponent implements ControlValueAccessor, Valid
8863
9273
  * <ui-detail-card title="COMMUNICATIONS" subtitle="All calls, texts, and emails"> ...body... </ui-detail-card>
8864
9274
  * <ui-detail-card title="CONTACT INFO" [showEdit]="true" (editClick)="openEdit()"> ... </ui-detail-card>
8865
9275
  * <ui-detail-card [showHeader]="false"> ...body only... </ui-detail-card>
9276
+ * <ui-detail-card [showHeader]="false" ariaLabel="Report filters"> ...body only, but named for assistive tech... </ui-detail-card>
8866
9277
  * <ui-detail-card [collapsible]="false">
8867
9278
  * <div card-header>Any markup</div>
8868
9279
  * <ui-button card-header-action size="xs" variant="secondary">Add</ui-button>
@@ -8876,6 +9287,8 @@ export declare class DetailCardComponent {
8876
9287
  readonly headingLevel: import("@angular/core").InputSignal<1 | 2 | 3 | 4 | 5 | 6 | null>;
8877
9288
  /** Optional id for the title text; the card is then labelled by it. Without it the title is the card's aria-label. */
8878
9289
  readonly titleId: import("@angular/core").InputSignal<string>;
9290
+ /** Accessible name of the card when the header gives none (no header, or no title). A visible title wins; blank = no name. */
9291
+ readonly ariaLabel: import("@angular/core").InputSignal<string>;
8879
9292
  /** Grey line under the title. */
8880
9293
  readonly subtitle: import("@angular/core").InputSignal<string>;
8881
9294
  /** Card background as any CSS colour (`#f8fafc`, `var(--surface)` …). Empty = white. */
@@ -8892,6 +9305,8 @@ export declare class DetailCardComponent {
8892
9305
  readonly showEdit: import("@angular/core").InputSignal<boolean>;
8893
9306
  readonly editLabel: import("@angular/core").InputSignal<string>;
8894
9307
  readonly editClick: import("@angular/core").OutputEmitterRef<void>;
9308
+ /** The group's aria-label: the visible title (unless `titleId` names the card instead), else `ariaLabel`, else none. */
9309
+ groupLabel(): string | null;
8895
9310
  /** The body is open unless the card has a header that is collapsible and was collapsed. */
8896
9311
  isOpen(): boolean;
8897
9312
  toggle(): void;
@@ -8914,6 +9329,11 @@ export declare class DetailCardComponent {
8914
9329
  "required": false;
8915
9330
  "isSignal": true;
8916
9331
  };
9332
+ "ariaLabel": {
9333
+ "alias": "ariaLabel";
9334
+ "required": false;
9335
+ "isSignal": true;
9336
+ };
8917
9337
  "subtitle": {
8918
9338
  "alias": "subtitle";
8919
9339
  "required": false;
@@ -9246,8 +9666,12 @@ export declare class ColorInputComponent implements ControlValueAccessor, Valida
9246
9666
  * - `iconColor`: colour of the icon (default blue #2563eb).
9247
9667
  * - `iconBackground`: colour of the tile behind the icon (default white when a `background` is set, otherwise light grey #f1f5f9).
9248
9668
  * - `bordered`: 1px #e2e8f0 border and 10px radius (default false).
9249
- * - `size`: 'md' (default; 1rem title, 28px tile) or 'sm' (0.875rem title, 24px tile).
9250
- * Padding is built in (14px 18px; 12px 14px at 480px and narrower) - there is no padding input.
9669
+ * - `size`: the visual size only (the semantic heading `level` is independent of it). Anything else becomes 'md'.
9670
+ * 'sm' 0.875rem (14px) title, 0.75rem description, 24px tile, 14px icon; padding 14px 18px (12px 14px at 480px and narrower)
9671
+ * 'md' (default) 1rem (16px) title, 0.8125rem description, 28px tile, 16px icon; padding 14px 18px (12px 14px at 480px and narrower)
9672
+ * 'lg' 1.25rem (20px) title, 0.9375rem description, 36px tile, 20px icon; padding 18px 22px (14px 16px at 480px and narrower)
9673
+ * 'xl' 1.5rem (24px) title (1.375rem at 480px and narrower), 1rem description, 44px tile, 24px icon; padding 22px 26px (16px 18px at 480px and narrower)
9674
+ * Padding is built in per size - there is no padding input.
9251
9675
  *
9252
9676
  * Content slots:
9253
9677
  * - `[section-header-icon]`: your own icon (an `<svg>`, an `<img>` ...) shown in the tile instead of the `icon` input.
@@ -9278,8 +9702,8 @@ export declare class SectionHeaderComponent {
9278
9702
  readonly iconBackground: import("@angular/core").InputSignal<string>;
9279
9703
  /** 1px border and 10px corners. */
9280
9704
  readonly bordered: import("@angular/core").InputSignal<boolean>;
9281
- /** 'md' (default) or 'sm'. */
9282
- readonly size: import("@angular/core").InputSignal<"sm" | "md">;
9705
+ /** Visual size: 'sm', 'md' (default), 'lg' or 'xl'. Independent of `level`; anything else renders as 'md'. */
9706
+ readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg" | "xl">;
9283
9707
  protected readonly titleId: string;
9284
9708
  protected readonly descriptionId: string;
9285
9709
  protected readonly headingLevel: import("@angular/core").Signal<number>;