mn-angular-lib 1.0.130 → 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.130",
3
+ "version": "1.0.132",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.1.3",
6
6
  "@angular/core": "^21.1.3"
@@ -3354,7 +3354,24 @@ declare abstract class MnCollectionBase<T, DS extends MnCollectionDataSource<T>>
3354
3354
  /** Client-side search filtering shared by list and grid. */
3355
3355
  protected applySearchFilter(items: T[]): T[];
3356
3356
  protected processLoadedRows(rows: T[]): void;
3357
- protected validateDataSource(): void;
3357
+ /**
3358
+ * Reports every misconfigured pagination setting and repairs it in place.
3359
+ *
3360
+ * This deliberately does **not** throw. It runs first in {@link ngOnInit}, and a
3361
+ * throw there aborts the rest of init — the data subscription is never made and
3362
+ * {@link applyFilter} never runs, so the component renders a permanently empty
3363
+ * body that only "heals" once some later interaction happens to call
3364
+ * {@link applyFilter}. That failure mode reads as "the table is broken" rather
3365
+ * than "the data source is misconfigured", and inside a modal the thrown error
3366
+ * is easy to miss entirely. Logging loudly and degrading to the nearest working
3367
+ * mode keeps the misconfiguration visible while still rendering the rows.
3368
+ */
3369
+ protected normalizeDataSource(): void;
3370
+ /**
3371
+ * Logs a data-source configuration problem, prefixed with the component name.
3372
+ * @param message What is wrong and how it was compensated for.
3373
+ */
3374
+ private reportConfigError;
3358
3375
  static ɵfac: i0.ɵɵFactoryDeclaration<MnCollectionBase<any, any>, never>;
3359
3376
  static ɵdir: i0.ɵɵDirectiveDeclaration<MnCollectionBase<any, any>, never, never, { "dataSource": { "alias": "dataSource"; "required": false; }; }, {}, never, never, true, never>;
3360
3377
  }
