@forgeroute/ui 0.4.20 → 0.4.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeroute/ui",
3
- "version": "0.4.20",
3
+ "version": "0.4.21",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=22.0.0",
6
6
  "@angular/core": ">=22.0.0",
@@ -480,6 +480,8 @@ export declare class PaginationComponent {
480
480
  readonly totalItems: import("@angular/core").InputSignal<number>;
481
481
  /** Standalone look (full border, full radius, small top margin) for use under separate cards rather than flush under a table. */
482
482
  readonly detached: import("@angular/core").InputSignal<boolean>;
483
+ /** Embedded look for a container that already has its own background and edge (a `ui-detail-card` footer, a panel): transparent, no border, no radius, no padding. Default false: unchanged. Wins over `detached`. */
484
+ readonly embedded: import("@angular/core").InputSignal<boolean>;
483
485
  readonly pageChange: import("@angular/core").OutputEmitterRef<number>;
484
486
  readonly totalPages: import("@angular/core").Signal<number>;
485
487
  readonly startItem: import("@angular/core").Signal<number>;
@@ -508,6 +510,11 @@ export declare class PaginationComponent {
508
510
  "required": false;
509
511
  "isSignal": true;
510
512
  };
513
+ "embedded": {
514
+ "alias": "embedded";
515
+ "required": false;
516
+ "isSignal": true;
517
+ };
511
518
  }, {
512
519
  "pageChange": "pageChange";
513
520
  }, never, never, true, never>;
@@ -959,6 +966,12 @@ export interface SelectOption<V extends string | number = string> {
959
966
  * Options without it are unchanged.
960
967
  */
961
968
  badge?: string;
969
+ /**
970
+ * Optional: list the choice but make it unselectable (native `<option disabled>`). Use it for an entry that must stay visible because it is the
971
+ * CURRENT value (e.g. an owner who is no longer active) but must not be picked. A disabled option that equals the current value is still shown as
972
+ * selected and still counts as a valid value; the user just cannot choose it again after picking another option. Options without it are unchanged.
973
+ */
974
+ disabled?: boolean;
962
975
  }
963
976
  export declare class SelectComponent implements ControlValueAccessor, Validator, UiFieldValidity {
964
977
  readonly label: import("@angular/core").InputSignal<string>;
@@ -1244,7 +1257,13 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1244
1257
  * tabs are all off, the header row is not rendered at all.
1245
1258
  */
1246
1259
  readonly showQuickTabs: import("@angular/core").InputSignal<boolean>;
1247
- /** False when the header row would be empty (no search box, no toolbar, no quick-filter tabs). */
1260
+ /**
1261
+ * Optional heading shown at the start of the header row, on the same row as the search box / tabs on the left and the
1262
+ * Export / primary buttons on the right (rendered as an `<h3>`, also while the table is loading and when it is empty).
1263
+ * Empty (default) renders nothing. Use it when the table is a section of a page and its title belongs with its toolbar.
1264
+ */
1265
+ readonly tableTitle: import("@angular/core").InputSignal<string>;
1266
+ /** False when the header row would be empty (no title, no search box, no toolbar, no quick-filter tabs). */
1248
1267
  protected readonly _showHeader: Signal<boolean>;
1249
1268
  /** Optional endpoint that returns Record<string,number> counts per quick-filter value. */
1250
1269
  readonly countsEndpoint: import("@angular/core").InputSignal<string>;
@@ -1777,6 +1796,11 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1777
1796
  "required": false;
1778
1797
  "isSignal": true;
1779
1798
  };
1799
+ "tableTitle": {
1800
+ "alias": "tableTitle";
1801
+ "required": false;
1802
+ "isSignal": true;
1803
+ };
1780
1804
  "countsEndpoint": {
1781
1805
  "alias": "countsEndpoint";
1782
1806
  "required": false;
@@ -6116,6 +6140,213 @@ export declare class ListItemComponent {
6116
6140
  "select": "select";
6117
6141
  }, never, ["[list-item-action], [li-action]"], true, never>;
6118
6142
  }
