@forgeroute/ui 0.4.21 → 0.4.23
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/fesm2022/forgeroute-ui.mjs +1001 -143
- package/fesm2022/forgeroute-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/types/forgeroute-ui.d.ts +337 -12
package/package.json
CHANGED
package/types/forgeroute-ui.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface ToolbarAction {
|
|
|
47
47
|
/** Hide this action entirely */
|
|
48
48
|
hidden?: boolean;
|
|
49
49
|
/** Documentation of intent: marks a mutating action ("Add …", bulk mutations). It does not change behavior - the table hides
|
|
50
|
-
* EVERY page-defined toolbar action for any `permission` other than 'full',
|
|
50
|
+
* EVERY page-defined toolbar action for any `permission` other than 'full', unless it is marked `allowReadOnly`. */
|
|
51
51
|
requiresFull?: boolean;
|
|
52
52
|
/**
|
|
53
53
|
* Keep this action when the table is empty and unfiltered. By default only `variant: 'primary'` buttons survive that
|
|
@@ -56,6 +56,13 @@ export interface ToolbarAction {
|
|
|
56
56
|
* menu back (with its usual items).
|
|
57
57
|
*/
|
|
58
58
|
keepWhenEmpty?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Keep this action for a read-only table (`permission` other than 'full'). By default a read-only table drops every
|
|
61
|
+
* page-defined toolbar action and keeps only Export; set this on a PURE READ - e.g. a "Board View" / "List View" switch
|
|
62
|
+
* that changes what is shown but writes nothing. Never set it on an action that creates, changes or deletes a record.
|
|
63
|
+
* On an empty, unfiltered table only primary buttons survive: add `keepWhenEmpty` as well to keep a view switch there.
|
|
64
|
+
*/
|
|
65
|
+
allowReadOnly?: boolean;
|
|
59
66
|
/**
|
|
60
67
|
* Standalone toggle buttons (e.g. "Assigned to Me"): true / false renders `aria-pressed` so screen readers announce
|
|
61
68
|
* the on / off state. Leave unset for ordinary buttons (no attribute).
|
|
@@ -660,12 +667,45 @@ export declare class BadgeComponent {
|
|
|
660
667
|
};
|
|
661
668
|
}, {}, never, never, true, never>;
|
|
662
669
|
}
|
|
670
|
+
/**
|
|
671
|
+
* Round avatar: initials on a colour derived from them (default), or a photo when `imageUrl` is set. The photo falls back to the
|
|
672
|
+
* initials by itself when it fails to load (and tries again when `imageUrl` changes).
|
|
673
|
+
*
|
|
674
|
+
* <ui-avatar initials="JS" name="Jane Smith" [size]="72" [imageUrl]="photoUrl()" />
|
|
675
|
+
*
|
|
676
|
+
* Editable (a profile picture the user can change): `[editable]="true"` makes the avatar a keyboard button (role="button",
|
|
677
|
+
* tabindex 0, Enter / Space / click) that emits `editClick` — the host opens its file picker and uploads — with a small camera
|
|
678
|
+
* badge in the corner. `editLabel` is its name for assistive tech and tooltip (default "Change photo"). `[busy]="true"` while the
|
|
679
|
+
* upload runs: spinner in the badge, the photo is dimmed, `aria-busy` is set and clicks / keys do nothing.
|
|
680
|
+
*
|
|
681
|
+
* <ui-avatar initials="JS" name="Jane Smith" [size]="72" [imageUrl]="photoUrl()" [editable]="canEdit()" [busy]="uploading()" (editClick)="picker.open()" />
|
|
682
|
+
*
|
|
683
|
+
* Without `imageUrl` and `editable` nothing changes from the plain initials avatar.
|
|
684
|
+
*/
|
|
663
685
|
export declare class AvatarComponent {
|
|
664
686
|
readonly initials: import("@angular/core").InputSignal<string>;
|
|
665
687
|
readonly name: import("@angular/core").InputSignal<string>;
|
|
666
688
|
readonly size: import("@angular/core").InputSignal<number>;
|
|
689
|
+
/** Photo URL. Empty / null = initials. A photo that fails to load falls back to the initials until the URL changes. */
|
|
690
|
+
readonly imageUrl: import("@angular/core").InputSignal<string | null | undefined>;
|
|
691
|
+
/** true = the avatar is a button that emits `editClick` (change the photo), with a camera badge. Default false: unchanged. */
|
|
692
|
+
readonly editable: import("@angular/core").InputSignal<boolean>;
|
|
693
|
+
/** Name of the editable avatar for assistive tech and the tooltip. */
|
|
694
|
+
readonly editLabel: import("@angular/core").InputSignal<string>;
|
|
695
|
+
/** The host is uploading: spinner badge, dimmed photo, aria-busy, no clicks. */
|
|
696
|
+
readonly busy: import("@angular/core").InputSignal<boolean>;
|
|
697
|
+
/** Fires on click / Enter / Space of an editable avatar (not while busy). */
|
|
698
|
+
readonly editClick: import("@angular/core").OutputEmitterRef<void>;
|
|
699
|
+
/** Fires when the photo could not be loaded (the initials are shown instead). */
|
|
700
|
+
readonly imageError: import("@angular/core").OutputEmitterRef<void>;
|
|
701
|
+
private readonly failedUrl;
|
|
702
|
+
protected readonly showImage: import("@angular/core").Signal<boolean>;
|
|
703
|
+
protected readonly badgeSize: import("@angular/core").Signal<number>;
|
|
667
704
|
protected readonly bg: import("@angular/core").Signal<string>;
|
|
668
705
|
protected readonly color: import("@angular/core").Signal<string>;
|
|
706
|
+
protected onImageError(): void;
|
|
707
|
+
protected onEdit(): void;
|
|
708
|
+
protected onKey(event: Event): void;
|
|
669
709
|
static ɵfac: i0.ɵɵFactoryDeclaration<AvatarComponent, never>;
|
|
670
710
|
static ɵcmp: i0.ɵɵComponentDeclaration<AvatarComponent, "ui-avatar", never, {
|
|
671
711
|
"initials": {
|
|
@@ -683,7 +723,30 @@ export declare class AvatarComponent {
|
|
|
683
723
|
"required": false;
|
|
684
724
|
"isSignal": true;
|
|
685
725
|
};
|
|
686
|
-
|
|
726
|
+
"imageUrl": {
|
|
727
|
+
"alias": "imageUrl";
|
|
728
|
+
"required": false;
|
|
729
|
+
"isSignal": true;
|
|
730
|
+
};
|
|
731
|
+
"editable": {
|
|
732
|
+
"alias": "editable";
|
|
733
|
+
"required": false;
|
|
734
|
+
"isSignal": true;
|
|
735
|
+
};
|
|
736
|
+
"editLabel": {
|
|
737
|
+
"alias": "editLabel";
|
|
738
|
+
"required": false;
|
|
739
|
+
"isSignal": true;
|
|
740
|
+
};
|
|
741
|
+
"busy": {
|
|
742
|
+
"alias": "busy";
|
|
743
|
+
"required": false;
|
|
744
|
+
"isSignal": true;
|
|
745
|
+
};
|
|
746
|
+
}, {
|
|
747
|
+
"editClick": "editClick";
|
|
748
|
+
"imageError": "imageError";
|
|
749
|
+
}, never, never, true, never>;
|
|
687
750
|
}
|
|
688
751
|
export interface MenuItem {
|
|
689
752
|
type: string;
|
|
@@ -780,7 +843,7 @@ export declare class TableToolbarComponent {
|
|
|
780
843
|
readonly activeViewId: import("@angular/core").InputSignal<string | null>;
|
|
781
844
|
readonly showSavedViews: import("@angular/core").InputSignal<boolean>;
|
|
782
845
|
/**
|
|
783
|
-
* Read-only (permission isn't 'full'): only Export
|
|
846
|
+
* Read-only (permission isn't 'full'): only Export and pure reads marked `allowReadOnly` stay — no other page buttons, no Import, no Columns, no
|
|
784
847
|
* Save Current View, no deleting views. More Actions offers only the user's existing Saved Views and is
|
|
785
848
|
* hidden when they have none.
|
|
786
849
|
*/
|
|
@@ -800,7 +863,7 @@ export declare class TableToolbarComponent {
|
|
|
800
863
|
protected _saveViewName: string;
|
|
801
864
|
/** Draft column visibility — only applied to the table when the user clicks Apply. */
|
|
802
865
|
private readonly _pendingCols;
|
|
803
|
-
/** Buttons this user may see — read-only users only get Export (page actions and Import are hidden). */
|
|
866
|
+
/** Buttons this user may see — read-only users only get Export and pure reads marked `allowReadOnly` (page actions and Import are hidden). */
|
|
804
867
|
private readonly _allowedButtons;
|
|
805
868
|
protected readonly standaloneButtons: import("@angular/core").Signal<ToolbarAction[]>;
|
|
806
869
|
protected readonly importButton: import("@angular/core").Signal<ToolbarAction | null>;
|
|
@@ -1286,7 +1349,7 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
|
|
|
1286
1349
|
/**
|
|
1287
1350
|
* Page permission. Anything other than 'full' makes the table read-only: no Import,
|
|
1288
1351
|
* no bulk update/delete and no checkbox column (even when `selectable` is set), no page-defined
|
|
1289
|
-
* toolbar buttons
|
|
1352
|
+
* toolbar buttons except Export and pure reads marked `allowReadOnly` (More Actions offers only their existing Saved Views), and the
|
|
1290
1353
|
* row menu offers only View (`type: 'view'`).
|
|
1291
1354
|
* Rows with nothing to show get no "⋮" trigger; the Actions column is dropped when no row has View.
|
|
1292
1355
|
*/
|
|
@@ -4215,6 +4278,11 @@ export declare class ConfirmOutletComponent implements OnInit, OnDestroy {
|
|
|
4215
4278
|
static ɵfac: i0.ɵɵFactoryDeclaration<ConfirmOutletComponent, never>;
|
|
4216
4279
|
static ɵcmp: i0.ɵɵComponentDeclaration<ConfirmOutletComponent, "ui-confirm-outlet", never, {}, {}, never, never, true, never>;
|
|
4217
4280
|
}
|
|
4281
|
+
/**
|
|
4282
|
+
* Centered empty state: icon in a round tile, optional bold title, message and a projected action.
|
|
4283
|
+
* `iconType`: search, table, chart, users, person, message, email, task, house, money, star, video, shield, pipeline (funnel),
|
|
4284
|
+
* property (building), clipboard (clipboard with check and lines), document (default; any unknown value also draws it).
|
|
4285
|
+
*/
|
|
4218
4286
|
export declare class EmptyStateComponent {
|
|
4219
4287
|
readonly message: import("@angular/core").InputSignal<string>;
|
|
4220
4288
|
readonly title: import("@angular/core").InputSignal<string>;
|
|
@@ -6276,6 +6344,11 @@ export interface NoteItem {
|
|
|
6276
6344
|
createdAt: string;
|
|
6277
6345
|
updatedAt: string;
|
|
6278
6346
|
}
|
|
6347
|
+
/**
|
|
6348
|
+
* Longest note body, in characters, counted AFTER trimming. Matches the backend `NoteController` cap (10000 after trim; the server trims first,
|
|
6349
|
+
* so leading / trailing spaces never count). The add box and the edit box block Add Note / Save above it and show "Too long (max 10000 characters).".
|
|
6350
|
+
*/
|
|
6351
|
+
export declare const NOTES_MAX_LENGTH = 10000;
|
|
6279
6352
|
/**
|
|
6280
6353
|
* 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
6354
|
* 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:
|
|
@@ -6290,6 +6363,8 @@ export interface NoteItem {
|
|
|
6290
6363
|
*
|
|
6291
6364
|
* `readOnly` lists the notes only (no box, no edit / delete). `totalChange` emits the total after every load. Failures show a
|
|
6292
6365
|
* library toast with the server's message. Put it in a `ui-detail-card`: the card's body padding is the only outer inset.
|
|
6366
|
+
* A note may hold at most `NOTES_MAX_LENGTH` (10000) characters after trimming; longer text (typed or pasted) shows the field's
|
|
6367
|
+
* "Too long" message at once and Add Note / Save stay disabled, so no request is sent.
|
|
6293
6368
|
* Pagination is the library `ui-pagination` (shown only above one page). Cmd/Ctrl+Enter in the add box adds the note.
|
|
6294
6369
|
*/
|
|
6295
6370
|
export declare class NotesComponent {
|
|
@@ -6305,6 +6380,8 @@ export declare class NotesComponent {
|
|
|
6305
6380
|
private readonly destroyRef;
|
|
6306
6381
|
private readonly apiBase;
|
|
6307
6382
|
readonly pageSize = 5;
|
|
6383
|
+
/** The character limit of a note (see `NOTES_MAX_LENGTH`). */
|
|
6384
|
+
readonly maxLength = 10000;
|
|
6308
6385
|
readonly notes: import("@angular/core").WritableSignal<NoteItem[]>;
|
|
6309
6386
|
readonly loading: import("@angular/core").WritableSignal<boolean>;
|
|
6310
6387
|
readonly saving: import("@angular/core").WritableSignal<boolean>;
|
|
@@ -6317,6 +6394,10 @@ export declare class NotesComponent {
|
|
|
6317
6394
|
editBody: string;
|
|
6318
6395
|
/** 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
6396
|
protected initials(name: string): string;
|
|
6397
|
+
/** True when the trimmed text is longer than the limit. */
|
|
6398
|
+
protected tooLong(text: string): boolean;
|
|
6399
|
+
/** True when the trimmed text is not empty and within the limit. */
|
|
6400
|
+
protected validBody(text: string): boolean;
|
|
6320
6401
|
private get listUrl();
|
|
6321
6402
|
constructor();
|
|
6322
6403
|
loadPage(p: number): void;
|
|
@@ -6821,6 +6902,11 @@ export declare class PanelInfoListComponent {
|
|
|
6821
6902
|
* Default false = the label and value as before.
|
|
6822
6903
|
*/
|
|
6823
6904
|
readonly labelColon: import("@angular/core").InputSignal<boolean>;
|
|
6905
|
+
/**
|
|
6906
|
+
* The list stretches to the height of its container (a grid cell or flex item with a definite height), so lists side by side in
|
|
6907
|
+
* one grid row end at the same height; with one section, that section's box fills the height. Default false = as tall as its content.
|
|
6908
|
+
*/
|
|
6909
|
+
readonly fill: import("@angular/core").InputSignal<boolean>;
|
|
6824
6910
|
readonly displaySections: import("@angular/core").Signal<PanelInfoSection[]>;
|
|
6825
6911
|
/**
|
|
6826
6912
|
* The safe href of an item, or null: `href` first, else `mailto:`/`tel:` built from `email`/`phone`. Only mailto, tel, http and https
|
|
@@ -6848,6 +6934,11 @@ export declare class PanelInfoListComponent {
|
|
|
6848
6934
|
"required": false;
|
|
6849
6935
|
"isSignal": true;
|
|
6850
6936
|
};
|
|
6937
|
+
"fill": {
|
|
6938
|
+
"alias": "fill";
|
|
6939
|
+
"required": false;
|
|
6940
|
+
"isSignal": true;
|
|
6941
|
+
};
|
|
6851
6942
|
}, {}, never, never, true, never>;
|
|
6852
6943
|
}
|
|
6853
6944
|
/**
|
|
@@ -7238,7 +7329,7 @@ export declare class AlertComponent {
|
|
|
7238
7329
|
/** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
|
|
7239
7330
|
export declare function iconPath(name: string): string | undefined;
|
|
7240
7331
|
/** Keys with an icon of their own (the data-table action keys are accepted too — see `builtInActionIcon`). */
|
|
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';
|
|
7332
|
+
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' | 'clipboard';
|
|
7242
7333
|
/**
|
|
7243
7334
|
* A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
|
|
7244
7335
|
* `iconClick`, and the host does the actual work (delete the row, open a panel, …).
|
|
@@ -7363,6 +7454,8 @@ export interface TimelineItem {
|
|
|
7363
7454
|
* { id: 'b', title: 'Letter sent', description: 'Mailed to the owner.', tone: 'success', icon: 'email' },
|
|
7364
7455
|
* ]" />
|
|
7365
7456
|
*
|
|
7457
|
+
* `groupByDay` adds a heading per calendar day (see the inputs `groupByDay`, `timeZone`, `now`, `dayHeadingLevel`).
|
|
7458
|
+
*
|
|
7366
7459
|
* Semantics: `<ol>` / `<li>`; the line and dots are decorative (aria-hidden). `time` is the visible text; `dateTime` goes into
|
|
7367
7460
|
* `<time datetime>`. With no items, `emptyMessage` (if set) is shown as a hint, otherwise nothing is rendered.
|
|
7368
7461
|
*/
|
|
@@ -7376,6 +7469,26 @@ export declare class TimelineComponent {
|
|
|
7376
7469
|
readonly maxHeight: import("@angular/core").InputSignal<string>;
|
|
7377
7470
|
/** `below` (default): the time is under the title / description. `end`: the time sits right-aligned on the title row. */
|
|
7378
7471
|
readonly timePosition: import("@angular/core").InputSignal<"end" | "below">;
|
|
7472
|
+
/**
|
|
7473
|
+
* Group the items under a heading per calendar day of `dateTime` ("Today", "Yesterday", "Oct 3", "Oct 3, 2025"). Default false = no headings,
|
|
7474
|
+
* 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.
|
|
7475
|
+
* An item without a parseable ISO `dateTime` joins the group it follows (a heading-less group at the top when first).
|
|
7476
|
+
* Grouped markup: `<ol class="tl"><li class="tl-day-group"><div class="tl-day" role="heading" aria-level>…</div><ol class="tl-sub">…items…</ol></li></ol>`.
|
|
7477
|
+
*/
|
|
7478
|
+
readonly groupByDay: import("@angular/core").InputSignal<boolean>;
|
|
7479
|
+
/** IANA time zone (e.g. `America/Chicago`) for the day grouping and the labels. A `dateTime` with a trailing Z / offset = an instant shown in this zone; a date or a wall-clock time without offset is taken as already being in that day. Empty (default) = the browser's local zone; an unknown zone also falls back to it. */
|
|
7480
|
+
readonly timeZone: import("@angular/core").InputSignal<string>;
|
|
7481
|
+
/** The moment "Today" / "Yesterday" are measured from. `null` (default) = the real current time; a Date or ISO string pins it (tests, replays). The real time is read when the groups are computed, not on a timer: a long-lived page should pass `now` (or re-bind `items`) to refresh Today / Yesterday after midnight. */
|
|
7482
|
+
readonly now: import("@angular/core").InputSignal<string | Date | null>;
|
|
7483
|
+
/** Heading level 1-6 of the day headings (role="heading" + aria-level); anything else becomes 3. */
|
|
7484
|
+
readonly dayHeadingLevel: import("@angular/core").InputSignal<number>;
|
|
7485
|
+
protected readonly headingLevel: import("@angular/core").Signal<number>;
|
|
7486
|
+
/** The items split into day groups (only used when `groupByDay` is on). */
|
|
7487
|
+
protected readonly groups: import("@angular/core").Signal<{
|
|
7488
|
+
key: string;
|
|
7489
|
+
label: string;
|
|
7490
|
+
items: TimelineItem[];
|
|
7491
|
+
}[]>;
|
|
7379
7492
|
/** Solid dot colour of an item's `dotVariant`, or null (then `tone` colours it). */
|
|
7380
7493
|
dotColor(item: TimelineItem): string | null;
|
|
7381
7494
|
static ɵfac: i0.ɵɵFactoryDeclaration<TimelineComponent, never>;
|
|
@@ -7405,6 +7518,26 @@ export declare class TimelineComponent {
|
|
|
7405
7518
|
"required": false;
|
|
7406
7519
|
"isSignal": true;
|
|
7407
7520
|
};
|
|
7521
|
+
"groupByDay": {
|
|
7522
|
+
"alias": "groupByDay";
|
|
7523
|
+
"required": false;
|
|
7524
|
+
"isSignal": true;
|
|
7525
|
+
};
|
|
7526
|
+
"timeZone": {
|
|
7527
|
+
"alias": "timeZone";
|
|
7528
|
+
"required": false;
|
|
7529
|
+
"isSignal": true;
|
|
7530
|
+
};
|
|
7531
|
+
"now": {
|
|
7532
|
+
"alias": "now";
|
|
7533
|
+
"required": false;
|
|
7534
|
+
"isSignal": true;
|
|
7535
|
+
};
|
|
7536
|
+
"dayHeadingLevel": {
|
|
7537
|
+
"alias": "dayHeadingLevel";
|
|
7538
|
+
"required": false;
|
|
7539
|
+
"isSignal": true;
|
|
7540
|
+
};
|
|
7408
7541
|
}, {}, never, never, true, never>;
|
|
7409
7542
|
}
|
|
7410
7543
|
/**
|
|
@@ -9052,12 +9185,24 @@ export interface BoardCardContext<T = unknown> {
|
|
|
9052
9185
|
$implicit: T;
|
|
9053
9186
|
}>;
|
|
9054
9187
|
}
|
|
9055
|
-
/**
|
|
9188
|
+
/**
|
|
9189
|
+
* Marks the template that renders the INSIDE of a card (the board draws the card frame, menu and drag handling).
|
|
9190
|
+
* Bind the board's columns to type the card item: `<ng-template [uiBoardCard]="columns()" let-lead>` gives `lead` the
|
|
9191
|
+
* columns' item type. The bare `<ng-template uiBoardCard let-item>` still works; `item` is then `unknown`.
|
|
9192
|
+
*/
|
|
9056
9193
|
export declare class BoardCardDirective<T = unknown> {
|
|
9194
|
+
/** Only used to infer the item type in the template; the value is never read. */
|
|
9195
|
+
readonly typeFrom: import("@angular/core").InputSignal<"" | BoardColumn<T>[]>;
|
|
9057
9196
|
readonly template: TemplateRef<BoardCardContext<T>>;
|
|
9058
9197
|
static ngTemplateContextGuard<T>(_dir: BoardCardDirective<T>, _ctx: unknown): _ctx is BoardCardContext<T>;
|
|
9059
9198
|
static ɵfac: i0.ɵɵFactoryDeclaration<BoardCardDirective<any>, never>;
|
|
9060
|
-
static ɵdir: i0.ɵɵDirectiveDeclaration<BoardCardDirective<any>, "ng-template[uiBoardCard]", never, {
|
|
9199
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<BoardCardDirective<any>, "ng-template[uiBoardCard]", never, {
|
|
9200
|
+
"typeFrom": {
|
|
9201
|
+
"alias": "uiBoardCard";
|
|
9202
|
+
"required": false;
|
|
9203
|
+
"isSignal": true;
|
|
9204
|
+
};
|
|
9205
|
+
}, {}, never, never, true, never>;
|
|
9061
9206
|
}
|
|
9062
9207
|
/**
|
|
9063
9208
|
* Kanban board: columns of cards that can be dragged between columns. Data and saving stay with the host.
|
|
@@ -9065,7 +9210,7 @@ export declare class BoardCardDirective<T = unknown> {
|
|
|
9065
9210
|
* <ui-board [columns]="columns()" [itemKey]="key" [itemLabel]="label" [cardActions]="actions" [readOnly]="perm() !== 'full'"
|
|
9066
9211
|
* [loading]="loading()" [error]="error()" (retry)="load()"
|
|
9067
9212
|
* (cardClick)="open($event)" (cardAction)="onAction($event)" (itemMove)="onMove($event)">
|
|
9068
|
-
* <ng-template uiBoardCard let-deal let-menu="menu">
|
|
9213
|
+
* <ng-template [uiBoardCard]="columns()" let-deal let-menu="menu">
|
|
9069
9214
|
* <div class="ub-card__top"><span class="ub-card__title">{{ deal.title }}</span>
|
|
9070
9215
|
* <ng-container [ngTemplateOutlet]="menu" [ngTemplateOutletContext]="{ $implicit: deal }" /></div>
|
|
9071
9216
|
* <div class="ub-card__contact">{{ deal.contactName }}</div>
|
|
@@ -9079,12 +9224,18 @@ export declare class BoardCardDirective<T = unknown> {
|
|
|
9079
9224
|
export declare class BoardComponent<T = unknown> {
|
|
9080
9225
|
/** The columns in display order, each with its items. A new value replaces the board's content (and any on-screen move). */
|
|
9081
9226
|
readonly columns: import("@angular/core").InputSignal<BoardColumn<T>[]>;
|
|
9082
|
-
/** Stable unique id of a card (used to track cards between renders). */
|
|
9227
|
+
/** Stable unique id of a card (used to track cards between renders, to find its menu and to move it). Must be unique across the whole board. */
|
|
9083
9228
|
readonly itemKey: import("@angular/core").InputSignal<(item: T) => string>;
|
|
9084
9229
|
/** Spoken name of a card: title, who, status, value... The card's visible content is the `uiBoardCard` template. */
|
|
9085
9230
|
readonly itemLabel: import("@angular/core").InputSignal<(item: T) => string>;
|
|
9086
9231
|
/** Menu on every card (the "⋮" button). Empty = no menu. Handle the pick in `(cardAction)`. */
|
|
9087
9232
|
readonly cardActions: import("@angular/core").InputSignal<RowAction[]>;
|
|
9233
|
+
/**
|
|
9234
|
+
* Adds "Move to <column>" to every card's menu (one item per column the card may be moved to), so a card can be moved
|
|
9235
|
+
* without dragging - by touch, with a screen reader or with the mouse alone. Only while cards can be moved (not read-only,
|
|
9236
|
+
* not `moveDisabled`) and only to columns that accept the card (`canMove`, `dropDisabled`). Set false to leave the menu to `cardActions`.
|
|
9237
|
+
*/
|
|
9238
|
+
readonly moveMenu: import("@angular/core").InputSignal<boolean>;
|
|
9088
9239
|
readonly loading: import("@angular/core").InputSignal<boolean>;
|
|
9089
9240
|
/** Error text: replaces the board with the message and a Retry button. */
|
|
9090
9241
|
readonly error: import("@angular/core").InputSignal<string | null>;
|
|
@@ -9101,21 +9252,42 @@ export declare class BoardComponent<T = unknown> {
|
|
|
9101
9252
|
readonly itemNoun: import("@angular/core").InputSignal<string>;
|
|
9102
9253
|
readonly itemNounPlural: import("@angular/core").InputSignal<string>;
|
|
9103
9254
|
readonly retryLabel: import("@angular/core").InputSignal<string>;
|
|
9255
|
+
/**
|
|
9256
|
+
* Cards drawn per column at first, and added by each "Show more" click (0 = draw every card). The board loads every item
|
|
9257
|
+
* at once, so drawing only a page of each column keeps the first paint fast however many items there are. Column counts
|
|
9258
|
+
* and summaries still cover every item; a card moved into a column is drawn at its top, so it is always on screen.
|
|
9259
|
+
*/
|
|
9260
|
+
readonly pageSize: import("@angular/core").InputSignal<number>;
|
|
9104
9261
|
readonly cardClick: import("@angular/core").OutputEmitterRef<T>;
|
|
9105
9262
|
readonly cardAction: import("@angular/core").OutputEmitterRef<BoardCardActionEvent<T>>;
|
|
9106
9263
|
readonly itemMove: import("@angular/core").OutputEmitterRef<BoardMoveEvent<T>>;
|
|
9107
9264
|
readonly retry: import("@angular/core").OutputEmitterRef<void>;
|
|
9108
|
-
protected readonly card: import("@angular/core").Signal<BoardCardDirective<
|
|
9265
|
+
protected readonly card: import("@angular/core").Signal<BoardCardDirective<T> | undefined>;
|
|
9109
9266
|
/** The columns as shown: the host's value, plus any move not yet replaced by new data. */
|
|
9110
9267
|
protected readonly cols: import("@angular/core").WritableSignal<BoardColumn<T>[]>;
|
|
9268
|
+
/** Cards drawn per column. Kept when the host re-sends columns (a saved move, a search), so the board never jumps back; back to the first page on a reload (`loading`). */
|
|
9269
|
+
private readonly drawn;
|
|
9270
|
+
/** The card the user last moved and where it went: it stays drawn even if the host re-sends it far down the column. */
|
|
9271
|
+
private readonly movedTo;
|
|
9272
|
+
protected readonly shownCols: import("@angular/core").Signal<{
|
|
9273
|
+
col: BoardColumn<T>;
|
|
9274
|
+
shown: T[];
|
|
9275
|
+
hidden: number;
|
|
9276
|
+
next: number;
|
|
9277
|
+
}[]>;
|
|
9278
|
+
/** How many cards of `col` are drawn: the pages shown so far, extended so the card the user moved is always on screen. */
|
|
9279
|
+
private limitOf;
|
|
9280
|
+
protected showMore(key: string): void;
|
|
9281
|
+
/** Id of the keyboard-move hint that describes the board region. */
|
|
9282
|
+
protected readonly hintId: string;
|
|
9111
9283
|
protected readonly overKey: import("@angular/core").WritableSignal<string | null>;
|
|
9112
9284
|
protected readonly draggingKey: import("@angular/core").WritableSignal<string | null>;
|
|
9113
9285
|
protected readonly announcement: import("@angular/core").WritableSignal<string>;
|
|
9114
9286
|
private readonly host;
|
|
9287
|
+
constructor();
|
|
9115
9288
|
private drag;
|
|
9116
9289
|
protected readonly skeletonColumns: import("@angular/core").Signal<number[]>;
|
|
9117
9290
|
protected readonly canDrag: import("@angular/core").Signal<boolean>;
|
|
9118
|
-
protected readonly canMoveAny: import("@angular/core").Signal<boolean>;
|
|
9119
9291
|
protected dot(color?: string): string;
|
|
9120
9292
|
protected onDragStart(event: DragEvent, item: T, col: BoardColumn<T>): void;
|
|
9121
9293
|
protected onDragEnd(): void;
|
|
@@ -9123,6 +9295,11 @@ export declare class BoardComponent<T = unknown> {
|
|
|
9123
9295
|
protected onDragLeave(event: DragEvent, col: BoardColumn<T>): void;
|
|
9124
9296
|
protected onDrop(event: DragEvent, col: BoardColumn<T>): void;
|
|
9125
9297
|
protected onCardKey(event: KeyboardEvent, item: T, col: BoardColumn<T>): void;
|
|
9298
|
+
/** The menu of each card by item key: the host's `cardActions`, then "Move to <column>" for each column that accepts the card (one shared array per column when the host has no `canMove` rule). */
|
|
9299
|
+
protected readonly menuActions: import("@angular/core").Signal<Map<string, RowAction[]>>;
|
|
9300
|
+
protected onMenuAction(type: string, item: T): void;
|
|
9301
|
+
/** The card is re-created in its new column: put the focus back on it once it is drawn. */
|
|
9302
|
+
private focusCard;
|
|
9126
9303
|
private accepts;
|
|
9127
9304
|
private move;
|
|
9128
9305
|
static ɵfac: i0.ɵɵFactoryDeclaration<BoardComponent<any>, never>;
|
|
@@ -9147,6 +9324,11 @@ export declare class BoardComponent<T = unknown> {
|
|
|
9147
9324
|
"required": false;
|
|
9148
9325
|
"isSignal": true;
|
|
9149
9326
|
};
|
|
9327
|
+
"moveMenu": {
|
|
9328
|
+
"alias": "moveMenu";
|
|
9329
|
+
"required": false;
|
|
9330
|
+
"isSignal": true;
|
|
9331
|
+
};
|
|
9150
9332
|
"loading": {
|
|
9151
9333
|
"alias": "loading";
|
|
9152
9334
|
"required": false;
|
|
@@ -9202,6 +9384,11 @@ export declare class BoardComponent<T = unknown> {
|
|
|
9202
9384
|
"required": false;
|
|
9203
9385
|
"isSignal": true;
|
|
9204
9386
|
};
|
|
9387
|
+
"pageSize": {
|
|
9388
|
+
"alias": "pageSize";
|
|
9389
|
+
"required": false;
|
|
9390
|
+
"isSignal": true;
|
|
9391
|
+
};
|
|
9205
9392
|
}, {
|
|
9206
9393
|
"cardClick": "cardClick";
|
|
9207
9394
|
"cardAction": "cardAction";
|
|
@@ -9809,6 +9996,144 @@ export declare class StackComponent {
|
|
|
9809
9996
|
};
|
|
9810
9997
|
}, {}, never, ["*"], true, never>;
|
|
9811
9998
|
}
|
|
9999
|
+
export type SidebarPosition = 'start' | 'end';
|
|
10000
|
+
/** Viewport width at and below which the layout becomes one column: sm = 640px, md = 900px, lg = 1200px, never = always two columns. */
|
|
10001
|
+
export type SidebarCollapse = 'sm' | 'md' | 'lg' | 'never';
|
|
10002
|
+
/**
|
|
10003
|
+
* Detail-page layout: a MAIN column plus a SIDE column of fixed width.
|
|
10004
|
+
*
|
|
10005
|
+
* Replaces the page-level grid / `position: sticky` CSS each detail page used to carry. Project the two columns with
|
|
10006
|
+
* the `layout-main` and `layout-side` attributes:
|
|
10007
|
+
*
|
|
10008
|
+
* <ui-sidebar-layout sideWidth="340px" gap="md" collapseAt="md">
|
|
10009
|
+
* <div layout-main> ...tabs, tables, timeline... </div>
|
|
10010
|
+
* <aside layout-side aria-label="Contact summary"> ...profile card, quick actions... </aside>
|
|
10011
|
+
* </ui-sidebar-layout>
|
|
10012
|
+
*
|
|
10013
|
+
* - `sideWidth`: any CSS length (default `320px`). The main column takes the rest (`minmax(0, 1fr)` track and
|
|
10014
|
+
* `min-width: 0`, so a wide table cannot stretch the grid).
|
|
10015
|
+
* - `sidePosition`: `end` (default, side on the right) | `start` (side on the left). The DOM order is always main then
|
|
10016
|
+
* side; only the visual position changes.
|
|
10017
|
+
* - `gap`: layout gap token (none | xs | sm | md | lg | xl) or a CSS length. Default `md` (16px). It is the space between
|
|
10018
|
+
* the columns and, when collapsed, between the stacked columns.
|
|
10019
|
+
* - `stickySide` (default true): at wide widths the side column is `position: sticky` at `stickyTop` px (default 16) with
|
|
10020
|
+
* `max-height: 100vh - (stickyTop + stickyOffset)` (default offset 32px; `100vh` first, `100dvh` after it for browsers
|
|
10021
|
+
* that know it) and `overflow-y: auto`, so only the main column scrolls the page; a side column taller than the viewport
|
|
10022
|
+
* scrolls inside itself. `false` = a plain static column.
|
|
10023
|
+
* - `collapseAt`: sm (640px) | md (900px, default) | lg (1200px) | never. At and below that viewport width there is ONE
|
|
10024
|
+
* column and the side column is static (never sticky, no max-height). `sideFirstWhenCollapsed` (default true) puts the
|
|
10025
|
+
* side column first there; `false` puts the main column first.
|
|
10026
|
+
* - `sideFirstInDom` (default false): by default the side column is only moved to the top on small screens with CSS
|
|
10027
|
+
* `order`, so Tab / reading order stays main then side. With `sideFirstInDom` AND `sideFirstWhenCollapsed`, the side
|
|
10028
|
+
* wrapper is rendered BEFORE the main wrapper in the DOM while the viewport is at or below the `collapseAt` width
|
|
10029
|
+
* (tracked with `matchMedia` for the same query), so Tab order equals the visual order. At wide widths the DOM is main
|
|
10030
|
+
* then side. The projected nodes are moved, never recreated (same element instances, no state lost). No `matchMedia`
|
|
10031
|
+
* (server rendering) = not collapsed; CSS `order` still gives the right visual order until the browser takes over.
|
|
10032
|
+
* The `.sl` root element carries `data-dom-order="main-first|side-first"`.
|
|
10033
|
+
* - `sideBleed` (CSS length, default `4px`; `0px` = none): the sticky side column scrolls inside itself
|
|
10034
|
+
* (`overflow-y: auto`), which would clip focus rings and shadows at its edges. It gets padding and a negative margin of the
|
|
10035
|
+
* bleed size (the full `sideBleed` on the outer and block edges, `min(sideBleed, gap)` toward main), so the content keeps exactly its `sideWidth` and `gap` (the layout does not
|
|
10036
|
+
* move) while a 2px outline with a 2px offset still fits. `top` is reduced by the bleed so the content sticks at
|
|
10037
|
+
* `stickyTop`, and `max-height` is `100vh - (stickyTop + stickyOffset)` for the CONTENT (the bleed is added back).
|
|
10038
|
+
* The bleed toward main is clamped to the gap (`min(sideBleed, gap)`), so the transparent padding never overlaps or
|
|
10039
|
+
* intercepts clicks on main; the outer edge keeps the full bleed. Needs CSS `min()` (all current browsers).
|
|
10040
|
+
* Collapsed and non-sticky columns have no bleed. NOTE: the outer edge of the side box overhangs the layout by the bleed
|
|
10041
|
+
* (4px by default, on the right for `end`, on the left for `start`), so a flush clipping or scrolling ancestor can
|
|
10042
|
+
* show 4px of horizontal overflow: use `sideBleed="0px"` there.
|
|
10043
|
+
* - `sideWidth`, `sideBleed` and `gap` must be non-negative lengths; a negative value falls back to the default. A `calc(...)` or
|
|
10044
|
+
* `var(...)` value is passed through as written and is not checked: make sure it cannot compute to a negative length.
|
|
10045
|
+
* - An empty side slot (no `layout-side` element) hides the side column and the main column takes the full width.
|
|
10046
|
+
*
|
|
10047
|
+
* Plain media queries only (no `:has()`, no container queries) so it behaves the same in Chrome, Edge, Firefox and Safari.
|
|
10048
|
+
* The column gap is a margin on the side column (not a grid `column-gap`), so an empty side column leaves no stray gap.
|
|
10049
|
+
*/
|
|
10050
|
+
export declare class SidebarLayoutComponent {
|
|
10051
|
+
/** Width of the side column: any CSS length (default `320px`). */
|
|
10052
|
+
readonly sideWidth: import("@angular/core").InputSignal<string>;
|
|
10053
|
+
readonly sidePosition: import("@angular/core").InputSignal<SidebarPosition>;
|
|
10054
|
+
/** Space between the columns (and between them when collapsed): a layout gap token or a CSS length (default `md` = 16px). */
|
|
10055
|
+
readonly gap: import("@angular/core").InputSignal<string>;
|
|
10056
|
+
/** Keep the side column pinned while the main column scrolls (wide widths only). */
|
|
10057
|
+
readonly stickySide: import("@angular/core").InputSignal<boolean>;
|
|
10058
|
+
/** Distance in px from the top of the viewport at which the side column's content sticks (default 16). */
|
|
10059
|
+
readonly stickyTop: import("@angular/core").InputSignal<number>;
|
|
10060
|
+
/** Total px of non-viewport space above and below the side column, used for its max-height (default 32). */
|
|
10061
|
+
readonly stickyOffset: import("@angular/core").InputSignal<number>;
|
|
10062
|
+
readonly collapseAt: import("@angular/core").InputSignal<SidebarCollapse>;
|
|
10063
|
+
/** When collapsed to one column: side column first (default) or main column first. */
|
|
10064
|
+
readonly sideFirstWhenCollapsed: import("@angular/core").InputSignal<boolean>;
|
|
10065
|
+
/** Also put the side wrapper first in the DOM (not only visually) while collapsed, so Tab order equals visual order. Default false. */
|
|
10066
|
+
readonly sideFirstInDom: import("@angular/core").InputSignal<boolean>;
|
|
10067
|
+
/** Room (CSS length) inside the sticky side's scroll box so focus rings at its edge are not clipped. Default `4px`; `0px` = none. */
|
|
10068
|
+
readonly sideBleed: import("@angular/core").InputSignal<string>;
|
|
10069
|
+
/** True while the viewport is at or below the collapseAt width; only tracked when sideFirstInDom is set. */
|
|
10070
|
+
private readonly collapsed;
|
|
10071
|
+
protected readonly position: import("@angular/core").Signal<SidebarPosition>;
|
|
10072
|
+
private readonly collapseKey;
|
|
10073
|
+
/** The side wrapper is rendered before the main wrapper. */
|
|
10074
|
+
protected readonly domSideFirst: import("@angular/core").Signal<boolean>;
|
|
10075
|
+
protected readonly domOrder: import("@angular/core").Signal<"side-first" | "main-first">;
|
|
10076
|
+
protected readonly sideCss: import("@angular/core").Signal<string>;
|
|
10077
|
+
protected readonly bleedCss: import("@angular/core").Signal<string>;
|
|
10078
|
+
protected readonly gapCss: import("@angular/core").Signal<string>;
|
|
10079
|
+
protected readonly topCss: import("@angular/core").Signal<string>;
|
|
10080
|
+
protected readonly offCss: import("@angular/core").Signal<string>;
|
|
10081
|
+
protected readonly rootClass: import("@angular/core").Signal<string>;
|
|
10082
|
+
constructor();
|
|
10083
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SidebarLayoutComponent, never>;
|
|
10084
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SidebarLayoutComponent, "ui-sidebar-layout", never, {
|
|
10085
|
+
"sideWidth": {
|
|
10086
|
+
"alias": "sideWidth";
|
|
10087
|
+
"required": false;
|
|
10088
|
+
"isSignal": true;
|
|
10089
|
+
};
|
|
10090
|
+
"sidePosition": {
|
|
10091
|
+
"alias": "sidePosition";
|
|
10092
|
+
"required": false;
|
|
10093
|
+
"isSignal": true;
|
|
10094
|
+
};
|
|
10095
|
+
"gap": {
|
|
10096
|
+
"alias": "gap";
|
|
10097
|
+
"required": false;
|
|
10098
|
+
"isSignal": true;
|
|
10099
|
+
};
|
|
10100
|
+
"stickySide": {
|
|
10101
|
+
"alias": "stickySide";
|
|
10102
|
+
"required": false;
|
|
10103
|
+
"isSignal": true;
|
|
10104
|
+
};
|
|
10105
|
+
"stickyTop": {
|
|
10106
|
+
"alias": "stickyTop";
|
|
10107
|
+
"required": false;
|
|
10108
|
+
"isSignal": true;
|
|
10109
|
+
};
|
|
10110
|
+
"stickyOffset": {
|
|
10111
|
+
"alias": "stickyOffset";
|
|
10112
|
+
"required": false;
|
|
10113
|
+
"isSignal": true;
|
|
10114
|
+
};
|
|
10115
|
+
"collapseAt": {
|
|
10116
|
+
"alias": "collapseAt";
|
|
10117
|
+
"required": false;
|
|
10118
|
+
"isSignal": true;
|
|
10119
|
+
};
|
|
10120
|
+
"sideFirstWhenCollapsed": {
|
|
10121
|
+
"alias": "sideFirstWhenCollapsed";
|
|
10122
|
+
"required": false;
|
|
10123
|
+
"isSignal": true;
|
|
10124
|
+
};
|
|
10125
|
+
"sideFirstInDom": {
|
|
10126
|
+
"alias": "sideFirstInDom";
|
|
10127
|
+
"required": false;
|
|
10128
|
+
"isSignal": true;
|
|
10129
|
+
};
|
|
10130
|
+
"sideBleed": {
|
|
10131
|
+
"alias": "sideBleed";
|
|
10132
|
+
"required": false;
|
|
10133
|
+
"isSignal": true;
|
|
10134
|
+
};
|
|
10135
|
+
}, {}, never, ["[layout-main]", "[layout-side]"], true, never>;
|
|
10136
|
+
}
|
|
9812
10137
|
export type ActionBarAlign = 'start' | 'center' | 'end' | 'between';
|
|
9813
10138
|
/**
|
|
9814
10139
|
* Bar for page-level actions (Save / Cancel) that stays at the bottom of the scrolling area.
|