@forgeroute/ui 0.4.22 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeroute/ui",
3
- "version": "0.4.22",
3
+ "version": "0.4.23",
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
  */
@@ -7322,7 +7329,7 @@ export declare class AlertComponent {
7322
7329
  /** The SVG path (24×24 stroke icon) for an icon key, or undefined when the key is unknown. */
7323
7330
  export declare function iconPath(name: string): string | undefined;
7324
7331
  /** 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';
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';
7326
7333
  /**
7327
7334
  * A clickable icon. The host passes a key (`name="delete"`) and the library draws that icon; clicking it emits
7328
7335
  * `iconClick`, and the host does the actual work (delete the row, open a panel, …).
@@ -9217,12 +9224,18 @@ export declare class BoardCardDirective<T = unknown> {
9217
9224
  export declare class BoardComponent<T = unknown> {
9218
9225
  /** The columns in display order, each with its items. A new value replaces the board's content (and any on-screen move). */
9219
9226
  readonly columns: import("@angular/core").InputSignal<BoardColumn<T>[]>;
9220
- /** 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. */
9221
9228
  readonly itemKey: import("@angular/core").InputSignal<(item: T) => string>;
9222
9229
  /** Spoken name of a card: title, who, status, value... The card's visible content is the `uiBoardCard` template. */
9223
9230
  readonly itemLabel: import("@angular/core").InputSignal<(item: T) => string>;
9224
9231
  /** Menu on every card (the "⋮" button). Empty = no menu. Handle the pick in `(cardAction)`. */
9225
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>;
9226
9239
  readonly loading: import("@angular/core").InputSignal<boolean>;
9227
9240
  /** Error text: replaces the board with the message and a Retry button. */
9228
9241
  readonly error: import("@angular/core").InputSignal<string | null>;
@@ -9282,6 +9295,11 @@ export declare class BoardComponent<T = unknown> {
9282
9295
  protected onDragLeave(event: DragEvent, col: BoardColumn<T>): void;
9283
9296
  protected onDrop(event: DragEvent, col: BoardColumn<T>): void;
9284
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;
9285
9303
  private accepts;
9286
9304
  private move;
9287
9305
  static ɵfac: i0.ɵɵFactoryDeclaration<BoardComponent<any>, never>;
@@ -9306,6 +9324,11 @@ export declare class BoardComponent<T = unknown> {
9306
9324
  "required": false;
9307
9325
  "isSignal": true;
9308
9326
  };
9327
+ "moveMenu": {
9328
+ "alias": "moveMenu";
9329
+ "required": false;
9330
+ "isSignal": true;
9331
+ };
9309
9332
  "loading": {
9310
9333
  "alias": "loading";
9311
9334
  "required": false;
@@ -10000,6 +10023,25 @@ export type SidebarCollapse = 'sm' | 'md' | 'lg' | 'never';
10000
10023
  * - `collapseAt`: sm (640px) | md (900px, default) | lg (1200px) | never. At and below that viewport width there is ONE
10001
10024
  * column and the side column is static (never sticky, no max-height). `sideFirstWhenCollapsed` (default true) puts the
10002
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.
10003
10045
  * - An empty side slot (no `layout-side` element) hides the side column and the main column takes the full width.
10004
10046
  *
10005
10047
  * Plain media queries only (no `:has()`, no container queries) so it behaves the same in Chrome, Edge, Firefox and Safari.
@@ -10013,19 +10055,31 @@ export declare class SidebarLayoutComponent {
10013
10055
  readonly gap: import("@angular/core").InputSignal<string>;
10014
10056
  /** Keep the side column pinned while the main column scrolls (wide widths only). */
10015
10057
  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). */
10058
+ /** Distance in px from the top of the viewport at which the side column's content sticks (default 16). */
10017
10059
  readonly stickyTop: import("@angular/core").InputSignal<number>;
10018
10060
  /** Total px of non-viewport space above and below the side column, used for its max-height (default 32). */
10019
10061
  readonly stickyOffset: import("@angular/core").InputSignal<number>;
10020
10062
  readonly collapseAt: import("@angular/core").InputSignal<SidebarCollapse>;
10021
10063
  /** When collapsed to one column: side column first (default) or main column first. */
10022
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;
10023
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">;
10024
10076
  protected readonly sideCss: import("@angular/core").Signal<string>;
10077
+ protected readonly bleedCss: import("@angular/core").Signal<string>;
10025
10078
  protected readonly gapCss: import("@angular/core").Signal<string>;
10026
10079
  protected readonly topCss: import("@angular/core").Signal<string>;
10027
10080
  protected readonly offCss: import("@angular/core").Signal<string>;
10028
10081
  protected readonly rootClass: import("@angular/core").Signal<string>;
10082
+ constructor();
10029
10083
  static ɵfac: i0.ɵɵFactoryDeclaration<SidebarLayoutComponent, never>;
10030
10084
  static ɵcmp: i0.ɵɵComponentDeclaration<SidebarLayoutComponent, "ui-sidebar-layout", never, {
10031
10085
  "sideWidth": {
@@ -10068,6 +10122,16 @@ export declare class SidebarLayoutComponent {
10068
10122
  "required": false;
10069
10123
  "isSignal": true;
10070
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
+ };
10071
10135
  }, {}, never, ["[layout-main]", "[layout-side]"], true, never>;
10072
10136
  }
10073
10137
  export type ActionBarAlign = 'start' | 'center' | 'end' | 'between';