mn-angular-lib 1.0.131 → 1.0.132

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": "mn-angular-lib",
3
- "version": "1.0.131",
3
+ "version": "1.0.132",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.1.3",
6
6
  "@angular/core": "^21.1.3"
@@ -3474,22 +3474,30 @@ type TableAppearance = {
3474
3474
  compact?: boolean;
3475
3475
  bordered?: boolean;
3476
3476
  /**
3477
- * How column widths are computed.
3477
+ * How column widths are computed. Defaults to `stable`.
3478
3478
  *
3479
- * - `auto` (default): the browser sizes each column to its widest cell, so the
3480
- * columns shift every time the content changes a new page, a filter, a
3481
- * search. Fine for a static table.
3482
- * - `fixed`: widths come from the header row and each column's {@link ColumnBase.width}
3483
- * only, never from the cell content, so they stay put across pages and
3484
- * filters. Columns without a `width` split the remaining space evenly.
3485
- * Overlong cell text is truncated with an ellipsis (and exposed as a
3486
- * `title` tooltip) instead of widening the column.
3487
- *
3488
- * Prefer `fixed` for any table whose rows change under the user server-side
3489
- * paginated, filtered or searched tables, and tables inside a modal, where a
3490
- * width change is most visible.
3491
- */
3492
- layout?: 'auto' | 'fixed';
3479
+ * - `stable` (default): the best of both. The first render with rows on screen
3480
+ * uses the browser's automatic layout, so each column is sized in proportion to
3481
+ * its real content; those measured widths are then pinned and the table switches
3482
+ * to a fixed layout. Columns therefore keep sensible, content-derived
3483
+ * proportions **and** stop moving when the rows change underneath a new page,
3484
+ * a filter or a search cannot resize them. Widths are re-measured only when the
3485
+ * table itself is resized (or a {@link ColumnBase.hiddenBelow} column appears or
3486
+ * disappears), never when the rows change. Content that no longer fits is
3487
+ * truncated with an ellipsis and exposed as a `title` tooltip.
3488
+ * - `auto`: the plain browser layout. Every column is re-sized to its widest cell
3489
+ * on every change, so the columns shift on each new page, filter and search.
3490
+ * Use it for a static table, or when a cell must never be truncated.
3491
+ * - `fixed`: widths are **data-independent**. Nothing is measured — not the cell
3492
+ * content, and not the header text either. Each column is either its declared
3493
+ * {@link ColumnBase.width} or an even share of whatever is left over:
3494
+ * `(table width − Σ declared widths) ÷ number of undeclared visible columns`.
3495
+ * Only worth choosing over `stable` when every column declares a `width`, or
3496
+ * when a deliberate even split is what you want: with widths undeclared, a
3497
+ * two-character status column is handed exactly as much room as a long
3498
+ * description.
3499
+ */
3500
+ layout?: 'auto' | 'fixed' | 'stable';
3493
3501
  };
3494
3502
  /**
3495
3503
  * The control rendered for a column filter, and the shape of the value it produces:
@@ -3744,7 +3752,11 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3744
3752
  * and its footer is pinned over the bottom of the table.
3745
3753
  */
3746
3754
  private static readonly MOBILE_PAGE_SIZE;
3747
- /** The component's own element, measured for every responsive decision. */
3755
+ /**
3756
+ * The component's own element, measured for every responsive decision. Typed via
3757
+ * the annotation, not `inject(ElementRef<HTMLElement>)` — that form is a generic
3758
+ * call on the token and leaves `nativeElement` untyped.
3759
+ */
3748
3760
  private readonly host;
3749
3761
  /** Whether the consumer owns filtering (server-side), mirroring {@link isServerSearched}. */
3750
3762
  get isServerFiltered(): boolean;
@@ -3818,15 +3830,20 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3818
3830
  /** Opens/closes the stacked filter panel shown on small screens. */
3819
3831
  toggleFiltersPanel(): void;
3820
3832
  private readonly baseTableClasses;
3821
- constructor();
3833
+ /**
3834
+ * Column widths measured from the automatic layout and pinned, keyed by column
3835
+ * key, for `stable`. Empty until the first render that has real rows on screen,
3836
+ * and cleared whenever the table is resized so the next render re-measures.
3837
+ */
3838
+ private pinnedWidths;
3822
3839
  /** Sets sort/filter state seeded from the data source before the first filter pass. */
3823
3840
  protected beforeInitialFilter(): void;
3824
3841
  /**
3825
- * Classes for the `<table>` element. `table-fixed` is added for the `fixed`
3826
- * layout so column widths come from the header row and the declared widths
3827
- * only, keeping them stable as the rows change.
3842
+ * Whether {@link pinColumnWidths} has run. Tracked separately from
3843
+ * {@link pinnedWidths} being non-empty, because the widest column is deliberately
3844
+ * left unpinned and a table with a single flexible column therefore pins nothing.
3828
3845
  */
3829
- get tableClasses(): string;
3846
+ private widthsPinned;
3830
3847
  /**
3831
3848
  * Recomputes whether the inline filter row should collapse into the panel.
3832
3849
  * Closes the panel when returning to the wide layout so reopened state never
@@ -3843,12 +3860,33 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3843
3860
  getColumnSkeletonData(column: ColumnDefinition<T>): Partial<MnSkeletonProps>;
3844
3861
  getSortIcon(column: ColumnDefinition<T>): string;
3845
3862
  isSortable(column: ColumnDefinition<T>): boolean;
3846
- /** Whether column widths are content-independent (see {@link TableAppearance.layout}). */
3847
- get isFixedLayout(): boolean;
3863
+ constructor();
3864
+ /**
3865
+ * Classes for the `<table>` element. `table-fixed` is added once column widths
3866
+ * are no longer allowed to follow the content: always for the `fixed` layout, and
3867
+ * for `stable` from the moment its widths have been measured and pinned.
3868
+ */
3869
+ get tableClasses(): string;
3848
3870
  /** Page size to use at/above the `md` breakpoint (consumer's pageSize, or the user's selection). */
