@forgeroute/ui 0.4.70 → 0.4.72

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.
@@ -356,6 +356,34 @@ export type BulkUpdateOption<T = any> = string | {
356
356
  * else "N <itemLabel>(s) <doneLabel>.". Default 'updated'.
357
357
  */
358
358
  doneLabel?: string;
359
+ /**
360
+ * Direct action only: the request returns a FILE instead of JSON (a ZIP of PDFs, a CSV...). The table still confirms first and POSTs
361
+ * `<endpoint>/bulk/<field>` with exactly `{ ids }`, but asks for a blob response and saves it as one download (one file, so browsers do not
362
+ * block it the way several automatic downloads are blocked). The file name is the response's `Content-Disposition` filename, else
363
+ * `fileName`, else the action key. The toast reads "N <itemLabel>(s) <doneLabel>." (`doneLabel` defaults to 'downloaded' here); the server may
364
+ * send an `X-Skipped-Count` header with the number of selected rows it left out of the file ("N downloaded, M skipped."). The rows do not change,
365
+ * so the table is not refreshed; the selection is cleared and `(bulkAction)` is emitted. An error response is read as `{ message }` JSON and shown
366
+ * in an error toast. Omit it and the action expects JSON as before.
367
+ *
368
+ * { field: 'download-pdfs', action: true, label: 'Download PDFs', download: { fileName: 'inspections.zip' } }
369
+ */
370
+ download?: {
371
+ fileName?: string;
372
+ };
373
+ /**
374
+ * Direct action only (`opens: 'panel'`): the button does not confirm and does not POST. It emits `(bulkPanelClick)` with the
375
+ * eligible rows and the host opens its own panel or form (compose an e-mail, add a charge...) and does the work itself.
376
+ * The host calls `clearSelection()` and `refresh()` on the table when it is done. `confirm` and `doneLabel` are ignored for a panel action (no confirm is shown; `variant: 'danger'` still colours the button); `eligible` still applies (refused rows are never passed on). Omit it and nothing changes.
377
+ *
378
+ * { field: 'email', action: true, opens: 'panel', label: 'Email', eligible: (r: Resident) => !!r.email }
379
+ */
380
+ opens?: 'panel';
381
+ /**
382
+ * Panel action only (`opens: 'panel'`): true while the action is not available right now (e.g. text messaging is switched off). The button is
383
+ * then not shown at all. Read every time the bar renders, so it can read signals; a function that throws counts as "not hidden". A panel
384
+ * action is also not shown while none of the selected rows is `eligible`. Omit it and the button shows whenever a selected row is eligible.
385
+ */
386
+ hidden?: () => boolean;
359
387
  };
360
388
  /** What a bulk confirm function receives. For a direct bulk action: `columnLabel` is the action label, `targetValue`/`targetLabel` are '', `rows`/`skippedRows` the eligible / refused selected rows. For bulk Delete: `targetValue`/`targetLabel` are '', `columnLabel` is 'Delete', skipped counts are 0. */