6143
+ /** Colour of the priority dot (`high` red, `medium` amber, `low` grey, `blue`, `green`); `none` = no dot. */
6144
+ export type CardListIndicator = 'high' | 'medium' | 'low' | 'blue' | 'green' | 'none';
6145
+ /** Icon of the round marker on the left. */
6146
+ export type CardListIcon = 'call' | 'email' | 'sms' | 'task' | 'doc' | 'money' | 'deal' | 'survey' | 'property' | 'search' | 'heart' | 'calc' | 'repeat';
6147
+ /**
6148
+ * Record row for the cards of a detail page (tasks, documents, deals, expenses, matches, surveys...). The layout is fixed;
6149
+ * pass inputs only:
6150
+ *
6151
+ * [dot | round icon] [title] [type badge] [status badge] [Edit] [View]
6152
+ * [description] [inline link]
6153
+ * [calendar icon + date]
6154
+ *
6155
+ * Each row is a bordered rounded box, 8px apart; put the rows straight in a `ui-detail-card` body (or in a `ui-list` template).
6156
+ *
6157
+ * <ui-card-list-item title="Call John" indicator="high" type="Meeting" status="Pending" statusVariant="gray"
6158
+ * date="Due Aug 21, 2026" description="Follow up about the offer"
6159
+ * [editButton]="true" (editClick)="edit(t)" [viewButton]="true" (viewClick)="view(t)" />
6160
+ *
6161
+ * Links: the library has no router, so a link is content you project and the app owns (`routerLink` etc.):
6162
+ * - `<a card-list-action class="cli-chip cli-chip--view" routerLink="/x">View</a>` at the row end (blue chip, like the View button);
6163
+ * - `<a card-list-inline class="cli-inline-link" routerLink="/x">View ...</a>` at the end of the description line.
6164
+ * Other projected content (default slot) shows after the chips at the row end; `[card-stats]` and `[card-detail]` add a footer row
6165
+ * under the row (use the `.cli-stat`, `.cli-stat-label`, `.cli-stat-value`, `.cli-stat-divider` classes inside `[card-stats]`).
6166
+ *
6167
+ * Accessibility: the Edit / View buttons are real buttons; give them a longer name with `editAriaLabel` / `viewAriaLabel`
6168
+ * ("Edit task Call John") when the visible word alone is ambiguous in a list. The priority dot (`high`/`medium`/`low`) has a tooltip and an
6169
+ * accessible name (`High priority`, ...), is focusable so the tooltip works by keyboard; `blue`/`green` dots are decorative. The icon circle is decorative.
6170
+ */
6171
+ export declare class CardListItemComponent {
6172
+ readonly title: import("@angular/core").InputSignal<string>;
6173
+ readonly description: import("@angular/core").InputSignal<string | undefined>;
6174
+ /** Date text with a small calendar icon, e.g. "Due Aug 21, 2026". Pre-formatted by the page. */
6175
+ readonly date: import("@angular/core").InputSignal<string | undefined>;
6176
+ /** Badge after the title; `typeVariant` colours it (default `gray`). */
6177
+ readonly type: import("@angular/core").InputSignal<string | undefined>;
6178
+ readonly typeVariant: import("@angular/core").InputSignal<BadgeVariant>;
6179
+ /** Second badge; shown only together with `statusVariant`. */
6180
+ readonly status: import("@angular/core").InputSignal<string | undefined>;
6181
+ readonly statusVariant: import("@angular/core").InputSignal<BadgeVariant | undefined>;
6182
+ /** Priority dot on the left (ignored when `icon` is set). */
6183
+ readonly indicator: import("@angular/core").InputSignal<CardListIndicator>;
6184
+ /** Round icon on the left (wins over `indicator`). */
6185
+ readonly icon: import("@angular/core").InputSignal<CardListIcon | undefined>;
6186
+ /** Built-in "Edit" chip button -> `editClick`. */
6187
+ readonly editButton: import("@angular/core").InputSignal<boolean>;
6188
+ /** Built-in "View" chip button -> `viewClick`. */
6189
+ readonly viewButton: import("@angular/core").InputSignal<boolean>;
6190
+ /** Accessible names for the buttons when the visible word is not enough ("Edit task Call John"). */
6191
+ readonly editAriaLabel: import("@angular/core").InputSignal<string>;
6192
+ readonly viewAriaLabel: import("@angular/core").InputSignal<string>;
6193
+ readonly editClick: import("@angular/core").OutputEmitterRef<void>;
6194
+ readonly viewClick: import("@angular/core").OutputEmitterRef<void>;
6195
+ protected readonly calendarPath = "M5 4h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z M16 2v4 M8 2v4 M3 10h18";
6196
+ protected readonly editPath = "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7 M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z";
6197
+ protected readonly iconPath: import("@angular/core").Signal<string | null>;
6198
+ protected readonly indicatorLabel: import("@angular/core").Signal<string>;
6199
+ static ɵfac: i0.ɵɵFactoryDeclaration<CardListItemComponent, never>;
6200
+ static ɵcmp: i0.ɵɵComponentDeclaration<CardListItemComponent, "ui-card-list-item", never, {
6201
+ "title": {
6202
+ "alias": "title";
6203
+ "required": false;
6204
+ "isSignal": true;
6205
+ };
6206
+ "description": {
6207
+ "alias": "description";
6208
+ "required": false;
6209
+ "isSignal": true;
6210
+ };
6211
+ "date": {
6212
+ "alias": "date";
6213
+ "required": false;
6214
+ "isSignal": true;
6215
+ };
6216
+ "type": {
6217
+ "alias": "type";
6218
+ "required": false;
6219
+ "isSignal": true;
6220
+ };
6221
+ "typeVariant": {
6222
+ "alias": "typeVariant";
6223
+ "required": false;
6224
+ "isSignal": true;
6225
+ };
6226
+ "status": {
6227
+ "alias": "status";
6228
+ "required": false;
6229
+ "isSignal": true;
6230
+ };
6231
+ "statusVariant": {
6232
+ "alias": "statusVariant";
6233
+ "required": false;
6234
+ "isSignal": true;
6235
+ };
6236
+ "indicator": {
6237
+ "alias": "indicator";
6238
+ "required": false;
6239
+ "isSignal": true;
6240
+ };
6241
+ "icon": {
6242
+ "alias": "icon";
6243
+ "required": false;
6244
+ "isSignal": true;
6245
+ };
6246
+ "editButton": {
6247
+ "alias": "editButton";
6248
+ "required": false;
6249
+ "isSignal": true;
6250
+ };
6251
+ "viewButton": {
6252
+ "alias": "viewButton";
6253
+ "required": false;
6254
+ "isSignal": true;
6255
+ };
6256
+ "editAriaLabel": {
6257
+ "alias": "editAriaLabel";
6258
+ "required": false;
6259
+ "isSignal": true;
6260
+ };
6261
+ "viewAriaLabel": {
6262
+ "alias": "viewAriaLabel";
6263
+ "required": false;
6264
+ "isSignal": true;
6265
+ };
6266
+ }, {
6267
+ "editClick": "editClick";
6268
+ "viewClick": "viewClick";
6269
+ }, never, ["[card-list-inline]", "[card-list-action]", "*", "[card-stats]", "[card-detail]"], true, never>;
6270
+ }
6271
+ /** A note as the notes API returns it. */
6272
+ export interface NoteItem {
6273
+ id: string;
6274
+ body: string;
6275
+ createdByName: string;
6276
+ createdAt: string;
6277
+ updatedAt: string;
6278
+ }
6279
+ /**
6280
+ * Notes for one record: a soft "Add a note" panel, then one clean card per note (author avatar, name, date, edit / delete icons, the text),
6281
+ * inline edit and a delete confirmation. Notes are listed 5 per page in the server's order. It talks to the notes API itself, so a page only names the record:
6282
+ *
6283
+ * <ui-notes resourceType="contact" [resourceId]="contactId" [readOnly]="!canEdit()" (totalChange)="notesTotal.set($event)" />
6284
+ *
6285
+ * API (under `FORGEROUTE_CONFIG.baseUrl`, default `/api`):
6286
+ * - `GET /notes/{resourceType}/{resourceId}?page=<0-based>&size=5` -> `{ content: NoteItem[], totalElements, totalPages, page, size }`
6287
+ * - `POST /notes/{resourceType}/{resourceId}` `{ body }` -> the new note
6288
+ * - `PUT /notes/{noteId}` `{ body }` -> the updated note
6289
+ * - `DELETE /notes/{noteId}`
6290
+ *
6291
+ * `readOnly` lists the notes only (no box, no edit / delete). `totalChange` emits the total after every load. Failures show a
6292
+ * library toast with the server's message. Put it in a `ui-detail-card`: the card's body padding is the only outer inset.
6293
+ * Pagination is the library `ui-pagination` (shown only above one page). Cmd/Ctrl+Enter in the add box adds the note.
6294
+ */
6295
+ export declare class NotesComponent {
6296
+ /** What the notes belong to, as the API names it: `contact`, `transaction`, `property`, `task`, ... */
6297
+ readonly resourceType: import("@angular/core").InputSignal<string>;
6298
+ readonly resourceId: import("@angular/core").InputSignal<string>;
6299
+ /** True: list only, no add box, edit or delete. */
6300
+ readonly readOnly: import("@angular/core").InputSignal<boolean>;
6301
+ /** Total number of notes after every load / add / delete. */
6302
+ readonly totalChange: import("@angular/core").OutputEmitterRef<number>;
6303
+ private readonly http;
6304
+ private readonly toast;
6305
+ private readonly destroyRef;
6306
+ private readonly apiBase;
6307
+ readonly pageSize = 5;
6308
+ readonly notes: import("@angular/core").WritableSignal<NoteItem[]>;
6309
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
6310
+ readonly saving: import("@angular/core").WritableSignal<boolean>;
6311
+ /** 0-based page index, as the server numbers it. */
6312
+ readonly page: import("@angular/core").WritableSignal<number>;
6313
+ readonly totalElements: import("@angular/core").WritableSignal<number>;
6314
+ readonly editingId: import("@angular/core").WritableSignal<string | null>;
6315
+ readonly deletingId: import("@angular/core").WritableSignal<string | null>;
6316
+ newBody: string;
6317
+ editBody: string;
6318
+ /** Up to two initials of the author's name for the avatar ("Ana Lopez" -> "AL"); "Y" for the current user's own unnamed notes. */
6319
+ protected initials(name: string): string;
6320
+ private get listUrl();
6321
+ constructor();
6322
+ loadPage(p: number): void;
6323
+ addNote(): void;
6324
+ startEdit(note: NoteItem): void;
6325
+ cancelEdit(): void;
6326
+ saveEdit(noteId: string): void;
6327
+ confirmDelete(id: string): void;
6328
+ deleteNote(): void;
6329
+ static ɵfac: i0.ɵɵFactoryDeclaration<NotesComponent, never>;
6330
+ static ɵcmp: i0.ɵɵComponentDeclaration<NotesComponent, "ui-notes", never, {
6331
+ "resourceType": {
6332
+ "alias": "resourceType";
6333
+ "required": false;
6334
+ "isSignal": true;
6335
+ };
6336
+ "resourceId": {
6337
+ "alias": "resourceId";
6338
+ "required": false;
6339
+ "isSignal": true;
6340
+ };
6341
+ "readOnly": {
6342
+ "alias": "readOnly";
6343
+ "required": false;
6344
+ "isSignal": true;
6345
+ };
6346
+ }, {
6347
+ "totalChange": "totalChange";
6348
+ }, never, never, true, never>;
6349
+ }
6119
6350
  /**
6120
6351
  * Adds one app-wide toast outlet (a `<ui-toast>` appended to `<body>`) when the app starts, so toasts show on pages that have
6121
6352
  * no `<ui-data-table>` and no `<ui-toast />` of their own.
@@ -6550,7 +6781,7 @@ export interface PanelInfoItem {
6550
6781
  phone?: string;
6551
6782
  /** Show the value with an "open" icon instead of blue link styling. */
