@forgeroute/ui 0.4.22 → 0.4.24

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.22",
3
+ "version": "0.4.24",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=22.0.0",
6
6
  "@angular/core": ">=22.0.0",
@@ -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', with or without this flag. */
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).
@@ -836,7 +843,7 @@ export declare class TableToolbarComponent {
836
843
  readonly activeViewId: import("@angular/core").InputSignal<string | null>;
837
844
  readonly showSavedViews: import("@angular/core").InputSignal<boolean>;
838
845
  /**
839
- * Read-only (permission isn't 'full'): only Export stays — no page buttons, no Import, no Columns, no
846
+ * Read-only (permission isn't 'full'): only Export and pure reads marked `allowReadOnly` stay — no other page buttons, no Import, no Columns, no
840
847
  * Save Current View, no deleting views. More Actions offers only the user's existing Saved Views and is
841
848
  * hidden when they have none.
842
849
  */
@@ -856,7 +863,7 @@ export declare class TableToolbarComponent {
856
863
  protected _saveViewName: string;
857
864
  /** Draft column visibility — only applied to the table when the user clicks Apply. */
858
865
  private readonly _pendingCols;
859
- /** 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). */
860
867
  private readonly _allowedButtons;
861
868
  protected readonly standaloneButtons: import("@angular/core").Signal<ToolbarAction[]>;
862
869
  protected readonly importButton: import("@angular/core").Signal<ToolbarAction | null>;
@@ -1342,7 +1349,7 @@ export declare class DataTableComponent<T extends Record<string, any>> implement
1342
1349
  /**
1343
1350
  * Page permission. Anything other than 'full' makes the table read-only: no Import,
1344
1351
  * no bulk update/delete and no checkbox column (even when `selectable` is set), no page-defined
1345
- * toolbar buttons at all (only Export; More Actions offers only their existing Saved Views), and the
1352
+ * toolbar buttons except Export and pure reads marked `allowReadOnly` (More Actions offers only their existing Saved Views), and the
1346
1353
  * row menu offers only View (`type: 'view'`).
1347
1354
  * Rows with nothing to show get no "⋮" trigger; the Actions column is dropped when no row has View.
1348
1355
  */
@@ -6858,6 +6865,9 @@ export interface PanelInfoItem {
6858
6865
  /** Renders a small edit (pencil) `ui-icon` button after the value; `editTooltip` is its tooltip and accessible name (default "Edit"). */
6859
6866
  editAction?: () => void;
6860
6867
  editTooltip?: string;
6868
+ /** Renders a small delete (bin) `ui-icon` button after the edit pencil; the host asks for confirmation and does the deleting. `deleteTooltip` is its tooltip and accessible name (default "Delete"). */
6869
+ deleteAction?: () => void;
6870
+ deleteTooltip?: string;
6861
6871
  muted?: boolean;
6862
6872
  danger?: boolean;
6863
6873
  hidden?: boolean;
@@ -7322,7 +7332,7 @@ export declare class AlertComponent {
7322
7332
  /** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
7323
7333
  export declare function iconPath(name: string): string | undefined;
7324
7334
  /** Keys with an icon of their own (the data-table action keys are accepted too — see `builtInActionIcon`). */
7325
- 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';
7335
+ 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';
7326
7336
  /**
7327
7337
  * A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
7328
7338
  * `iconClick`, and the host does the actual work (delete the row, open a panel, …).
@@ -9217,12 +9227,18 @@ export declare class BoardCardDirective<T = unknown> {
9217
9227
  export declare class BoardComponent<T = unknown> {
9218
9228
  /** The columns in display order, each with its items. A new value replaces the board's content (and any on-screen move). */
9219
9229
  readonly columns: import("@angular/core").InputSignal<BoardColumn<T>[]>;
9220
- /** Stable unique id of a card (used to track cards between renders). */
9230
+ /** 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. */
9221
9231
  readonly itemKey: import("@angular/core").InputSignal<(item: T) => string>;
9222
9232
  /** Spoken name of a card: title, who, status, value... The card's visible content is the `uiBoardCard` template. */
9223
9233
  readonly itemLabel: import("@angular/core").InputSignal<(item: T) => string>;
9224
9234
  /** Menu on every card (the "⋮" button). Empty = no menu. Handle the pick in `(cardAction)`. */
9225
9235
  readonly cardActions: import("@angular/core").InputSignal<RowAction[]>;
9236
+ /**
9237
+ * 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
9238
+ * without dragging - by touch, with a screen reader or with the mouse alone. Only while cards can be moved (not read-only,
9239
+ * not `moveDisabled`) and only to columns that accept the card (`canMove`, `dropDisabled`). Set false to leave the menu to `cardActions`.
9240
+ */
9241
+ readonly moveMenu: import("@angular/core").InputSignal<boolean>;
9226
9242
  readonly loading: import("@angular/core").InputSignal<boolean>;
9227
9243
  /** Error text: replaces the board with the message and a Retry button. */
9228
9244
  readonly error: import("@angular/core").InputSignal<string | null>;
@@ -9282,6 +9298,11 @@ export declare class BoardComponent<T = unknown> {
9282
9298
  protected onDragLeave(event: DragEvent, col: BoardColumn<T>): void;
9283
9299
  protected onDrop(event: DragEvent, col: BoardColumn<T>): void;
9284
9300
  protected onCardKey(event: KeyboardEvent, item: T, col: BoardColumn<T>): void;
9301
+ /** 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). */
9302
+ protected readonly menuActions: import("@angular/core").Signal<Map<string, RowAction[]>>;
9303
+ protected onMenuAction(type: string, item: T): void;
9304
+ /** The card is re-created in its new column: put the focus back on it once it is drawn. */
9305
+ private focusCard;
9285
9306
  private accepts;
9286
9307
  private move;
9287
9308
  static ɵfac: i0.ɵɵFactoryDeclaration<BoardComponent<any>, never>;
@@ -9306,6 +9327,11 @@ export declare class BoardComponent<T = unknown> {
9306
9327
  "required": false;
9307
9328
  "isSignal": true;
9308
9329
  };
9330
+ "moveMenu": {
9331
+ "alias": "moveMenu";
9332
+ "required": false;
9333
+ "isSignal": true;
9334
+ };
9309
9335
  "loading": {
9310
9336
  "alias": "loading";
9311
9337
  "required": false;
@@ -10000,6 +10026,25 @@ export type SidebarCollapse = 'sm' | 'md' | 'lg' | 'never';
10000
10026
  * - `collapseAt`: sm (640px) | md (900px, default) | lg (1200px) | never. At and below that viewport width there is ONE
10001
10027
  * column and the side column is static (never sticky, no max-height). `sideFirstWhenCollapsed` (default true) puts the
10002
10028
  * side column first there; `false` puts the main column first.
10029
+ * - `sideFirstInDom` (default false): by default the side column is only moved to the top on small screens with CSS
10030
+ * `order`, so Tab / reading order stays main then side. With `sideFirstInDom` AND `sideFirstWhenCollapsed`, the side
10031
+ * wrapper is rendered BEFORE the main wrapper in the DOM while the viewport is at or below the `collapseAt` width
10032
+ * (tracked with `matchMedia` for the same query), so Tab order equals the visual order. At wide widths the DOM is main
10033
+ * then side. The projected nodes are moved, never recreated (same element instances, no state lost). No `matchMedia`
10034
+ * (server rendering) = not collapsed; CSS `order` still gives the right visual order until the browser takes over.
10035
+ * The `.sl` root element carries `data-dom-order="main-first|side-first"`.
10036
+ * - `sideBleed` (CSS length, default `4px`; `0px` = none): the sticky side column scrolls inside itself
10037
+ * (`overflow-y: auto`), which would clip focus rings and shadows at its edges. It gets padding and a negative margin of the
10038
+ * 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
10039
+ * move) while a 2px outline with a 2px offset still fits. `top` is reduced by the bleed so the content sticks at
10040
+ * `stickyTop`, and `max-height` is `100vh - (stickyTop + stickyOffset)` for the CONTENT (the bleed is added back).
10041
+ * The bleed toward main is clamped to the gap (`min(sideBleed, gap)`), so the transparent padding never overlaps or
10042
+ * intercepts clicks on main; the outer edge keeps the full bleed. Needs CSS `min()` (all current browsers).
10043
+ * Collapsed and non-sticky columns have no bleed. NOTE: the outer edge of the side box overhangs the layout by the bleed
10044
+ * (4px by default, on the right for `end`, on the left for `start`), so a flush clipping or scrolling ancestor can
10045
+ * show 4px of horizontal overflow: use `sideBleed="0px"` there.
10046
+ * - `sideWidth`, `sideBleed` and `gap` must be non-negative lengths; a negative value falls back to the default. A `calc(...)` or
10047
+ * `var(...)` value is passed through as written and is not checked: make sure it cannot compute to a negative length.
10003
10048
  * - An empty side slot (no `layout-side` element) hides the side column and the main column takes the full width.
10004
10049
  *
10005
10050
  * Plain media queries only (no `:has()`, no container queries) so it behaves the same in Chrome, Edge, Firefox and Safari.
@@ -10013,19 +10058,31 @@ export declare class SidebarLayoutComponent {
10013
10058
  readonly gap: import("@angular/core").InputSignal<string>;
10014
10059
  /** Keep the side column pinned while the main column scrolls (wide widths only). */
10015
10060
  readonly stickySide: import("@angular/core").InputSignal<boolean>;
10016
- /** Distance in px from the top of the viewport at which the side column sticks (default 16). */
10061
+ /** Distance in px from the top of the viewport at which the side column's content sticks (default 16). */
10017
10062
  readonly stickyTop: import("@angular/core").InputSignal<number>;
10018
10063
  /** Total px of non-viewport space above and below the side column, used for its max-height (default 32). */
10019
10064
  readonly stickyOffset: import("@angular/core").InputSignal<number>;
10020
10065
  readonly collapseAt: import("@angular/core").InputSignal<SidebarCollapse>;
10021
10066
  /** When collapsed to one column: side column first (default) or main column first. */
10022
10067
  readonly sideFirstWhenCollapsed: import("@angular/core").InputSignal<boolean>;
10068
+ /** Also put the side wrapper first in the DOM (not only visually) while collapsed, so Tab order equals visual order. Default false. */
10069
+ readonly sideFirstInDom: import("@angular/core").InputSignal<boolean>;
10070
+ /** Room (CSS length) inside the sticky side's scroll box so focus rings at its edge are not clipped. Default `4px`; `0px` = none. */
10071
+ readonly sideBleed: import("@angular/core").InputSignal<string>;
10072
+ /** True while the viewport is at or below the collapseAt width; only tracked when sideFirstInDom is set. */
10073
+ private readonly collapsed;
10023
10074
  protected readonly position: import("@angular/core").Signal<SidebarPosition>;
10075
+ private readonly collapseKey;
10076
+ /** The side wrapper is rendered before the main wrapper. */
10077
+ protected readonly domSideFirst: import("@angular/core").Signal<boolean>;
10078
+ protected readonly domOrder: import("@angular/core").Signal<"side-first" | "main-first">;
10024
10079
  protected readonly sideCss: import("@angular/core").Signal<string>;
10080
+ protected readonly bleedCss: import("@angular/core").Signal<string>;
10025
10081
  protected readonly gapCss: import("@angular/core").Signal<string>;
10026
10082
  protected readonly topCss: import("@angular/core").Signal<string>;
10027
10083
  protected readonly offCss: import("@angular/core").Signal<string>;
10028
10084
  protected readonly rootClass: import("@angular/core").Signal<string>;
10085
+ constructor();
10029
10086
  static ɵfac: i0.ɵɵFactoryDeclaration<SidebarLayoutComponent, never>;
10030
10087
  static ɵcmp: i0.ɵɵComponentDeclaration<SidebarLayoutComponent, "ui-sidebar-layout", never, {
10031
10088
  "sideWidth": {
@@ -10068,6 +10125,16 @@ export declare class SidebarLayoutComponent {
10068
10125
  "required": false;
10069
10126
  "isSignal": true;
10070
10127
  };
10128
+ "sideFirstInDom": {
10129
+ "alias": "sideFirstInDom";
10130
+ "required": false;
10131
+ "isSignal": true;
10132
+ };
10133
+ "sideBleed": {
10134
+ "alias": "sideBleed";
10135
+ "required": false;
10136
+ "isSignal": true;
10137
+ };
10071
10138
  }, {}, never, ["[layout-main]", "[layout-side]"], true, never>;
10072
10139
  }
10073
10140
  export type ActionBarAlign = 'start' | 'center' | 'end' | 'between';