3849
3871
  private desktopPageSize;
3872
+ /** The effective column-width strategy, defaulting to `stable`. */
3873
+ get layoutMode(): 'auto' | 'fixed' | 'stable';
3874
+ /**
3875
+ * Whether column widths have stopped following the cell content — `fixed` always,
3876
+ * `stable` once {@link pinColumnWidths} has captured them. Drives `table-fixed`
3877
+ * and the cell truncation together, so a cell is never clipped while the column
3878
+ * it sits in could still have grown to fit it.
3879
+ */
3880
+ get widthsArePinned(): boolean;
3850
3881
  /**
3851
- * The `title` tooltip for a cell, so text truncated by the fixed layout stays
3882
+ * The width to render for a column: the consumer's own declared width always
3883
+ * wins, then a width pinned by the `stable` layout, otherwise none.
3884
+ * @param column The column being rendered.
3885
+ * @returns A CSS width, or `null` to leave it to the layout algorithm.
3886
+ */
3887
+ columnWidth(column: ColumnDefinition<T>): string | null;
3888
+ /**
3889
+ * The `title` tooltip for a cell, so text truncated by a pinned column stays
3852
3890
  * readable. Only string cells have text to expose; template cells render their
3853
3891
  * own markup and are left alone.
3854
3892
  * @param column The column being rendered.
@@ -3856,6 +3894,31 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3856
3894
  * @returns The full cell text, or `null` when there is nothing to expose.
3857
3895
  */
3858
3896
  cellTitle(column: ColumnDefinition<T>, row: T): string | null;
3897
+ /**
3898
+ * Captures the current, automatically-derived width of every visible column and
3899
+ * pins it, which flips the table to `table-fixed` on the next render.
3900
+ *
3901
+ * Runs only with real rows on screen: measuring the loading skeletons would pin
3902
+ * the placeholder bars' widths rather than the data's. Hidden columns
3903
+ * ({@link ColumnBase.hiddenBelow}) measure 0 and are skipped, so they are free to
3904
+ * size themselves if a resize later reveals them.
3905
+ *
3906
+ * The **widest** column is measured but deliberately left unpinned, so it absorbs
3907
+ * whatever space the pinned ones leave over. Pinning every column instead makes the
3908
+ * widths sum to slightly more than the container — `border-collapse` shares borders
3909
+ * between neighbours, so rounding each cell's measured width over-counts them — and
3910
+ * the table then overflows into a spurious horizontal scrollbar. Leaving one column
3911
+ * elastic also means a later resize squeezes the widest column first instead of
3912
+ * clipping every column equally.
3913
+ */
3914
+ private pinColumnWidths;
3915
+ /**
3916
+ * Drops the pinned widths so the next render with rows re-measures them. Called
3917
+ * when the table is resized: the old pixel widths were shares of a box that no
3918
+ * longer exists, and a resize is also what makes `hiddenBelow` columns come and
3919
+ * go, changing which columns need a share at all.
3920
+ */
3921
+ private unpinColumnWidths;
3859
3922
  /**
3860
3923
  * Re-evaluate on a window resize too. The ResizeObserver covers every change to
3861
3924
  * the table's own box, but {@link isMobileViewport} reads the window, which can
@@ -3979,6 +4042,9 @@ declare function matchesColumnFilter<T>(column: ColumnDefinition<T>, row: T, val
3979
4042
  * window, so viewport breakpoints would reveal columns the table has no room for.
3980
4043
  * mn-table marks its chrome `@container` for exactly this.
3981
4044
  *
4045
+ * Because of that, `sm`/`md`/`lg` mean "the table is at least this wide", and the
4046
+ * thresholds are **not** the viewport values of the same names — see {@link classMap}.
4047
+ *
3982
4048
  * Uses a static class map so Tailwind CSS can detect the full class names at build time.
3983
4049
  *
3984
4050
  * Usage: `<td [mnHiddenBelow]="column.hiddenBelow">`
@@ -3989,7 +4055,18 @@ declare class MnHiddenBelowDirective implements OnChanges {
3989
4055
  private readonly el;
3990
4056
  private readonly renderer;
3991
4057
  private appliedClasses;
3992
- /** Static mapping of breakpoints to their full Tailwind class names. */
4058
+ /**
4059
+ * Static mapping of breakpoints to their full Tailwind class names, so Tailwind
4060
+ * can detect them at build time.
4061
+ *
4062
+ * These are **container** widths, deliberately lower than the viewport
4063
+ * breakpoints they are named after. A table almost never gets the whole window:
4064
+ * a page table sits inside a docked sidebar plus page padding, which on a
4065
+ * 1280px screen leaves it under 900px. Reusing 1024px for `lg` would demand a
4066
+ * ~1400px window before an `lg` column ever appeared — hiding columns on the
4067
+ * most ordinary laptop. These values instead express how much room the column
4068
+ * itself needs, which is what a container query should measure.
4069
+ */
3993
4070
  private readonly classMap;
3994
4071
  ngOnChanges(): void;
3995
4072
  static ɵfac: i0.ɵɵFactoryDeclaration<MnHiddenBelowDirective, never>;