6552
6783
  linkIcon?: boolean;
6553
- /** Renders a small edit (pencil) icon button after the value. */
6784
+ /** Renders a small edit (pencil) `ui-icon` button after the value; `editTooltip` is its tooltip and accessible name (default "Edit"). */
6554
6785
  editAction?: () => void;
6555
6786
  editTooltip?: string;
6556
6787
  muted?: boolean;
@@ -7007,7 +7238,7 @@ export declare class AlertComponent {
7007
7238
  /** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
7008
7239
  export declare function iconPath(name: string): string | undefined;
7009
7240
  /** Keys with an icon of their own (the data-table action keys are accepted too — see `builtInActionIcon`). */
7010
- export type IconName = 'add' | 'close' | 'check' | 'upload' | 'image' | 'chevron-up' | 'chevron-down' | 'arrow-left' | 'bell' | 'print' | 'lock' | 'shield' | 'arrow-up' | 'arrow-down' | 'arrow-right' | 'document' | 'grip' | 'card' | 'bank' | 'view' | 'view-off' | 'edit' | 'delete' | 'download' | 'duplicate' | 'archive' | 'restore' | 'share' | 'email' | 'message' | 'call' | 'refund' | 'chevron-right';
7241
+ export type IconName = 'add' | 'close' | 'check' | 'upload' | 'image' | 'chevron-up' | 'chevron-down' | 'arrow-left' | 'bell' | 'print' | 'lock' | 'shield' | 'arrow-up' | 'arrow-down' | 'arrow-right' | 'document' | 'grip' | 'card' | 'bank' | 'view' | 'view-off' | 'edit' | 'delete' | 'download' | 'duplicate' | 'archive' | 'restore' | 'share' | 'email' | 'message' | 'call' | 'refund' | 'chevron-right' | 'link' | 'save' | 'chart';
7011
7242
  /**
7012
7243
  * A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
7013
7244
  * `iconClick`, and the host does the actual work (delete the row, open a panel, …).
@@ -8535,6 +8766,8 @@ export declare class PdfHotspotComponent {
8535
8766
  * - `background`: the card's background colour (any CSS colour; default white).
8536
8767
  * - `headerBackground`: the background colour of the header bar only (any CSS colour; default none, the card's own background shows). The body keeps the card's `background`, and a header-less card (`showHeader` false) has no bar to colour. A collapsible header keeps this colour on hover. Pair it with `textColor` when the colour is dark.
8537
8768
  * - `textColor`: the colour of the title, subtitle, chevron and body text (any CSS colour; default: the normal slate colours). Use it for light text on a dark `background`. Text you colour yourself inside the body keeps its own colour.
8769
+ * - `<ng-container card-footer>` / `<div card-footer>`: a footer bar under the body (a pager, totals, a link). Hidden while empty or collapsed, 10px 18px padding,
8770
+ * a light top line; `footerBackground` colours it (pair it with `headerBackground` to frame the card). In a `fill` card it is pinned to the bottom edge.
8538
8771
  * - The body has built-in padding (16px 18px, matching the header's 18px side padding); there is no padding input.
8539
8772
  * - `collapsible` (default true): a chevron toggle and a clickable header; `[(expanded)]` is two-way.
8540
8773
  * Without a header, or with `collapsible` false, the body is always open.
@@ -8577,6 +8810,8 @@ export declare class DetailCardComponent {
8577
8810
  readonly background: import("@angular/core").InputSignal<string>;
8578
8811
  /** Background colour of the header bar only, as any CSS colour (`#eaf3ff`, `var(--surface-2)` …). Empty = the header has no colour of its own (the card background shows). The body is not affected. */
8579
8812
  readonly headerBackground: import("@angular/core").InputSignal<string>;
8813
+ /** Background colour of the footer bar (the `[card-footer]` slot) as any CSS colour. Empty = no colour of its own. */
8814
+ readonly footerBackground: import("@angular/core").InputSignal<string>;
8580
8815
  /** Text colour as any CSS colour (`#fff`, `var(--on-dark)` …). Empty = the default colours. Colours the title, subtitle, chevron and body text. */
8581
8816
  readonly textColor: import("@angular/core").InputSignal<string>;
8582
8817
  /** false = no header at all (no title, edit button, toggle or divider): the card is only the body, always open. */
@@ -8656,6 +8891,11 @@ export declare class DetailCardComponent {
8656
8891
  "required": false;
8657
8892
  "isSignal": true;
8658
8893
  };
8894
+ "footerBackground": {
8895
+ "alias": "footerBackground";
8896
+ "required": false;
8897
+ "isSignal": true;
8898
+ };
8659
8899
  "textColor": {
8660
8900
  "alias": "textColor";
8661
8901
  "required": false;
@@ -8730,7 +8970,7 @@ export declare class DetailCardComponent {
8730
8970
  "expanded": "expandedChange";
8731
8971
  "editClick": "editClick";
8732
8972
  "selected": "selectedChange";
8733
- }, never, ["[card-header-leading]", "[card-header]", "[card-header-action]", "*"], true, never>;
8973
+ }, never, ["[card-header-leading]", "[card-header]", "[card-header-action]", "*", "[card-footer]"], true, never>;
8734
8974
  }
8735
8975
  export type DividerSpace = 'none' | 'sm' | 'md' | 'lg';
8736
8976
  /**