361
389
  export interface BulkConfirmContext<T = any> {
@@ -423,6 +451,14 @@ export interface BulkActionDef {
423
451
  eligible?: (row: any) => boolean;
424
452
  /** Direct action: past-tense word in the result toast (from `{ doneLabel }`), default 'updated'. */
425
453
  doneLabel?: string;
454
+ /** Direct action that returns a file (from `{ download }`): the response is saved as a download instead of read as JSON. */
455
+ download?: {
456
+ fileName?: string;
457
+ };
458
+ /** True for a direct action that opens a host panel instead of confirming and POSTing (from `{ opens: 'panel' }`). */
459
+ opensPanel?: boolean;
460
+ /** Panel action: true while it is not available (from `{ hidden }`); the button is not shown. */
461
+ hidden?: () => boolean;
426
462
  /**
427
463
  * The item field this action targets (e.g. 'status', 'type').
428
464
  * When set the table shows an inline select populated from the column's option sources.
@@ -437,6 +473,19 @@ export interface BulkActionEvent {
437
473
  value?: string;
438
474
  skippedCount: number;
439
475
  }
476
+ /** Emitted by (bulkPanelClick) when the user clicks a bulk action declared with `opens: 'panel'`. */
477
+ export interface BulkPanelEvent<T = any> {
478
+ /** `action-<key>` of the clicked bulk option. */
479
+ type: string;
480
+ /** The option's `field` (its key), e.g. 'email'. */
481
+ key: string;
482
+ /** Ids of the eligible selected rows. */
483
+ ids: string[];
484
+ /** The eligible selected rows (loaded page). */
485
+ rows: T[];
486
+ /** Selected rows the option's `eligible` refused. */
487
+ skippedCount: number;
488
+ }
440
489
  /** Emitted by (rowReorder) after the user moves a row (`reorderable`). */
441
490
  export interface TableRowReorderEvent<T = any> {
442
491
  /** The row that was moved. */
@@ -1084,6 +1133,8 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
1084
1133
  readonly label: import("@angular/core").InputSignal<string>;
1085
1134
  /** Accessible name for a select with no visible label (e.g. an inline row or table-cell select). */
1086
1135
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
1136
+ /** Native `name` attribute placed on the inner `<select>` (e2e `[name=x]` selectors, autofill). None when empty. */
1137
+ readonly inputName: import("@angular/core").InputSignal<string>;
1087
1138
  readonly placeholder: import("@angular/core").InputSignal<string>;
1088
1139
  readonly options: import("@angular/core").InputSignal<SelectOption<string | number>[]>;
1089
1140
  /** What the control value is: `'string'` (default, numeric option values become strings) or `'number'` (`number | null`). */
@@ -1152,6 +1203,11 @@ export declare class SelectComponent implements ControlValueAccessor, Validator,
1152
1203
  "required": false;
1153
1204
  "isSignal": true;
1154
1205
  };
1206
+ "inputName": {
1207
+ "alias": "inputName";
1208
+ "required": false;
1209
+ "isSignal": true;
1210
+ };
1155
1211
  "placeholder": {
1156
1212
  "alias": "placeholder";
1157
1213
  "required": false;
@@ -1540,6 +1596,11 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1540
1596
  readonly tabChange: import("@angular/core").OutputEmitterRef<string>;
1541
1597
  /** Emitted when the user applies a bulk action. The table has already filtered to eligible IDs. */
1542
1598
  readonly bulkAction: import("@angular/core").OutputEmitterRef<BulkActionEvent>;
1599
+ /**
1600
+ * Emitted when the user clicks a bulk action declared `{ field, action: true, opens: 'panel' }`. The table sends nothing: the host opens its
1601
+ * own panel for `rows` and, when it has finished, calls `clearSelection()` and `refresh()` on the table.
1602
+ */
1603
+ readonly bulkPanelClick: import("@angular/core").OutputEmitterRef<BulkPanelEvent<T>>;
1543
1604
  /** Emitted after a built-in single-row delete completes successfully. */
1544
1605
  readonly rowDeleted: import("@angular/core").OutputEmitterRef<T>;
1545
1606
  /**
@@ -1763,6 +1824,8 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1763
1824
  private _hostConfirm;
1764
1825
  /** Why a bulk action can't be used right now ('' = it can). Only Delete with `deletable` and a direct action with `eligible` can be blocked. */
1765
1826
  protected _bulkBlockedReason(def: BulkActionDef): string;
1827
+ /** A panel action is not shown while the host says it is unavailable or no selected row is eligible. Everything else is always shown. */
1828
+ protected _bulkHidden(def: BulkActionDef): boolean;
1766
1829
  /** The distinct reasons of the currently blocked bulk actions, shown as visible text next to the disabled buttons. */
1767
1830
  protected _bulkBlockedHints(): string[];
1768
1831
  /** Selected rows (loaded page) split by the delete option's `deletable` predicate; without it every selected row is deletable. */
@@ -1773,8 +1836,14 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1773
1836
  protected readonly _runningAction: import("@angular/core").WritableSignal<string | null>;
1774
1837
  /** True while a direct action's confirm dialog is open (guards against a second click). */
1775
1838
  private _confirmingAction;
1839
+ /** Panel action: no confirm and no request; hands the eligible rows to the host, which opens its own panel. */
1840
+ private _openBulkPanel;
1776
1841
  /** Direct bulk action: confirm, then POST `<endpoint>/bulk/<key>` with `{ ids }` of the eligible rows. */
1777
1842
  private _runDirectAction;
1843
+ /** Direct action that returns a file: POST `{ ids }` asking for a blob, save it as one download, toast, clear the selection. */
1844
+ private _runDownloadAction;
1845
+ /** The `{ message }` of a failed file request: with a blob response the error body arrives as a Blob, so it is read as text first. */
1846
+ private _downloadErrorMessage;
1778
1847
  /** Called when the user clicks a bulk action button in the selection bar. */
1779
1848
  protected onBulkActionClick(def: BulkActionDef): void;
1780
1849
  /** True while the field-change confirmation dialog is open (guards against a second Apply click). */
@@ -1801,6 +1870,10 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1801
1870
  /** State for the built-in single-row delete confirmation modal. */
1802
1871
  protected readonly _rowDeleteTarget: import("@angular/core").WritableSignal<T | null>;
1803
1872
  protected readonly _rowDeleting: import("@angular/core").WritableSignal<boolean>;
1873
+ /** Ids the built-in delete confirm dialog is described by (aria-describedby): its message and the "what will happen" list. */
1874
+ private readonly _confirmUid;
1875
+ protected readonly _confirmMsgId: string;
1876
+ protected readonly _confirmDetailsId: string;
1804
1877
  protected readonly _rowDeleteConfig: import("@angular/core").WritableSignal<{
1805
1878
  title: string;
1806
1879
  message: string;
@@ -2119,6 +2192,7 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
2119
2192
  "selectionChange": "selectionChange";
2120
2193
  "tabChange": "tabChange";
2121
2194
  "bulkAction": "bulkAction";
2195
+ "bulkPanelClick": "bulkPanelClick";
2122
2196
  "rowDeleted": "rowDeleted";
2123
2197
  "rowReorder": "rowReorder";
2124
2198
  }, never, never, true, never>;
@@ -2154,7 +2228,7 @@ export declare class SidePanelComponent implements OnDestroy {
2154
2228
  readonly cancelLabel: import("@angular/core").InputSignal<string>;
2155
2229
  /** Right-aligned primary action button label. Empty string = hidden. */
2156
2230
  readonly primaryLabel: import("@angular/core").InputSignal<string>;
2157
- readonly primaryVariant: import("@angular/core").InputSignal<"primary" | "danger" | "warning">;
2231
+ readonly primaryVariant: import("@angular/core").InputSignal<"warning" | "danger" | "primary">;
2158
2232
  readonly primaryLoading: import("@angular/core").InputSignal<boolean>;
2159
2233
  readonly primaryDisabled: import("@angular/core").InputSignal<boolean>;
2160
2234
  /** `data-testid` of the Cancel button; none when empty (default). */
@@ -2261,8 +2335,14 @@ export declare class ModalComponent implements OnInit, OnDestroy {
2261
2335
  readonly open: import("@angular/core").InputSignal<boolean>;
2262
2336
  readonly title: import("@angular/core").InputSignal<string>;
2263
2337
  readonly description: import("@angular/core").InputSignal<string>;
2338
+ /**
2339
+ * Id(s) of the element(s) inside the dialog that describe it (space-separated, as `aria-describedby` takes them), e.g. the confirm message
2340
+ * paragraph. Screen readers read them after the title when the dialog opens. When it is not set and `description` is, the dialog is described
2341
+ * by that description line; with neither, no `aria-describedby` is rendered (the body of a form dialog is not a description).
2342
+ */
2343
+ readonly describedBy: import("@angular/core").InputSignal<string | null>;
2264
2344
  readonly maxWidth: import("@angular/core").InputSignal<string>;
2265
- readonly bodyOverflow: import("@angular/core").InputSignal<"hidden" | "auto" | "visible">;
2345
+ readonly bodyOverflow: import("@angular/core").InputSignal<"hidden" | "visible" | "auto">;
2266
2346
  /** `data-testid` of the dialog element itself (the host `<ui-modal>` is always in the page, the dialog only while open). None when empty. */
2267
2347
  readonly dialogTestId: import("@angular/core").InputSignal<string>;
2268
2348
  /** Hides the header X button (e.g. while a request is running). Escape and a backdrop click still close unless `closable` is false. */
@@ -2295,6 +2375,8 @@ export declare class ModalComponent implements OnInit, OnDestroy {
2295
2375
  readonly returnFocusTo: import("@angular/core").InputSignal<HTMLElement | null>;
2296
2376
  protected readonly kiosk: import("@angular/core").Signal<boolean>;
2297
2377
  protected readonly titleId: string;
2378
+ protected readonly descId: string;
2379
+ protected readonly describedById: import("@angular/core").Signal<string | null>;
2298
2380
  private readonly el;
2299
2381
  private readonly ren;
2300
2382
  private readonly doc;
@@ -2332,6 +2414,11 @@ export declare class ModalComponent implements OnInit, OnDestroy {
2332
2414
  "required": false;
2333
2415
  "isSignal": true;
2334
2416
  };
2417
+ "describedBy": {
2418
+ "alias": "describedBy";
2419
+ "required": false;
2420
+ "isSignal": true;
2421
+ };
2335
2422
  "maxWidth": {
2336
2423
  "alias": "maxWidth";
2337
2424
  "required": false;
@@ -2813,7 +2900,7 @@ export declare class AudioPlayerComponent {
2813
2900
  /** Accessible name of the player. */
2814
2901
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
2815
2902
  /** How much to load before play: 'metadata' (default), 'auto' or 'none'. */
2816
- readonly preload: import("@angular/core").InputSignal<"none" | "auto" | "metadata">;
2903
+ readonly preload: import("@angular/core").InputSignal<"none" | "metadata" | "auto">;
2817
2904
  /** Fires when the browser can't load or decode the source. */
2818
2905
  readonly playbackError: import("@angular/core").OutputEmitterRef<void>;
2819
2906
  protected readonly failed: import("@angular/core").WritableSignal<boolean>;
@@ -3465,7 +3552,7 @@ export declare class StepperComponent {
3465
3552
  * its circle, one continuous line behind the circles that fills as steps complete; a complete step shows a check, the current step an
3466
3553
  * accent ring with a dot, an upcoming step an empty circle (no numbers). Circles are 40px (32px with `size="sm"`).
3467
3554
  */
3468
- readonly labelPosition: import("@angular/core").InputSignal<"beside" | "below">;
3555
+ readonly labelPosition: import("@angular/core").InputSignal<"below" | "beside">;
3469
3556
  /** Viewport width in px below which a horizontal stepper is drawn vertical (labels beside the circles), e.g. `640`. null (default) = never. */
3470
3557
  readonly stackBelow: import("@angular/core").InputSignal<number | null>;
3471
3558
  /** CSS length (e.g. `10.5rem`): horizontal only - the list gets `min-width: calc(<step count> * <value>)`. Empty (default) = no minimum. */
@@ -4094,7 +4181,7 @@ export declare class ButtonComponent {
4094
4181
  * `ghost-start`: a ghost button for list / section toggles - a full-width row with its content aligned to the start, transparent
4095
4182
  * background, hover tint and the normal focus ring. Pair it with `ariaExpanded` / `ariaPressed`; the label may contain a heading element.
4096
4183
  */
4097
- readonly variant: import("@angular/core").InputSignal<"primary" | "secondary" | "danger" | "warning" | "link" | "ghost" | "ghost-start" | "tab" | "icon" | "icon-danger">;
4184
+ readonly variant: import("@angular/core").InputSignal<"link" | "icon" | "warning" | "danger" | "primary" | "secondary" | "ghost" | "ghost-start" | "tab" | "icon-danger">;
4098
4185
  /** Renders an anchor to this route instead of a button (use with `variant="link"` for an inline text link). `['/settings']` or `'/settings'`. While `disabled` or `loading` it renders a non-clickable `<span role="link" aria-disabled>` with no href (like `href`). */
4099
4186
  readonly routerLink: import("@angular/core").InputSignal<string | readonly unknown[] | null>;
4100
4187
  /** Query params of the `routerLink` (e.g. `{ section: 'deal-defaults' }`). */
@@ -4140,7 +4227,7 @@ export declare class ButtonComponent {
4140
4227
  readonly color: import("@angular/core").InputSignal<string | undefined>;
4141
4228
  readonly active: import("@angular/core").InputSignal<boolean>;
4142
4229
  readonly size: import("@angular/core").InputSignal<"sm" | "md" | "lg" | "xs">;
4143
- readonly type: import("@angular/core").InputSignal<"reset" | "submit" | "button">;
4230
+ readonly type: import("@angular/core").InputSignal<"button" | "reset" | "submit">;
4144
4231
  readonly disabled: import("@angular/core").InputSignal<boolean>;
4145
4232
  readonly loading: import("@angular/core").InputSignal<boolean>;
4146
4233
  readonly fullWidth: import("@angular/core").InputSignal<boolean>;
@@ -4591,7 +4678,7 @@ export declare class BulkMessagePanelComponent {
4591
4678
  readonly sending: import("@angular/core").InputSignal<boolean>;
4592
4679
  readonly closed: import("@angular/core").OutputEmitterRef<void>;
4593
4680
  readonly send: import("@angular/core").OutputEmitterRef<BulkMessageSendEvent>;
4594
- readonly channel: import("@angular/core").WritableSignal<"text" | "email">;
4681
+ readonly channel: import("@angular/core").WritableSignal<"email" | "text">;
4595
4682
  private readonly _subject;
4596
4683
  private readonly _body;
4597
4684
  get subject(): string;
@@ -4943,6 +5030,11 @@ export declare class ConfirmService {
4943
5030
  export declare class ConfirmOutletComponent implements OnInit, OnDestroy {
4944
5031
  protected readonly svc: ConfirmService;
4945
5032
  protected readonly _isPrimary: import("@angular/core").Signal<boolean>;
5033
+ /** Unique per outlet: the dialog is described by the message and, when there is one, the "what will happen" list (aria-describedby). */
5034
+ private readonly _uid;
5035
+ protected readonly msgId: string;
5036
+ protected readonly detailsId: string;
5037
+ protected readonly _describedBy: import("@angular/core").Signal<string>;
4946
5038
  ngOnInit(): void;
4947
5039
  ngOnDestroy(): void;
4948
5040
  static ɵfac: i0.ɵɵFactoryDeclaration<ConfirmOutletComponent, never>;
@@ -5060,6 +5152,8 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5060
5152
  * and `inputTestId="email-input"`. Omitted = no attribute.
5061
5153
  */
5062
5154
  readonly inputTestId: import("@angular/core").InputSignal<string>;
5155
+ /** Native `name` attribute placed on the inner `<input>` (e2e `[name=x]` selectors, browser autofill). None when empty. */
5156
+ readonly inputName: import("@angular/core").InputSignal<string>;
5063
5157
  readonly placeholder: import("@angular/core").InputSignal<string>;
5064
5158
  /**
5065
5159
  * An optional leading mark shown inside the field, left of the text (e.g. `prefix="$"` for a dollar amount). Hidden unless set; it is
@@ -5079,7 +5173,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5079
5173
  readonly passwordRequirements: import("@angular/core").InputSignal<boolean>;
5080
5174
  /** Rules for `passwordRequirements`; default = the library's five rules (8+ characters, upper, lower, digit, special). */
5081
5175
  readonly passwordRules: import("@angular/core").InputSignal<readonly PasswordRule[] | undefined>;
5082
- readonly type: import("@angular/core").InputSignal<"number" | "text" | "email" | "tel" | "password" | "url">;
5176
+ readonly type: import("@angular/core").InputSignal<"number" | "email" | "text" | "tel" | "password" | "url">;
5083
5177
  readonly maxlength: import("@angular/core").InputSignal<number | undefined>;
5084
5178
  readonly required: import("@angular/core").InputSignal<boolean>;
5085
5179
  readonly autocomplete: import("@angular/core").InputSignal<string>;
@@ -5088,7 +5182,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5088
5182
  * `search`, `text`, `none`). Empty (default) = the browser's default for the field, except `type="tel"`, which keeps `tel`.
5089
5183
  * It only picks the keyboard; it does not restrict what can be typed or pasted.
5090
5184
  */
5091
- readonly inputmode: import("@angular/core").InputSignal<"" | "none" | "text" | "search" | "email" | "tel" | "url" | "decimal" | "numeric">;
5185
+ readonly inputmode: import("@angular/core").InputSignal<"" | "none" | "email" | "search" | "text" | "numeric" | "tel" | "url" | "decimal">;
5092
5186
  readonly readonly: import("@angular/core").InputSignal<boolean>;
5093
5187
  readonly hint: import("@angular/core").InputSignal<string>;
5094
5188
  /** Id(s) of an external element that also describes this control (e.g. one shared error text for a group of fields). Space separated; MERGED after the control's own hint / error id in `aria-describedby`, never replacing it. Empty = unchanged. */
@@ -5154,7 +5248,7 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5154
5248
  protected readonly _stepAttr: import("@angular/core").Signal<string | null>;
5155
5249
  protected readonly _revealable: import("@angular/core").Signal<boolean>;
5156
5250
  protected readonly _shown: import("@angular/core").WritableSignal<boolean>;
5157
- protected readonly _inputType: import("@angular/core").Signal<"number" | "text" | "email" | "tel" | "password" | "url">;
5251
+ protected readonly _inputType: import("@angular/core").Signal<"number" | "email" | "text" | "tel" | "password" | "url">;
5158
5252
  protected readonly _eyePath: string;
5159
5253
  protected readonly _eyeOffPath: string;
5160
5254
  protected readonly _showRequirements: import("@angular/core").Signal<boolean>;
@@ -5218,6 +5312,11 @@ export declare class TextInputComponent implements ControlValueAccessor, Validat
5218
5312
  "required": false;
5219
5313
  "isSignal": true;
5220
5314
  };
5315
+ "inputName": {
5316
+ "alias": "inputName";
5317
+ "required": false;
5318
+ "isSignal": true;
5319
+ };
5221
5320
  "placeholder": {
5222
5321
  "alias": "placeholder";
5223
5322
  "required": false;
@@ -6974,7 +7073,7 @@ export declare class FormRowComponent {
6974
7073
  * child simply takes the full width. Keep a numeric span within the row's `cols` (use `all` for the whole row).
6975
7074
  */
6976
7075
  export declare class FormColSpanDirective {
6977
- readonly uiColSpan: import("@angular/core").InputSignal<"" | "2" | "3" | 2 | 3 | "all">;
7076
+ readonly uiColSpan: import("@angular/core").InputSignal<"" | 2 | 3 | "2" | "3" | "all">;
6978
7077
  protected readonly _class: import("@angular/core").Signal<string>;
6979
7078
  static ɵfac: i0.ɵɵFactoryDeclaration<FormColSpanDirective, never>;
6980
7079
  static ɵdir: i0.ɵɵDirectiveDeclaration<FormColSpanDirective, "[uiColSpan]", never, {
@@ -7024,7 +7123,7 @@ export type ListItemDetail = string | {
7024
7123
  export type ListItemIconColor = 'slate' | 'amber' | 'red' | 'blue' | 'green' | 'purple' | 'teal' | 'orange';
7025
7124
  export declare class ListItemComponent {
7026
7125
  /** Show a radio button, checkbox, or nothing on the left. */
7027
- readonly leading: import("@angular/core").InputSignal<"none" | "checkbox" | "radio">;
7126
+ readonly leading: import("@angular/core").InputSignal<"none" | "radio" | "checkbox">;
7028
7127
  /** `name` attribute for the radio/checkbox (groups radios together). */
7029
7128
  readonly inputName: import("@angular/core").InputSignal<string>;
7030
7129
  /** `id` attribute for the radio/checkbox (links the label). */
@@ -8516,7 +8615,7 @@ export declare class IconComponent {
8516
8615
  readonly ariaLabel: import("@angular/core").InputSignal<string>;
8517
8616
  /** Icon size in px. */
8518
8617
  readonly size: import("@angular/core").InputSignal<number>;
8519
- readonly variant: import("@angular/core").InputSignal<"danger" | "default">;
8618
+ readonly variant: import("@angular/core").InputSignal<"default" | "danger">;
8520
8619
  readonly disabled: import("@angular/core").InputSignal<boolean>;
8521
8620
  /** 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. */
8522
8621
  readonly loading: import("@angular/core").InputSignal<boolean>;
@@ -8628,7 +8727,7 @@ export declare class TimelineComponent {
8628
8727
  /** Any CSS length (e.g. `600px`). When set, the list scrolls inside a keyboard-focusable region of at most this height. Empty = no wrapper. */
8629
8728
  readonly maxHeight: import("@angular/core").InputSignal<string>;
8630
8729
  /** `below` (default): the time is under the title / description. `end`: the time sits right-aligned on the title row. */
8631
- readonly timePosition: import("@angular/core").InputSignal<"end" | "below">;
8730
+ readonly timePosition: import("@angular/core").InputSignal<"below" | "end">;
8632
8731
  /**
8633
8732
  * Group the items under a heading per calendar day of `dateTime` ("Today", "Yesterday", "Oct 3", "Oct 3, 2025"). Default false = no headings,
8634
8733
  * DOM unchanged. The given order is kept: consecutive items of one day share a heading, the same day appearing again later starts a new group.
@@ -8709,6 +8808,341 @@ export declare class HintComponent {
8709
8808
  static ɵfac: i0.ɵɵFactoryDeclaration<HintComponent, never>;
8710
8809
  static ɵcmp: i0.ɵɵComponentDeclaration<HintComponent, "ui-hint", never, {}, {}, never, ["*"], true, never>;
8711
8810
  }
8811
+ /**
8812
+ * The value of `ui-address-input`: five plain strings (never null). `apt` is the second line (apartment / suite / unit).
8813
+ *
8814
+ * Note: the library already exports `formatAddress(parts, options)` / `AddressParts` (utils/format) for display of an address with
8815
+ * city / state / zip parts. The helpers below work on this value model and are therefore named `...AddressValue`.
8816
+ */
8817
+ export interface AddressValue {
8818
+ street: string;
8819
+ apt: string;
8820
+ city: string;
8821
+ state: string;
8822
+ zip: string;
8823
+ }
8824
+ /** The 50 US states + DC as select options: value = 2-letter code, label = the code (compact, fits the narrow State column), description = the state name (read by screen readers, shown under the select). */
8825
+ export declare const US_STATE_OPTIONS: readonly SelectOption[];
8826
+ /** A fresh blank address. */
8827
+ export declare function emptyAddress(): AddressValue;
8828
+ /** Coerces anything (null, a partial object, non-string members) to a complete `AddressValue` of strings. */
8829
+ export declare function normalizeAddress(v: Partial<Record<keyof AddressValue, unknown>> | null | undefined): AddressValue;
8830
+ /** True when every part is empty or whitespace (null / undefined count as empty). */
8831
+ export declare function isEmptyAddress(a: Partial<Record<keyof AddressValue, unknown>> | null | undefined): boolean;
8832
+ /** The default ZIP rule of the component: US 5 digits or 5+4. */
8833
+ export declare const US_ZIP_PATTERN: RegExp;
8834
+ /**
8835
+ * Multi-line text, the storage form used by apps: street, apt, then "City, ST 12345", joined with "\n". Every part is trimmed and empty
8836
+ * parts are left out, so `parseAddressValue(formatAddressValue(x))` is stable.
8837
+ * `{street:'1 Main', apt:'Apt 4', city:'Austin', state:'TX', zip:'78701'}` -> `"1 Main\nApt 4\nAustin, TX 78701"`.
8838
+ */
8839
+ export declare function formatAddressValue(a: AddressValue | null | undefined): string;
8840
+ /**
8841
+ * The same address on ONE line for tables, labels and maps: "1 Main, Apt 4, Austin, TX 78701". `{ comma: false }` drops the comma between
8842
+ * city and state ("1 Main, Apt 4, Austin TX 78701", the older mfh resident style). Empty parts are left out.
8843
+ */
8844
+ export declare function formatAddressValueOneLine(a: AddressValue | null | undefined, options?: {
8845
+ comma?: boolean;
8846
+ }): string;
8847
+ /**
8848
+ * Reads the multi-line text form back into an address (the inverse of `formatAddressValue`; never throws).
8849
+ * - null / undefined / blank -> empty address.
8850
+ * - 1 line: the street only (`parseAddressOneLine` reads the one-line form).
8851
+ * - 2 lines: street + city line. 3 lines: street + apt + city line.
8852
+ * - 4+ lines: first = street, last = city line, everything in between = apt (joined with ", ").
8853
+ * - The city line may be "City, ST 12345", "City ST 12345", "City, ST 12345-6789", "city, st 12345" (any case with a comma), "City, ST",
8854
+ * "TX 78701" (city blank), with a trailing comma / period / semicolon tolerated. The state is two letters (capitals unless a comma
8855
+ * separates it), the ZIP 5 digits or 5+4. Anything else (a city only, a full state name, a 4-digit or foreign postcode, a unit line
8856
+ * without a city line) lands WHOLE in `city` - nothing is dropped.
8857
+ * - Lines are trimmed, blank lines dropped, \r\n accepted.
8858
+ */
8859
+ export declare function parseAddressValue(raw: string | null | undefined): AddressValue;
8860
+ /**
8861
+ * Reads the ONE-LINE form that `formatAddressValueOneLine` writes ("street[, apt], city, ST 12345", or "..., city ST 12345" without the
8862
+ * comma). Segments are split at commas: the last one carries "ST 12345" (and the city when there is no comma before the state), the city
8863
+ * is the segment before it, the first remaining segment is the street and the rest the apt. A street that itself contains a comma
8864
+ * ("Bldg 2, 100 Main St") therefore splits into street + apt. Text that does not end in "ST 12345" becomes the street whole.
8865
+ */
8866
+ export declare function parseAddressOneLine(raw: string | null | undefined): AddressValue;
8867
+ /** 5 digits or 5+4 ("12345", "12345-6789"); surrounding spaces are NOT accepted (trim first). */
8868
+ export declare function isValidZip(zip: string): boolean;
8869
+ type AddressField = keyof AddressValue;
8870
+ /**
8871
+ * Address entry (ControlValueAccessor): street, unit, city, state (select) and ZIP, built only from library inputs.
8872
+ *
8873
+ * Value: `AddressValue` ({ street, apt, city, state, zip }, all strings). Works with `[(ngModel)]`, `[formControl]`, or a signal via
8874
+ * `[value]` / `(valueChange)`. A written `null` / `undefined` clears the form; a written string is read with `parseAddressValue`.
8875
+ * Writing a value never emits; only user edits emit (the whole new `AddressValue`).
8876
+ *
8877
+ * Built-in validation (also reported on the form control as a `Validator`; `invalid` / `errorMessage` signals, `invalidChange` output,
8878
+ * provides `UiFieldValidity`): `required` -> street, city, state and ZIP are required ("<Field label> is required."); ZIP must be
8879
+ * 5 digits or 5+4 ("Enter a valid ZIP code."); state must be one of the options ("Select a valid option."); maxlengths
8880
+ * ("Too long (max N characters)."); `requireComplete` -> once ANY of street / city / state / ZIP is filled the others are required too
8881
+ * (for an optional address that must not be half-filled). The unit is never required. Messages show per field after that field was left,
8882
+ * or all at once while `showErrors` is true; the host `error` is shown under the group and always counts as invalid.
8883
+ *
8884
+ * Layout: street + unit, then city + state + ZIP in `ui-form-row`s (they collapse to one column on phones).
8885
+ */
8886
+ export declare class AddressInputComponent implements ControlValueAccessor, Validator, UiFieldValidity {
8887
+ /** Group label (the visible legend and the group's accessible name). Empty -> no legend, the group is named "Address". */
8888
+ readonly label: import("@angular/core").InputSignal<string>;
8889
+ /** Street, city, state and ZIP are required (never the unit). Shows `*` on the legend and on those fields. */
8890
+ readonly required: import("@angular/core").InputSignal<boolean>;
8891
+ /** Optional address that must not be half-filled: when any of street / city / state / ZIP has text, the other three are required. */
8892
+ readonly requireComplete: import("@angular/core").InputSignal<boolean>;
8893
+ readonly streetLabel: import("@angular/core").InputSignal<string>;
8894
+ readonly unitLabel: import("@angular/core").InputSignal<string>;
8895
+ readonly cityLabel: import("@angular/core").InputSignal<string>;
8896
+ readonly stateLabel: import("@angular/core").InputSignal<string>;
8897
+ readonly zipLabel: import("@angular/core").InputSignal<string>;
8898
+ readonly streetPlaceholder: import("@angular/core").InputSignal<string>;
8899
+ readonly unitPlaceholder: import("@angular/core").InputSignal<string>;
8900
+ readonly cityPlaceholder: import("@angular/core").InputSignal<string>;
8901
+ readonly zipPlaceholder: import("@angular/core").InputSignal<string>;
8902
+ /** Text of the empty State option (the one that clears the state). */
8903
+ readonly statePlaceholder: import("@angular/core").InputSignal<string>;
8904
+ /** Show the Apt / Suite field. Hidden -> the value's `apt` is kept as written and never edited. */
8905
+ readonly showUnit: import("@angular/core").InputSignal<boolean>;
8906
+ /** State choices (value = what is stored). Default: the 50 US states + DC by code. An empty "no state" option is added in front when missing. */
8907
+ readonly states: import("@angular/core").InputSignal<readonly SelectOption<string>[]>;
8908
+ readonly streetMaxlength: import("@angular/core").InputSignal<number>;
8909
+ readonly unitMaxlength: import("@angular/core").InputSignal<number>;
8910
+ readonly cityMaxlength: import("@angular/core").InputSignal<number>;
8911
+ readonly zipMaxlength: import("@angular/core").InputSignal<number>;
8912
+ /** Native select cannot be read-only: while `readonly`, the State select is disabled and the text fields are read-only. */
8913
+ readonly readonly: import("@angular/core").InputSignal<boolean>;
8914
+ /** Reveal every built-in message now (e.g. after a failed Save click). Switching back to false forgets which fields were left. */
8915
+ readonly showErrors: import("@angular/core").InputSignal<boolean>;
8916
+ readonly hint: import("@angular/core").InputSignal<string>;
8917
+ /** Host error for the whole address; shown under the group (replaces the hint), always makes the field invalid. */
8918
+ readonly error: import("@angular/core").InputSignal<string>;
8919
+ /** Prefix of the per-field test ids and native names: `<prefix>street`, `<prefix>apt`, `<prefix>city`, `<prefix>state`, `<prefix>zip` (and `<prefix>group`). */
8920
+ readonly idPrefix: import("@angular/core").InputSignal<string>;
8921
+ /** Alias of `idPrefix` (the name the old app-level address input used). When set (even to ''), it wins over `idPrefix`. */
8922
+ readonly prefix: import("@angular/core").InputSignal<string | undefined>;
8923
+ /**
8924
+ * What the form control (ngModel / FormControl) receives. 'auto' (default): the type last written to it - a string model gets the multi-line
8925
+ * string (`formatAddressValue`), an object / null / never-written model gets the `AddressValue`; 'text': always the string; 'object': always
8926
+ * the object. `valueChange` always emits the object. A written string is read with `parseAddressValue` for display only: the control keeps
8927
+ * the original text until the user edits a field (so a text the parser cannot split is not rewritten on load).
8928
+ */
8929
+ readonly valueFormat: import("@angular/core").InputSignal<"object" | "text" | "auto">;
8930
+ /** ZIP rule; the default is the US 5 digits / 5+4 rule. `null` accepts any non-empty ZIP. The ZIP is validated and stored trimmed. */
8931
+ readonly zipPattern: import("@angular/core").InputSignal<RegExp | null>;
8932
+ /** Message shown when the ZIP breaks `zipPattern`. */
8933
+ readonly zipError: import("@angular/core").InputSignal<string>;
8934
+ /** Signal-style value (use with `(valueChange)` or `[(value)]`); not needed with ngModel / FormControl. null / undefined = blank. */
8935
+ readonly value: import("@angular/core").InputSignal<string | AddressValue | null | undefined>;
8936
+ /** The `[disabled]` input (also set by the form control through `setDisabledState`). Public so `[disabled]="x"` type-checks in hosts. */
8937
+ readonly _disabledInput: import("@angular/core").InputSignal<boolean>;
8938
+ /** Emits the whole new address when the USER edits a field. Not emitted for written values. */
8939
+ readonly valueChange: import("@angular/core").OutputEmitterRef<AddressValue>;
8940
+ /** Emits whenever the address becomes invalid / valid. */
8941
+ readonly invalidChange: import("@angular/core").OutputEmitterRef<boolean>;
8942
+ private readonly host;
8943
+ private readonly _cvaDisabled;
8944
+ protected readonly disabled: import("@angular/core").Signal<boolean>;
8945
+ protected readonly _v: import("@angular/core").WritableSignal<AddressValue>;
8946
+ private readonly _touched;
8947
+ protected readonly _standalone: {
8948
+ standalone: boolean;
8949
+ };
8950
+ protected readonly _p: import("@angular/core").Signal<string>;
8951
+ private readonly _uid;
8952
+ protected readonly _legendId: string;
8953
+ protected readonly _errorId: string;
8954
+ protected readonly _hintId: string;
8955
+ protected readonly _stateOptions: import("@angular/core").Signal<SelectOption<string>[]>;
8956
+ private readonly _issues;
8957
+ /** True while the address breaks a built-in rule or the host passed an `[error]`. */
8958
+ readonly invalid: import("@angular/core").Signal<boolean>;
8959
+ /** Message of the first broken built-in rule in field order ('' when valid), shown or not. */
8960
+ readonly errorMessage: import("@angular/core").Signal<string>;
8961
+ /** Street, city, state and ZIP are required: always with `required`, and with `requireComplete` once any of them has text. */
8962
+ protected readonly _req: import("@angular/core").Signal<boolean>;
8963
+ protected readonly _describedBy: import("@angular/core").Signal<string | null>;
8964
+ private _onChange;
8965
+ /** True once a string was written to the control (valueFormat 'auto' then emits strings). */
8966
+ private _textModel;
8967
+ /** Fields whose text the USER changed since the last write; only those are trimmed when left. */
8968
+ private readonly _edited;
8969
+ private _onTouched;
8970
+ private _onValidatorChange;
8971
+ constructor();
8972
+ /** Re-run the form control's validation when a rule input (required, maxlength, states, ...) changes. */
8973
+ ngOnChanges(): void;
8974
+ writeValue(val: AddressValue | string | null | undefined): void;
8975
+ registerOnChange(fn: (v: AddressValue | string) => void): void;
8976
+ registerOnTouched(fn: () => void): void;
8977
+ setDisabledState(d: boolean): void;
8978
+ registerOnValidatorChange(fn: () => void): void;
8979
+ validate(control: AbstractControl): ValidationErrors | null;
8980
+ protected _set(field: AddressField, text: unknown): void;
8981
+ protected _onFocusOut(field: AddressField, event: FocusEvent): void;
8982
+ /** True while the field's own message may show: `showErrors`, or the field was left. Bound to the child's `showErrors` so a reset clears the child too. */
8983
+ protected _revealed(field: AddressField): boolean;
8984
+ protected _fieldError(field: AddressField): string;
8985
+ private _hasText;
8986
+ private _firstIssue;
8987
+ private _check;
8988
+ private _zipOk;
8989
+ static ɵfac: i0.ɵɵFactoryDeclaration<AddressInputComponent, never>;
8990
+ static ɵcmp: i0.ɵɵComponentDeclaration<AddressInputComponent, "ui-address-input", never, {
8991
+ "label": {
8992
+ "alias": "label";
8993
+ "required": false;
8994
+ "isSignal": true;
8995
+ };
8996
+ "required": {
8997
+ "alias": "required";
8998
+ "required": false;
8999
+ "isSignal": true;
9000
+ };
9001
+ "requireComplete": {
9002
+ "alias": "requireComplete";
9003
+ "required": false;
9004
+ "isSignal": true;
9005
+ };
9006
+ "streetLabel": {
9007
+ "alias": "streetLabel";
9008
+ "required": false;
9009
+ "isSignal": true;
9010
+ };
9011
+ "unitLabel": {
9012
+ "alias": "unitLabel";
9013
+ "required": false;
9014
+ "isSignal": true;
9015
+ };
9016
+ "cityLabel": {
9017
+ "alias": "cityLabel";
9018
+ "required": false;
9019
+ "isSignal": true;
9020
+ };
9021
+ "stateLabel": {
9022
+ "alias": "stateLabel";
9023
+ "required": false;
9024
+ "isSignal": true;
9025
+ };
9026
+ "zipLabel": {
9027
+ "alias": "zipLabel";
9028
+ "required": false;
9029
+ "isSignal": true;
9030
+ };
9031
+ "streetPlaceholder": {
9032
+ "alias": "streetPlaceholder";
9033
+ "required": false;
9034
+ "isSignal": true;
9035
+ };
9036
+ "unitPlaceholder": {
9037
+ "alias": "unitPlaceholder";
9038
+ "required": false;
9039
+ "isSignal": true;
9040
+ };
9041
+ "cityPlaceholder": {
9042
+ "alias": "cityPlaceholder";
9043
+ "required": false;
9044
+ "isSignal": true;
9045
+ };
9046
+ "zipPlaceholder": {
9047
+ "alias": "zipPlaceholder";
9048
+ "required": false;
9049
+ "isSignal": true;
9050
+ };
9051
+ "statePlaceholder": {
9052
+ "alias": "statePlaceholder";
9053
+ "required": false;
9054
+ "isSignal": true;
9055
+ };
9056
+ "showUnit": {
9057
+ "alias": "showUnit";
9058
+ "required": false;
9059
+ "isSignal": true;
9060
+ };
9061
+ "states": {
9062
+ "alias": "states";
9063
+ "required": false;
9064
+ "isSignal": true;
9065
+ };
9066
+ "streetMaxlength": {
9067
+ "alias": "streetMaxlength";
9068
+ "required": false;
9069
+ "isSignal": true;
9070
+ };
9071
+ "unitMaxlength": {
9072
+ "alias": "unitMaxlength";
9073
+ "required": false;
9074
+ "isSignal": true;
9075
+ };
9076
+ "cityMaxlength": {
9077
+ "alias": "cityMaxlength";
9078
+ "required": false;
9079
+ "isSignal": true;
9080
+ };
9081
+ "zipMaxlength": {
9082
+ "alias": "zipMaxlength";
9083
+ "required": false;
9084
+ "isSignal": true;
9085
+ };
9086
+ "readonly": {
9087
+ "alias": "readonly";
9088
+ "required": false;
9089
+ "isSignal": true;
9090
+ };
9091
+ "showErrors": {
9092
+ "alias": "showErrors";
9093
+ "required": false;
9094
+ "isSignal": true;
9095
+ };
9096
+ "hint": {
9097
+ "alias": "hint";
9098
+ "required": false;
9099
+ "isSignal": true;
9100
+ };
9101
+ "error": {
9102
+ "alias": "error";
9103
+ "required": false;
9104
+ "isSignal": true;
9105
+ };
9106
+ "idPrefix": {
9107
+ "alias": "idPrefix";
9108
+ "required": false;
9109
+ "isSignal": true;
9110
+ };
9111
+ "prefix": {
9112
+ "alias": "prefix";
9113
+ "required": false;
9114
+ "isSignal": true;
9115
+ };
9116
+ "valueFormat": {
9117
+ "alias": "valueFormat";
9118
+ "required": false;
9119
+ "isSignal": true;
9120
+ };
9121
+ "zipPattern": {
9122
+ "alias": "zipPattern";
9123
+ "required": false;
9124
+ "isSignal": true;
9125
+ };
9126
+ "zipError": {
9127
+ "alias": "zipError";
9128
+ "required": false;
9129
+ "isSignal": true;
9130
+ };
9131
+ "value": {
9132
+ "alias": "value";
9133
+ "required": false;
9134
+ "isSignal": true;
9135
+ };
9136
+ "_disabledInput": {
9137
+ "alias": "disabled";
9138
+ "required": false;
9139
+ "isSignal": true;
9140
+ };
9141
+ }, {
9142
+ "valueChange": "valueChange";
9143
+ "invalidChange": "invalidChange";
9144
+ }, never, never, true, never>;
9145
+ }
8712
9146
  export type ImagePreviewShape = 'circle' | 'rounded';
8713
9147
  export type ImagePreviewSize = 'sm' | 'md' | 'lg';
8714
9148
  export type ImagePreviewPlaceholder = 'person' | 'image';
@@ -9983,7 +10417,7 @@ export declare class PdfHotspotComponent {
9983
10417
  /** `ready`: clickable; `blocked`: disabled (a prerequisite is missing); `signed`: shows `image`. */
9984
10418
  readonly state: import("@angular/core").InputSignal<"ready" | "blocked" | "signed">;
9985
10419
  /** `warning` gives the ready state an amber look (e.g. for initials), default is blue. */
9986
- readonly tone: import("@angular/core").InputSignal<"primary" | "warning">;
10420
+ readonly tone: import("@angular/core").InputSignal<"warning" | "primary">;
9987
10421
  /** Text of the ready button (e.g. "Sign Here"). */
9988
10422
  readonly label: import("@angular/core").InputSignal<string>;
9989
10423
  /** Text of the blocked button (e.g. "Add signature above"). */
@@ -12300,6 +12734,12 @@ export declare function todayInTimezone(timezone?: string | null): string;
12300
12734
  * download). A no-op without a DOM (server-side rendering).
12301
12735
  */
12302
12736
  export declare function downloadBlob(blob: Blob, fileName: string): void;
12737
+ /**
12738
+ * The file name of a `Content-Disposition` header (`attachment; filename="a.zip"`, `filename=a.zip` or the encoded `filename*=UTF-8''a%20b.zip`),
12739
+ * or '' when the header is missing or names no file. Path separators and leading dots are removed so a server name can never point outside the
12740
+ * downloads folder.
12741
+ */
12742
+ export declare function fileNameFromContentDisposition(header: string | null | undefined): string;
12303
12743
  /**
12304
12744
  * A lightbox: a `ui-modal` that shows a `ui-image-gallery` with a Close button in the footer.
12305
12745
  *
@@ -13693,6 +14133,30 @@ export interface ConversationMessage {
13693
14133
  sender?: string | null;
13694
14134
  /** A link-style button under the text, e.g. "View transaction"; clicking it emits `(messageAction)` with this message. */
13695
14135
  action?: ConversationMessageAction | null;
14136
+ /** A record the message points at (a job, an estimate…), shown as a chip under the text; a chip with a `url` is a link. */
14137
+ link?: ConversationLink | null;
14138
+ }
14139
+ /** A record linked to a message (see `ConversationMessage.link`, `[(link)]` and `ConversationSendEvent.link`). */
14140
+ export interface ConversationLink {
14141
+ /** What kind of record it is (`JOB`, `ESTIMATE`…): the host's own code, handed back unchanged. */
14142
+ type: string;
14143
+ /** The record's id. */
14144
+ id: string;
14145
+ /** What the chip says, e.g. "Johnson Roof — Alice". */
14146
+ label: string;
14147
+ /** Where the chip goes when it is clicked; none = a plain chip. Only used on messages, not in the composer. */
14148
+ url?: string | null;
14149
+ }
14150
+ /** One section of the "Link a record" picker: the records of one kind. */
14151
+ export interface ConversationLinkGroup {
14152
+ /** The kind, copied to `ConversationLink.type` of every pick from this section. */
14153
+ type: string;
14154
+ /** The section heading, e.g. "Jobs". */
14155
+ label: string;
14156
+ items: ReadonlyArray<{
14157
+ id: string;
14158
+ label: string;
14159
+ }>;
13696
14160
  }
13697
14161
  /** The button a message can carry (see `ConversationMessage.action`). */
13698
14162
  export interface ConversationMessageAction {
@@ -13710,6 +14174,8 @@ export interface ConversationSendEvent {
13710
14174
  text: string;
13711
14175
  /** Only with `[richText]="true"`: the formatted message as sanitised HTML (bold, italic, underline, links, lists). */
13712
14176
  html?: string;
14177
+ /** Only when a record is linked (`[(link)]`): the chosen record. The key is absent otherwise. */
14178
+ link?: ConversationLink;
13713
14179
  }
13714
14180
  /** A message with the date separator that goes above it ('' when it shares the day of the previous message). */
13715
14181
  export interface ConversationMessageRow {
@@ -13921,6 +14387,24 @@ export declare class ConversationsComponent {
13921
14387
  /** Files accepted by the attach picker (type and size already checked against `acceptedFiles` / `maxFileSize`). */
13922
14388
  readonly filesSelected: import("@angular/core").OutputEmitterRef<File[]>;
13923
14389
  readonly attachmentRemove: import("@angular/core").OutputEmitterRef<void>;
14390
+ /**
14391
+ * Link a record to a message (opt-in): `null` (default) = no link button, no picker, nothing changes. A list (even an empty one) turns on a "Link a record"
14392
+ * button in the composer that opens a searchable picker of these sections; the pick becomes `[(link)]`, shows as a removable chip above the composer, and
14393
+ * goes out with `(send)` as `link`. Load the sections when `(linkPickerOpened)` fires and show `[linkLoading]` meanwhile.
14394
+ */
14395
+ readonly linkGroups: import("@angular/core").InputSignal<readonly ConversationLinkGroup[] | null>;
14396
+ readonly linkLoading: import("@angular/core").InputSignal<boolean>;
14397
+ /** The record linked to the message being written. Cleared by the host after a send, like `[(draft)]`; the chip's × clears it too. */
14398
+ readonly link: import("@angular/core").ModelSignal<ConversationLink | null>;
14399
+ readonly linkLabel: import("@angular/core").InputSignal<string>;
14400
+ readonly linkPickerTitle: import("@angular/core").InputSignal<string>;
14401
+ readonly linkSearchPlaceholder: import("@angular/core").InputSignal<string>;
14402
+ readonly linkSearchLabel: import("@angular/core").InputSignal<string>;
14403
+ readonly linkEmptyText: import("@angular/core").InputSignal<string>;
14404
+ readonly linkLoadingText: import("@angular/core").InputSignal<string>;
14405
+ readonly removeLinkLabel: import("@angular/core").InputSignal<string>;
14406
+ /** The "Link a record" button opened the picker: load the sections now. */
14407
+ readonly linkPickerOpened: import("@angular/core").OutputEmitterRef<void>;
13924
14408
  /** An image in a bubble was clicked: the host opens it (usually in a new tab). */
13925
14409
  readonly mediaOpen: import("@angular/core").OutputEmitterRef<string>;
13926
14410
  /** The link-style button of a message (`ConversationMessage.action`) was clicked. */
@@ -13949,8 +14433,12 @@ export declare class ConversationsComponent {
13949
14433
  protected readonly emptyText: import("@angular/core").Signal<string>;
13950
14434
  protected readonly messageRows: import("@angular/core").Signal<import("@forgeroute/ui").ConversationMessageRow[]>;
13951
14435
  protected readonly contextVisible: import("@angular/core").Signal<boolean>;
13952
- /** Something to send (text or a file), the composer is open and no request is running. */
14436
+ /** Something to send (text, a file or a linked record), the composer is open and no request is running. */
13953
14437
  readonly canSend: import("@angular/core").Signal<boolean>;
14438
+ protected readonly pickerOpen: import("@angular/core").WritableSignal<boolean>;
14439
+ protected readonly linkQuery: import("@angular/core").WritableSignal<string>;
14440
+ /** The picker's sections narrowed by the search (case-insensitive, on the record label); a section with no match is left out. */
14441
+ protected readonly visibleLinkGroups: import("@angular/core").Signal<ConversationLinkGroup[]>;
13954
14442
  /** The composer's formatted content. `draft` is always its plain text, so a host that reads or sets `[(draft)]` never sees markup. */
13955
14443
  protected readonly editorHtml: import("@angular/core").WritableSignal<string>;
13956
14444
  protected readonly richHide: RichTextOption[];
@@ -13969,6 +14457,12 @@ export declare class ConversationsComponent {
13969
14457
  protected onComposeKeydown(event: KeyboardEvent): void;
13970
14458
  protected onEditorChange(html: string): void;
13971
14459
  protected sendMessage(): void;
14460
+ protected openLinkPicker(): void;
14461
+ protected closeLinkPicker(): void;
14462
+ protected pickLink(group: ConversationLinkGroup, item: {
14463
+ id: string;
14464
+ label: string;
14465
+ }): void;
13972
14466
  private clock;
13973
14467
  private singleColumn;
13974
14468
  private scrollToBottom;
@@ -14399,6 +14893,56 @@ export declare class ConversationsComponent {
14399
14893
  "required": false;
14400
14894
  "isSignal": true;
14401
14895
  };
14896
+ "linkGroups": {
14897
+ "alias": "linkGroups";
14898
+ "required": false;
14899
+ "isSignal": true;
14900
+ };
14901
+ "linkLoading": {
14902
+ "alias": "linkLoading";
14903
+ "required": false;
14904
+ "isSignal": true;
14905
+ };
14906
+ "link": {
14907
+ "alias": "link";
14908
+ "required": false;
14909
+ "isSignal": true;
14910
+ };
14911
+ "linkLabel": {
14912
+ "alias": "linkLabel";
14913
+ "required": false;
14914
+ "isSignal": true;
14915
+ };
14916
+ "linkPickerTitle": {
14917
+ "alias": "linkPickerTitle";
14918
+ "required": false;
14919
+ "isSignal": true;
14920
+ };
14921
+ "linkSearchPlaceholder": {
14922
+ "alias": "linkSearchPlaceholder";
14923
+ "required": false;
14924
+ "isSignal": true;
14925
+ };
14926
+ "linkSearchLabel": {
14927
+ "alias": "linkSearchLabel";
14928
+ "required": false;
14929
+ "isSignal": true;
14930
+ };
14931
+ "linkEmptyText": {
14932
+ "alias": "linkEmptyText";
14933
+ "required": false;
14934
+ "isSignal": true;
14935
+ };
14936
+ "linkLoadingText": {
14937
+ "alias": "linkLoadingText";
14938
+ "required": false;
14939
+ "isSignal": true;
14940
+ };
14941
+ "removeLinkLabel": {
14942
+ "alias": "removeLinkLabel";
14943
+ "required": false;
14944
+ "isSignal": true;
14945
+ };
14402
14946
  }, {
14403
14947
  "searchText": "searchTextChange";
14404
14948
  "contextOpen": "contextOpenChange";
@@ -14416,10 +14960,609 @@ export declare class ConversationsComponent {
14416
14960
  "send": "send";
14417
14961
  "filesSelected": "filesSelected";
14418
14962
  "attachmentRemove": "attachmentRemove";
14963
+ "link": "linkChange";
14964
+ "linkPickerOpened": "linkPickerOpened";
14419
14965
  "mediaOpen": "mediaOpen";
14420
14966
  "messageAction": "messageAction";
14421
14967
  "loadEarlier": "loadEarlier";
14422
14968
  "messagesRetry": "messagesRetry";
14423
14969
  }, never, ["[uiConversationsContext]"], true, never>;
14424
14970
  }
14425
- export type { CalendarAgendaGroup, CalendarDay, CalendarDayLevel, CalendarRange, CalendarViewMode };
14971
+ /**
14972
+ * Types and pure helpers of `ui-payment-side-panel`. Nothing here knows a payment processor: Stripe / Square arrive as plugins
14973
+ * (`PaymentMethodPlugin`), the card-present terminal as host callbacks.
14974
+ */
14975
+ /** Well-known method values the panel gives behaviour to. Hosts may list any other value (it is recorded like Cash). */
14976
+ export declare const PAYMENT_METHOD: {
14977
+ readonly CARD: "CARD";
14978
+ readonly ACH: "ACH";
14979
+ readonly CASH: "CASH";
14980
+ readonly CHECK: "CHECK";
14981
+ readonly MONEY_ORDER: "MONEY_ORDER";
14982
+ readonly CARD_PRESENT: "CARD_PRESENT";
14983
+ };
14984
+ interface PaymentMethodOption {
14985
+ value: string;
14986
+ label: string;
14987
+ /** `'terminal'`: listed only while the host gave the terminal callbacks (`listReaders` + `startTerminalPayment`). */
14988
+ requires?: 'terminal';
14989
+ }
14990
+ /** The methods listed when the host gives none: Card and ACH are record-only until a processor plugin takes them over. */
14991
+ export declare const DEFAULT_PAYMENT_METHODS: readonly PaymentMethodOption[];
14992
+ interface PayableCharge {
14993
+ id: string;
14994
+ chargeType: string;
14995
+ description: string;
14996
+ /** ISO date `YYYY-MM-DD`. */
14997
+ dueDate: string;
14998
+ balanceDue: number;
14999
+ }
15000
+ interface SavedPaymentMethod {
15001
+ method: string;
15002
+ last4: string;
15003
+ label?: string;
15004
+ }
15005
+ interface AllocationModeOption {
15006
+ value: string;
15007
+ label: string;
15008
+ }
15009
+ export declare const ALLOCATION_CUSTOM = "custom";
15010
+ export declare const DEFAULT_ALLOCATION_MODES: readonly AllocationModeOption[];
15011
+ interface ChargeAllocation {
15012
+ chargeId: string;
15013
+ amount: number;
15014
+ }
15015
+ /**
15016
+ * How the payment is applied to charges. A non-custom mode sends `{ allocationMode }`. "Choose charges" sends `{ allocationChargeIds, allocations }`
15017
+ * (no `allocationMode`, like the old drawer); `allocations` carries the amount per charge.
15018
+ */
15019
+ interface PaymentAllocation {
15020
+ allocationMode?: string;
15021
+ allocationChargeIds?: string[];
15022
+ allocations?: ChargeAllocation[];
15023
+ }
15024
+ interface PaymentEntry {
15025
+ paymentMethod: string;
15026
+ amount: number;
15027
+ /** Check number / last 4 digits; `null` when the method has none. */
15028
+ reference: string | null;
15029
+ /** The receipt checkbox applies to the LAST entry only (like the old drawer); earlier split entries are `false`. */
15030
+ sendReceipt: boolean;
15031
+ }
15032
+ interface RecordPaymentPayload extends PaymentAllocation {
15033
+ /** Total of all entries. */
15034
+ amount: number;
15035
+ /** The method of a single payment, `'SPLIT'` when `split`. */
15036
+ paymentMethod: string;
15037
+ receivedDate: string;
15038
+ /** Reference of the single payment (`null` for a split: see `entries`). */
15039
+ reference: string | null;
15040
+ notes: string | null;
15041
+ sendReceipt: boolean;
15042
+ /** One per payment to create (the host posts one request per entry, or one request with all). */
15043
+ entries: PaymentEntry[];
15044
+ split: boolean;
15045
+ }
15046
+ interface RecordPaymentResult {
15047
+ type: 'manual' | 'stripe' | 'split';
15048
+ paymentMethod: string;
15049
+ amount: number;
15050
+ }
15051
+ /** What the panel reports for the host to word (toasts, analytics). It never toasts itself. */
15052
+ type PaymentPanelOutcome = {
15053
+ kind: 'recorded';
15054
+ result: RecordPaymentResult;
15055
+ } | {
15056
+ kind: 'split-recorded';
15057
+ result: RecordPaymentResult;
15058
+ } | {
15059
+ kind: 'terminal-paid';
15060
+ result: RecordPaymentResult;
15061
+ } | {
15062
+ kind: 'plugin';
15063
+ method: string;
15064
+ code: string;
15065
+ result?: RecordPaymentResult;
15066
+ message?: string;
15067
+ } | {
15068
+ kind: 'failed';
15069
+ stage: 'record' | 'readers' | 'terminal';
15070
+ message: string;
15071
+ };
15072
+ type HostResult<T> = Observable<T> | Promise<T> | T;
15073
+ type RecordPaymentFn = (payload: RecordPaymentPayload) => HostResult<unknown>;
15074
+ interface TerminalReader {
15075
+ id: string;
15076
+ label?: string;
15077
+ status: string;
15078
+ deviceType?: string;
15079
+ }
15080
+ interface TerminalStartContext extends PaymentAllocation {
15081
+ readerId: string;
15082
+ amount: number;
15083
+ receivedDate: string;
15084
+ notes: string | null;
15085
+ }
15086
+ interface TerminalStartResult {
15087
+ paymentIntentId: string;
15088
+ }
15089
+ interface TerminalStatusResult {
15090
+ status: string;
15091
+ }
15092
+ interface TerminalConfirmContext extends TerminalStartContext {
15093
+ paymentIntentId: string;
15094
+ }
15095
+ type TerminalPhase = 'idle' | 'waiting' | 'confirming' | 'success';
15096
+ /** Reader statuses that end the wait. */
15097
+ export declare function terminalVerdict(status: string | null | undefined): 'succeeded' | 'canceled' | 'declined' | 'pending';
15098
+ export declare function terminalTimeoutText(ms: number): string;
15099
+ export declare const MIN_PAYMENT_AMOUNT = 0.01;
15100
+ export declare const MAX_PAYMENT_AMOUNT = 99999.99;
15101
+ declare const toCents: (value: number) => number;
15102
+ declare const fromCents: (cents: number) => number;
15103
+ declare const roundMoney: (value: number) => number;
15104
+ /** A payable amount: a finite number from 0.01 to `max` with at most two decimals. */
15105
+ export declare function isValidAmount(value: unknown, max?: number): value is number;
15106
+ declare function sumMoney(values: readonly number[]): number;
15107
+ declare function referenceKind(method: string, live: boolean): 'check' | 'last4-card' | 'last4-account' | null;
15108
+ /** The last-4 box accepts digits only (blank is fine). */
15109
+ export declare function last4Error(value: string): string;
15110
+ declare function last4Digits(value: string, max?: number): string;
15111
+ interface AllocationCheck {
15112
+ general: string;
15113
+ perCharge: Record<string, string>;
15114
+ }
15115
+ type SelectedCharges = Record<string, number | null>;
15116
+ declare function allocationCheck(charges: readonly PayableCharge[], selected: SelectedCharges, totalPayment: number | null, format: (n: number) => string): AllocationCheck;
15117
+ /** The amount to propose when a charge is ticked: its balance, capped by what is left of the payment. */
15118
+ declare function defaultChargeAmount(charge: PayableCharge, selected: SelectedCharges, totalPayment: number | null): number | null;
15119
+ declare function buildAllocation(mode: string, charges: readonly PayableCharge[], selected: SelectedCharges): PaymentAllocation;
15120
+ /** "2026-11-03" -> "Nov 3, 2026" without `Date` string parsing (Safari-safe, time-zone independent). */
15121
+ declare function formatDueDate(iso: string, locale?: string): string;
15122
+ /** Runs a host callback that may return an Observable, a Promise or a value; a synchronous throw becomes a rejection. */
15123
+ export declare function callHost<T>(thunk: () => HostResult<T>): Promise<T>;
15124
+ /** The text of a failed host call: its HTTP body message, its message, else the fallback. */
15125
+ export declare function hostErrorMessage(err: unknown, fallback: string): string;
15126
+ /** A method a plugin contributes to the panel's method select (while the plugin is rendered inside the panel). */
15127
+ export interface PaymentPluginMethod {
15128
+ value: string;
15129
+ label: string;
15130
+ }
15131
+ /** The footer button a plugin body may ask the panel to show instead of the panel's own (the panel's own button is hidden for plugin methods). */
15132
+ export interface PaymentPluginPrimary {
15133
+ label: string;
15134
+ disabled?: boolean;
15135
+ loading?: boolean;
15136
+ onClick: () => void;
15137
+ }
15138
+ /**
15139
+ * What the panel hands to a plugin body (`<ng-template let-ctx>`). The values are a snapshot that is replaced whenever the form changes (read them in the
15140
+ * template or in an `input()` of a child component); the functions are stable.
15141
+ */
15142
+ export interface PaymentMethodContext {
15143
+ /** The selected method value (one of the plugin's `methods`). */
15144
+ readonly method: string;
15145
+ /** The entered amount, or `null` while it is empty or invalid (a plugin should not start a charge without it). */
15146
+ readonly amount: number | null;
15147
+ /** ISO date the payment was received. */
15148
+ readonly receivedDate: string;
15149
+ readonly note: string;
15150
+ /** How the payment is applied to charges (same shape the manual payload carries). */
15151
+ readonly allocation: PaymentAllocation;
15152
+ /** ISO 4217 code of the panel (`'USD'`). */
15153
+ readonly currency: string;
15154
+ /** Every field of the panel is valid (amount, date, note, allocation). */
15155
+ readonly valid: boolean;
15156
+ /** Reveals the panel's field errors (as a failed Save click does) and returns whether the form is valid. Call it before starting a charge. */
15157
+ validate(): boolean;
15158
+ /** Clears the whole form (method, amount, note, split, allocation; the date returns to today). */
15159
+ reset(): void;
15160
+ /** The payment is done: the panel reports it (`outcome` kind `'plugin'`, code `'complete'`, then `recorded`). Defaults: type `'manual'`, the selected method, the entered amount. Runs once. */
15161
+ complete(result?: Partial<RecordPaymentResult>): void;
15162
+ /** A flow is running: the fields are locked and the method cannot be changed. */
15163
+ setBusy(busy: boolean): void;
15164
+ /** The plugin's own footer button (e.g. "Pay $120.00"); `null` removes it. The panel's own Save button never shows for a plugin method. */
15165
+ setPrimary(button: PaymentPluginPrimary | null): void;
15166
+ /** Hide the panel's fields and show only the plugin body (e.g. a "payment submitted" notice). */
15167
+ setExclusive(on: boolean): void;
15168
+ /** Called when the body is about to go away unfinished: the panel closes, the method changes, or the plugin is removed. Not called after `complete()`. */
15169
+ onCancel(fn: (() => void) | null): void;
15170
+ /** Tell the host something happened (`outcome` kind `'plugin'`) so it can word a toast. */
15171
+ report(code: string, extra?: {
15172
+ result?: RecordPaymentResult;
15173
+ message?: string;
15174
+ }): void;
15175
+ /** Text for the panel's screen-reader status region. */
15176
+ announce(text: string): void;
15177
+ }
15178
+ export interface PaymentMethodBodyContext {
15179
+ $implicit: PaymentMethodContext;
15180
+ }
15181
+ /**
15182
+ * The contract of a payment plugin: a child element of `<ui-payment-side-panel>` that contributes live methods (Stripe, Square, ...).
15183
+ * The panel finds plugins with `contentChildren(PAYMENT_METHOD_PLUGIN)`, so presence IS the opt-in; remove the element and its methods leave the select.
15184
+ * A plugin whose method value is `CARD` or `ACH` replaces the record-only behaviour (last 4 digits) of that value while it is rendered.
15185
+ */
15186
+ export declare abstract class PaymentMethodPlugin {
15187
+ abstract readonly methods: Signal<readonly PaymentPluginMethod[]>;
15188
+ /** The body shown under the form while one of the plugin's methods is selected. */
15189
+ abstract readonly template: Signal<TemplateRef<PaymentMethodBodyContext> | undefined>;
15190
+ }
15191
+ export declare const PAYMENT_METHOD_PLUGIN: InjectionToken<PaymentMethodPlugin>;
15192
+ /**
15193
+ * The generic payment plugin: the host renders the body of a method it built itself (Square Web SDK, a gateway widget, ...).
15194
+ *
15195
+ * <ui-payment-side-panel ...>
15196
+ * <ui-payment-custom-method value="CARD" label="Card">
15197
+ * <ng-template let-ctx> <my-square-card [amount]="ctx.amount" (paid)="ctx.complete({ type: 'stripe' })" /> </ng-template>
15198
+ * </ui-payment-custom-method>
15199
+ * </ui-payment-side-panel>
15200
+ */
15201
+ export declare class PaymentCustomMethodComponent extends PaymentMethodPlugin {
15202
+ readonly value: import("@angular/core").InputSignal<string>;
15203
+ readonly label: import("@angular/core").InputSignal<string>;
15204
+ private readonly body;
15205
+ readonly methods: import("@angular/core").Signal<readonly PaymentPluginMethod[]>;
15206
+ readonly template: import("@angular/core").Signal<TemplateRef<PaymentMethodBodyContext> | undefined>;
15207
+ static ɵfac: i0.ɵɵFactoryDeclaration<PaymentCustomMethodComponent, never>;
15208
+ static ɵcmp: i0.ɵɵComponentDeclaration<PaymentCustomMethodComponent, "ui-payment-custom-method", never, {
15209
+ "value": {
15210
+ "alias": "value";
15211
+ "required": true;
15212
+ "isSignal": true;
15213
+ };
15214
+ "label": {
15215
+ "alias": "label";
15216
+ "required": true;
15217
+ "isSignal": true;
15218
+ };
15219
+ }, {}, ["body"], never, true, never>;
15220
+ }
15221
+ interface SplitEntry {
15222
+ method: string;
15223
+ amount: number;
15224
+ reference: string;
15225
+ }
15226
+ /**
15227
+ * Side panel for staff to RECORD or COLLECT a payment. Works on its own (record-only: Cash, Check, Money Order, Card and ACH with the last 4 digits) and grows
15228
+ * by PLUGINS the host drops in as child elements (`ui-payment-stripe-method` from `@forgeroute/ui/stripe`, `ui-payment-custom-method` for Square or any widget):
15229
+ * a plugin's presence IS the opt-in, and a plugin method with the value `CARD` / `ACH` replaces the record-only behaviour while it is rendered.
15230
+ * The panel has no HTTP and no processor knowledge: the host gives callbacks (`recordPayment`, the terminal calls) and words the `outcome` events.
15231
+ *
15232
+ * <ui-payment-side-panel [open]="open()" [today]="today()" [outstandingBalance]="due()" [charges]="charges()"
15233
+ * [recordPayment]="record" (recorded)="onRecorded($event)" (closed)="open.set(false)" />
15234
+ *
15235
+ * See docs/payment-side-panel.md.
15236
+ */
15237
+ export declare class PaymentSidePanelComponent {
15238
+ private readonly injector;
15239
+ private readonly hostEl;
15240
+ protected readonly ids: string;
15241
+ protected readonly customMode = "custom";
15242
+ readonly open: import("@angular/core").InputSignal<boolean>;
15243
+ readonly title: import("@angular/core").InputSignal<string>;
15244
+ readonly subtitle: import("@angular/core").InputSignal<string>;
15245
+ /** Today as `YYYY-MM-DD` (the host's clock/time zone): the default Received Date. The panel never builds dates itself. */
15246
+ readonly today: import("@angular/core").InputSignal<string>;
15247
+ /** When > 0 a banner shows it and the amount starts at this balance. */
15248
+ readonly outstandingBalance: import("@angular/core").InputSignal<number | null>;
15249
+ /** ISO 4217 code used for every amount shown. */
15250
+ readonly currency: import("@angular/core").InputSignal<string>;
15251
+ readonly maxAmount: import("@angular/core").InputSignal<number>;
15252
+ /** Latest Received Date (ISO). Default: `today`, because a payment cannot be received in the future. */
15253
+ readonly maxDate: import("@angular/core").InputSignal<string>;
15254
+ /** The methods listed (value/label). Default: Card, ACH / Bank Transfer, Cash, Check, Money Order, Card Present (terminal callbacks given). */
15255
+ readonly methods: import("@angular/core").InputSignal<readonly PaymentMethodOption[]>;
15256
+ readonly savedMethods: import("@angular/core").InputSignal<readonly SavedPaymentMethod[]>;
15257
+ readonly charges: import("@angular/core").InputSignal<readonly PayableCharge[]>;
15258
+ readonly allocationModes: import("@angular/core").InputSignal<readonly AllocationModeOption[]>;
15259
+ readonly defaultAllocationMode: import("@angular/core").InputSignal<string>;
15260
+ /** Text of the "send receipt" checkbox (e.g. "Email receipt to resident"); empty hides it. */
15261
+ readonly receiptLabel: import("@angular/core").InputSignal<string>;
15262
+ readonly submitLabel: import("@angular/core").InputSignal<string>;
15263
+ readonly splitSubmitLabel: import("@angular/core").InputSignal<string>;
15264
+ /** Records the payment. Returns an Observable / Promise; a rejection is shown in the panel and it stays open. */
15265
+ readonly recordPayment: import("@angular/core").InputSignal<RecordPaymentFn | null>;
15266
+ readonly listReaders: import("@angular/core").InputSignal<(() => HostResult<readonly TerminalReader[]>) | null>;
15267
+ readonly startTerminalPayment: import("@angular/core").InputSignal<((ctx: TerminalStartContext) => HostResult<TerminalStartResult>) | null>;
15268
+ readonly terminalStatus: import("@angular/core").InputSignal<((paymentIntentId: string) => HostResult<TerminalStatusResult>) | null>;
15269
+ readonly confirmTerminalPayment: import("@angular/core").InputSignal<((ctx: TerminalConfirmContext) => HostResult<unknown>) | null>;
15270
+ readonly cancelTerminal: import("@angular/core").InputSignal<((readerId: string) => HostResult<unknown>) | null>;
15271
+ readonly terminalProvider: import("@angular/core").InputSignal<string>;
15272
+ readonly noReadersText: import("@angular/core").InputSignal<string>;
15273
+ readonly terminalPollMs: import("@angular/core").InputSignal<number>;
15274
+ readonly terminalTimeoutMs: import("@angular/core").InputSignal<number>;
15275
+ readonly readerRefreshMs: import("@angular/core").InputSignal<number>;
15276
+ readonly terminalSuccessDelayMs: import("@angular/core").InputSignal<number>;
15277
+ readonly closed: import("@angular/core").OutputEmitterRef<void>;
15278
+ /** The payment is done (any flow). The host closes the panel. */
15279
+ readonly recorded: import("@angular/core").OutputEmitterRef<RecordPaymentResult>;
15280
+ /** What happened, for the host to word (the panel never toasts). */
15281
+ readonly outcome: import("@angular/core").OutputEmitterRef<PaymentPanelOutcome>;
15282
+ private readonly plugins;
15283
+ private readonly fields;
15284
+ readonly method: import("@angular/core").WritableSignal<string>;
15285
+ readonly amount: import("@angular/core").WritableSignal<number | null>;
15286
+ readonly receivedDate: import("@angular/core").WritableSignal<string>;
15287
+ readonly reference: import("@angular/core").WritableSignal<string>;
15288
+ readonly note: import("@angular/core").WritableSignal<string>;
15289
+ readonly sendReceipt: import("@angular/core").WritableSignal<boolean>;
15290
+ readonly splitEntries: import("@angular/core").WritableSignal<SplitEntry[]>;
15291
+ readonly allocationMode: import("@angular/core").WritableSignal<string>;
15292
+ readonly selectedCharges: import("@angular/core").WritableSignal<SelectedCharges>;
15293
+ readonly touched: import("@angular/core").WritableSignal<boolean>;
15294
+ readonly submitting: import("@angular/core").WritableSignal<boolean>;
15295
+ readonly submitError: import("@angular/core").WritableSignal<string>;
15296
+ readonly announcement: import("@angular/core").WritableSignal<string>;
15297
+ private readonly pluginBusy;
15298
+ protected readonly exclusive: import("@angular/core").WritableSignal<boolean>;
15299
+ private readonly pluginPrimary;
15300
+ private cancelHook;
15301
+ private completed;
15302
+ readonly readers: import("@angular/core").WritableSignal<readonly TerminalReader[]>;
15303
+ readonly readersLoaded: import("@angular/core").WritableSignal<boolean>;
15304
+ readonly loadingReaders: import("@angular/core").WritableSignal<boolean>;
15305
+ readonly readersError: import("@angular/core").WritableSignal<string>;
15306
+ readonly readerId: import("@angular/core").WritableSignal<string>;
15307
+ readonly terminalPhase: import("@angular/core").WritableSignal<TerminalPhase>;
15308
+ readonly terminalError: import("@angular/core").WritableSignal<string>;
15309
+ readonly sendingToReader: import("@angular/core").WritableSignal<boolean>;
15310
+ readonly retryIntentId: import("@angular/core").WritableSignal<string>;
15311
+ readonly retrying: import("@angular/core").WritableSignal<boolean>;
15312
+ readonly terminalAmount: import("@angular/core").WritableSignal<number>;
15313
+ /** Bumped when the form is reset or the panel closes: an async terminal step that finishes later must not touch the new state. */
15314
+ private session;
15315
+ private retryContext;
15316
+ private pollTimer;
15317
+ private pollToken;
15318
+ private refreshTimer;
15319
+ private successTimer;
15320
+ private pendingTerminalResult;
15321
+ private terminalContext;
15322
+ protected readonly busy: import("@angular/core").Signal<boolean>;
15323
+ /** Fields are read-only while something runs, and while a taken terminal payment still has to be recorded (its amount must not change). */
15324
+ protected readonly locked: import("@angular/core").Signal<boolean>;
15325
+ protected readonly dateMax: import("@angular/core").Signal<string>;
15326
+ protected readonly money: (value: number) => string;
15327
+ protected readonly currencySymbol: import("@angular/core").Signal<string>;
15328
+ private readonly terminalAvailable;
15329
+ /** value -> label of every live method the rendered plugins contribute (the first plugin wins a duplicate value). */
15330
+ private readonly liveMethods;
15331
+ protected readonly kind: import("@angular/core").Signal<"none" | "live" | "terminal" | "manual">;
15332
+ private kindOf;
15333
+ /** The method list as shown: record-only built-ins (a plugin takes over its own values), plugin extras, and only manual ones while a split is being built. */
15334
+ readonly methodList: import("@angular/core").Signal<PaymentMethodOption[]>;
15335
+ protected readonly methodOptions: import("@angular/core").Signal<{
15336
+ value: string;
15337
+ label: string;
15338
+ }[]>;
15339
+ protected readonly methodRequired: import("@angular/core").Signal<boolean>;
15340
+ protected readonly allocationOptions: import("@angular/core").Signal<{
15341
+ value: string;
15342
+ label: string;
15343
+ }[]>;
15344
+ /** Hidden while a taken terminal payment still has to be recorded: the method cannot change then. */
15345
+ protected readonly savedChips: import("@angular/core").Signal<SavedPaymentMethod[]>;
15346
+ protected readonly refKind: import("@angular/core").Signal<"check" | "last4-card" | "last4-account" | null>;
15347
+ protected readonly referenceError: import("@angular/core").Signal<string>;
15348
+ private readonly amountValid;
15349
+ protected readonly splitTotal: import("@angular/core").Signal<number>;
15350
+ private readonly totalPayment;
15351
+ protected readonly allocation: import("@angular/core").Signal<AllocationCheck>;
15352
+ private readonly allocationPayload;
15353
+ protected readonly hasInvalid: import("@angular/core").Signal<boolean>;
15354
+ protected readonly canSplit: import("@angular/core").Signal<boolean>;
15355
+ protected readonly readerOptions: import("@angular/core").Signal<{
15356
+ value: string;
15357
+ label: string;
15358
+ }[]>;
15359
+ protected readonly selectedReader: import("@angular/core").Signal<TerminalReader | null>;
15360
+ protected readonly canSendToReader: import("@angular/core").Signal<boolean>;
15361
+ protected readonly pluginTemplate: import("@angular/core").Signal<import("@angular/core").TemplateRef<PaymentMethodBodyContext> | undefined>;
15362
+ protected readonly pluginContext: import("@angular/core").Signal<PaymentMethodBodyContext>;
15363
+ protected readonly primaryLabel: import("@angular/core").Signal<string>;
15364
+ protected readonly primaryLoading: import("@angular/core").Signal<boolean>;
15365
+ protected readonly primaryDisabled: import("@angular/core").Signal<boolean>;
15366
+ constructor();
15367
+ private init;
15368
+ private teardown;
15369
+ private resetForm;
15370
+ /** A card payment the reader took but that was never recorded is being abandoned: the host must hear about it. */
15371
+ private dropPendingRetry;
15372
+ private cleanup;
15373
+ protected onClose(): void;
15374
+ private runCancelHook;
15375
+ protected onMethodChange(value: string | null): void;
15376
+ protected selectSaved(saved: SavedPaymentMethod): void;
15377
+ protected chipLabel(saved: SavedPaymentMethod): string;
15378
+ protected methodLabel(value: string): string;
15379
+ protected onLast4(value: string | null): void;
15380
+ protected addSplit(): void;
15381
+ protected removeSplit(index: number): void;
15382
+ protected onAllocationMode(value: string | null): void;
15383
+ protected isTicked(id: string): boolean;
15384
+ protected toggleCharge(charge: PayableCharge, checked: boolean): void;
15385
+ protected setChargeAmount(id: string, value: number | string | null): void;
15386
+ protected chargeDescription(charge: PayableCharge): string;
15387
+ private referenceFor;
15388
+ private buildEntries;
15389
+ /** The payload the host receives, or `null` when there is nothing valid to record. */
15390
+ buildPayload(): RecordPaymentPayload | null;
15391
+ protected onPrimary(): void;
15392
+ submit(): Promise<void>;
15393
+ /**
15394
+ * A host that posts the entries one by one can tell the panel how many went through before a failure: `throw { message, postedEntries: 1 }`.
15395
+ * Those first entries are removed, so a retry does not post them again.
15396
+ */
15397
+ private dropPostedEntries;
15398
+ private buildContext;
15399
+ private completeFromPlugin;
15400
+ private resetTerminal;
15401
+ private startTerminalSession;
15402
+ protected refreshReaders(): void;
15403
+ private loadReaders;
15404
+ private scheduleReaderRefresh;
15405
+ private stopReaderRefresh;
15406
+ private terminalBase;
15407
+ sendToReader(): Promise<void>;
15408
+ private startPolling;
15409
+ private stopPolling;
15410
+ private endWaiting;
15411
+ private confirmTerminal;
15412
+ /** Repeats the recording for the payment the reader already took, with the amount that was charged (not the form's current values). */
15413
+ protected retryRecording(): Promise<void>;
15414
+ private finishTerminal;
15415
+ protected cancelTerminalPayment(): void;
15416
+ private cancelTerminalIfWaiting;
15417
+ /** Moves focus after the next render (the control that had it was just removed from the DOM). */
15418
+ private focusLater;
15419
+ private fireCancelReader;
15420
+ static ɵfac: i0.ɵɵFactoryDeclaration<PaymentSidePanelComponent, never>;
15421
+ static ɵcmp: i0.ɵɵComponentDeclaration<PaymentSidePanelComponent, "ui-payment-side-panel", never, {
15422
+ "open": {
15423
+ "alias": "open";
15424
+ "required": false;
15425
+ "isSignal": true;
15426
+ };
15427
+ "title": {
15428
+ "alias": "title";
15429
+ "required": false;
15430
+ "isSignal": true;
15431
+ };
15432
+ "subtitle": {
15433
+ "alias": "subtitle";
15434
+ "required": false;
15435
+ "isSignal": true;
15436
+ };
15437
+ "today": {
15438
+ "alias": "today";
15439
+ "required": false;
15440
+ "isSignal": true;
15441
+ };
15442
+ "outstandingBalance": {
15443
+ "alias": "outstandingBalance";
15444
+ "required": false;
15445
+ "isSignal": true;
15446
+ };
15447
+ "currency": {
15448
+ "alias": "currency";
15449
+ "required": false;
15450
+ "isSignal": true;
15451
+ };
15452
+ "maxAmount": {
15453
+ "alias": "maxAmount";
15454
+ "required": false;
15455
+ "isSignal": true;
15456
+ };
15457
+ "maxDate": {
15458
+ "alias": "maxDate";
15459
+ "required": false;
15460
+ "isSignal": true;
15461
+ };
15462
+ "methods": {
15463
+ "alias": "methods";
15464
+ "required": false;
15465
+ "isSignal": true;
15466
+ };
15467
+ "savedMethods": {
15468
+ "alias": "savedMethods";
15469
+ "required": false;
15470
+ "isSignal": true;
15471
+ };
15472
+ "charges": {
15473
+ "alias": "charges";
15474
+ "required": false;
15475
+ "isSignal": true;
15476
+ };
15477
+ "allocationModes": {
15478
+ "alias": "allocationModes";
15479
+ "required": false;
15480
+ "isSignal": true;
15481
+ };
15482
+ "defaultAllocationMode": {
15483
+ "alias": "defaultAllocationMode";
15484
+ "required": false;
15485
+ "isSignal": true;
15486
+ };
15487
+ "receiptLabel": {
15488
+ "alias": "receiptLabel";
15489
+ "required": false;
15490
+ "isSignal": true;
15491
+ };
15492
+ "submitLabel": {
15493
+ "alias": "submitLabel";
15494
+ "required": false;
15495
+ "isSignal": true;
15496
+ };
15497
+ "splitSubmitLabel": {
15498
+ "alias": "splitSubmitLabel";
15499
+ "required": false;
15500
+ "isSignal": true;
15501
+ };
15502
+ "recordPayment": {
15503
+ "alias": "recordPayment";
15504
+ "required": false;
15505
+ "isSignal": true;
15506
+ };
15507
+ "listReaders": {
15508
+ "alias": "listReaders";
15509
+ "required": false;
15510
+ "isSignal": true;
15511
+ };
15512
+ "startTerminalPayment": {
15513
+ "alias": "startTerminalPayment";
15514
+ "required": false;
15515
+ "isSignal": true;
15516
+ };
15517
+ "terminalStatus": {
15518
+ "alias": "terminalStatus";
15519
+ "required": false;
15520
+ "isSignal": true;
15521
+ };
15522
+ "confirmTerminalPayment": {
15523
+ "alias": "confirmTerminalPayment";
15524
+ "required": false;
15525
+ "isSignal": true;
15526
+ };
15527
+ "cancelTerminal": {
15528
+ "alias": "cancelTerminal";
15529
+ "required": false;
15530
+ "isSignal": true;
15531
+ };
15532
+ "terminalProvider": {
15533
+ "alias": "terminalProvider";
15534
+ "required": false;
15535
+ "isSignal": true;
15536
+ };
15537
+ "noReadersText": {
15538
+ "alias": "noReadersText";
15539
+ "required": false;
15540
+ "isSignal": true;
15541
+ };
15542
+ "terminalPollMs": {
15543
+ "alias": "terminalPollMs";
15544
+ "required": false;
15545
+ "isSignal": true;
15546
+ };
15547
+ "terminalTimeoutMs": {
15548
+ "alias": "terminalTimeoutMs";
15549
+ "required": false;
15550
+ "isSignal": true;
15551
+ };
15552
+ "readerRefreshMs": {
15553
+ "alias": "readerRefreshMs";
15554
+ "required": false;
15555
+ "isSignal": true;
15556
+ };
15557
+ "terminalSuccessDelayMs": {
15558
+ "alias": "terminalSuccessDelayMs";
15559
+ "required": false;
15560
+ "isSignal": true;
15561
+ };
15562
+ }, {
15563
+ "closed": "closed";
15564
+ "recorded": "recorded";
15565
+ "outcome": "outcome";
15566
+ }, ["plugins"], never, true, never>;
15567
+ }
15568
+ export type { AllocationModeOption, CalendarAgendaGroup, CalendarDay, CalendarDayLevel, CalendarRange, CalendarViewMode, ChargeAllocation, HostResult, PayableCharge, PaymentAllocation, PaymentEntry, PaymentMethodOption, PaymentPanelOutcome, RecordPaymentFn, RecordPaymentPayload, RecordPaymentResult, SavedPaymentMethod, TerminalConfirmContext, TerminalPhase, TerminalReader, TerminalStartContext, TerminalStartResult, TerminalStatusResult };