@@ -3456,6 +3473,31 @@ type TableAppearance = {
3456
3473
  hover?: boolean;
3457
3474
  compact?: boolean;
3458
3475
  bordered?: boolean;
3476
+ /**
3477
+ * How column widths are computed. Defaults to `stable`.
3478
+ *
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';
3459
3501
  };
3460
3502
  /**
3461
3503
  * The control rendered for a column filter, and the shape of the value it produces:
@@ -3702,7 +3744,20 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3702
3744
  private readonly filterDebounce;
3703
3745
  /** The open filter popover, used to tell inside clicks from outside ones. */
3704
3746
  private filterPopover?;
3705
- constructor();
3747
+ /**
3748
+ * Most rows shown per page on mobile (< md). A **cap**, not an override: a data
3749
+ * source asking for fewer rows keeps its own size. Raising a small page size on
3750
+ * a phone is the opposite of what it is for — it pushes the paginator below the
3751
+ * fold, which is most damaging inside a modal, where the sheet is already short
3752
+ * and its footer is pinned over the bottom of the table.
3753
+ */
3754
+ private static readonly MOBILE_PAGE_SIZE;
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
+ */
3760
+ private readonly host;
3706
3761
  /** Whether the consumer owns filtering (server-side), mirroring {@link isServerSearched}. */
3707
3762
  get isServerFiltered(): boolean;
3708
3763
  /** Every column filter that is actually set, in column order. */
@@ -3774,12 +3829,21 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3774
3829
  get clearFiltersButtonLabel(): string;
3775
3830
  /** Opens/closes the stacked filter panel shown on small screens. */
3776
3831
  toggleFiltersPanel(): void;
3777
- /** Re-evaluate responsive page size and filter layout when the viewport changes. */
3778
- protected onWindowResize(): void;
3832
+ private readonly baseTableClasses;
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;
3779
3839
  /** Sets sort/filter state seeded from the data source before the first filter pass. */
3780
3840
  protected beforeInitialFilter(): void;
3781
- /** True when the viewport is below the filter-collapse breakpoint. */
3782
- private isFilterViewport;
3841
+ /**
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.
3845
+ */
3846
+ private widthsPinned;
3783
3847
  /**
3784
3848
  * Recomputes whether the inline filter row should collapse into the panel.
3785
3849
  * Closes the panel when returning to the wide layout so reopened state never
@@ -3796,19 +3860,74 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3796
3860
  getColumnSkeletonData(column: ColumnDefinition<T>): Partial<MnSkeletonProps>;
3797
3861
  getSortIcon(column: ColumnDefinition<T>): string;
3798
3862
  isSortable(column: ColumnDefinition<T>): boolean;
3799
- /** Rows shown per page on mobile (< md). Forced regardless of any configured pageSize. */
3800
- private static readonly MOBILE_PAGE_SIZE;
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;
3801
3870
  /** Page size to use at/above the `md` breakpoint (consumer's pageSize, or the user's selection). */
3802
3871
  private desktopPageSize;
3803
- /** True when the viewport is below the `md` (768px) breakpoint. */
3804
- private isMobileViewport;
3872
+ /** The effective column-width strategy, defaulting to `stable`. */
3873
+ get layoutMode(): 'auto' | 'fixed' | 'stable';
3805
3874
  /**
3806
- * Applies the breakpoint-appropriate page size: {@link MOBILE_PAGE_SIZE} below `md`,
3807
- * the desktop size at/above it. When the size actually changes, client-side tables
3808
- * re-slice locally and server-side tables ask the consumer to refetch, so the
3809
- * rendered rows update in every pagination mode (used at init and on window resize).
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.
3810
3879
  */
3811
- private applyResponsivePageSize;
3880
+ get widthsArePinned(): boolean;
3881
+ /**
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
3890
+ * readable. Only string cells have text to expose; template cells render their
3891
+ * own markup and are left alone.
3892
+ * @param column The column being rendered.
3893
+ * @param row The row being rendered.
3894
+ * @returns The full cell text, or `null` when there is nothing to expose.
3895
+ */
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;
3922
+ /**
3923
+ * Re-evaluate on a window resize too. The ResizeObserver covers every change to
3924
+ * the table's own box, but {@link isMobileViewport} reads the window, which can
3925
+ * change without the table's width following it (a fixed-width table, a modal
3926
+ * pinned to a max width).
3927
+ */
3928
+ protected onWindowResize(): void;
3929
+ /** Re-evaluate responsive page size and filter layout when the table is resized. */
3930
+ private onHostResize;
3812
3931
  /**
3813
3932
  * Resolves table-specific translation keys (column headers/filters) plus the
3814
3933
  * shared keys handled by the base.
@@ -3821,7 +3940,36 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3821
3940
  /** Returns the small-screen cell value for a column with cellSm defined. */
3822
3941
  getCellSmValue(column: ColumnDefinition<T>, row: T): string;
3823
3942
  trackByKey: (_index: number, column: ColumnDefinition<T>) => string;
3824
- readonly tableClasses = "w-full border-collapse overflow-y-hidden";
3943
+ /** True when the table is narrower than the filter-collapse breakpoint. */
3944
+ private isFilterViewport;
3945
+ /**
3946
+ * True when the **window** is below the `md` (768px) breakpoint.
3947
+ *
3948
+ * Deliberately viewport-based, unlike {@link isFilterViewport}: the forced
3949
+ * mobile page size exists to keep a phone screen scrollable, and it is paired
3950
+ * with the rows-per-page selector that mn-collection-pagination hides at the
3951
+ * same viewport breakpoint. Measuring the table's own width instead would let
3952
+ * the two disagree — a 700px table on a desktop would be pinned to the mobile
3953
+ * row count while still offering the selector that overrides it.
3954
+ */
3955
+ private isMobileViewport;
3956
+ /**
3957
+ * The table's own rendered width, which every responsive decision is made
3958
+ * against — the same width the `@container` queries in the template use, so
3959
+ * the TS and CSS halves of the responsive layout can never disagree.
3960
+ *
3961
+ * Falls back to the window width before the host has been laid out (and in
3962
+ * SSR), which is the closest available approximation at that point.
3963
+ * @returns The width in CSS pixels.
3964
+ */
3965
+ private measuredWidth;
3966
+ /**
3967
+ * Applies the breakpoint-appropriate page size: capped at {@link MOBILE_PAGE_SIZE}
3968
+ * below `md`, the desktop size at/above it. When the size actually changes, client-side tables
3969
+ * re-slice locally and server-side tables ask the consumer to refetch, so the
3970
+ * rendered rows update in every pagination mode (used at init and on window resize).
3971
+ */
3972
+ private applyResponsivePageSize;
3825
3973
  get totalColumnCount(): number;
3826
3974
  /**
3827
3975
  * Hands the active filters to the consumer. Locks the body height first so the
@@ -3889,6 +4037,14 @@ declare function matchesColumnFilter<T>(column: ColumnDefinition<T>, row: T, val
3889
4037
  * Attribute directive that applies responsive-hiding classes to table cells/headers.
3890
4038
  * Hides the element by default and shows it as `table-cell` at the specified breakpoint.
3891
4039
  *
4040
+ * The breakpoints are **container** queries against the table's own width, not the
4041
+ * viewport: a table inside a modal (or any narrow column) is far narrower than the
4042
+ * window, so viewport breakpoints would reveal columns the table has no room for.
4043
+ * mn-table marks its chrome `@container` for exactly this.
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
+ *
3892
4048
  * Uses a static class map so Tailwind CSS can detect the full class names at build time.
3893
4049
  *
3894
4050
  * Usage: `<td [mnHiddenBelow]="column.hiddenBelow">`
@@ -3899,7 +4055,18 @@ declare class MnHiddenBelowDirective implements OnChanges {
3899
4055
  private readonly el;
3900
4056
  private readonly renderer;
3901
4057
  private appliedClasses;
3902
- /** 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
+ */
3903
4070
  private readonly classMap;
3904
4071
  ngOnChanges(): void;
3905
4072
  static ɵfac: i0.ɵɵFactoryDeclaration<MnHiddenBelowDirective, never>;