mn-angular-lib 1.0.129 → 1.0.131

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.129",
3
+ "version": "1.0.131",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.1.3",
6
6
  "@angular/core": "^21.1.3"
@@ -2621,10 +2621,24 @@ declare class MnMultiSelect implements OnInit {
2621
2621
  private readonly lang;
2622
2622
  private readonly destroyRef;
2623
2623
  private readonly renderer;
2624
+ private readonly cdr;
2624
2625
  /** Reference to the trigger element for positioning the dropdown */
2625
2626
  triggerRef: ElementRef<HTMLElement>;
2626
2627
  /** The panel element currently moved into `document.body`, if any. */
2627
2628
  private movedPanel;
2629
+ /**
2630
+ * Watches the trigger while the panel is open. The panel lives in `document.body`,
2631
+ * so it survives its own trigger being hidden by an ancestor — e.g. a wizard step
2632
+ * or a tab that is switched away with `display: none` instead of being destroyed.
2633
+ * When the trigger stops being visible the panel must go with it.
2634
+ */
2635
+ private visibilityObserver;
2636
+ /**
2637
+ * Capture-phase scroll listener installed while open. `window:scroll` only fires for
2638
+ * the document scroller, so scrolling an inner container (a modal body, a scrollable
2639
+ * card) used to leave the portalled panel floating at its stale coordinates.
2640
+ */
2641
+ private scrollCapture;
2628
2642
  /**
2629
2643
  * The dropdown panel element, queried while it is rendered by the `@if` block.
2630
2644
  * The setter relocates the panel to `document.body` so that its `position: fixed`
@@ -2657,10 +2671,25 @@ declare class MnMultiSelect implements OnInit {
2657
2671
  registerOnTouched(fn: () => void): void;
2658
2672
  setDisabledState(isDisabled: boolean): void;
2659
2673
  toggle(): void;
2660
- /** Calculates the fixed position for the dropdown based on the trigger element */
2661
- private updateDropdownPosition;
2662
2674
  /** Closes the dropdown on Escape for keyboard accessibility. */
2663
2675
  onEscape(): void;
2676
+ /** Closes the dropdown when the page or a scrollable parent is scrolled */
2677
+ onWindowScrollOrResize(): void;
2678
+ /**
2679
+ * The single close path. Every trigger (outside click, Escape, scroll, resize, the
2680
+ * trigger being hidden) funnels through here so the open-only listeners are always
2681
+ * torn down with the panel and never leak.
2682
+ */
2683
+ private close;
2684
+ /** Calculates the fixed position for the dropdown based on the trigger element */
2685
+ private updateDropdownPosition;
2686
+ /**
2687
+ * Starts the open-only watchers: an `IntersectionObserver` on the trigger (closes the
2688
+ * panel as soon as the trigger stops being rendered/visible) and a capture-phase
2689
+ * `scroll` listener (closes it when any ancestor scroller moves under it). Scrolls
2690
+ * that originate inside the panel's own option list are ignored.
2691
+ */
2692
+ private startWatchingTrigger;
2664
2693
  /**
2665
2694
  * Move the dropdown panel to `document.body` when it appears, and detach it when
2666
2695
  * the query clears. Appending to the body root makes the panel immune to ancestor
@@ -2668,8 +2697,8 @@ declare class MnMultiSelect implements OnInit {
2668
2697
  * and the panel stays under its trigger. Idempotent and safe to call with `null`.
2669
2698
  */
2670
2699
  private relocateDropdown;
2671
- /** Closes the dropdown when the page or a scrollable parent is scrolled */
2672
- onWindowScrollOrResize(): void;
2700
+ /** Tears down the watchers installed by `startWatchingTrigger`. Idempotent. */
2701
+ private stopWatchingTrigger;
2673
2702
  toggleOption(option: MnMultiSelectOption): void;
2674
2703
  removeOption(option: MnMultiSelectOption, event: Event): void;
2675
2704
  isSelected(option: MnMultiSelectOption): boolean;
@@ -3325,7 +3354,24 @@ declare abstract class MnCollectionBase<T, DS extends MnCollectionDataSource<T>>
3325
3354
  /** Client-side search filtering shared by list and grid. */
3326
3355
  protected applySearchFilter(items: T[]): T[];
3327
3356
  protected processLoadedRows(rows: T[]): void;
3328
- 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;
3329
3375
  static ɵfac: i0.ɵɵFactoryDeclaration<MnCollectionBase<any, any>, never>;
3330
3376
  static ɵdir: i0.ɵɵDirectiveDeclaration<MnCollectionBase<any, any>, never, never, { "dataSource": { "alias": "dataSource"; "required": false; }; }, {}, never, never, true, never>;
3331
3377
  }
@@ -3427,6 +3473,23 @@ type TableAppearance = {
3427
3473
  hover?: boolean;
3428
3474
  compact?: boolean;
3429
3475
  bordered?: boolean;
3476
+ /**
3477
+ * How column widths are computed.
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';
3430
3493
  };
3431
3494
  /**
3432
3495
  * The control rendered for a column filter, and the shape of the value it produces:
@@ -3673,7 +3736,16 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3673
3736
  private readonly filterDebounce;
3674
3737
  /** The open filter popover, used to tell inside clicks from outside ones. */
3675
3738
  private filterPopover?;
3676
- constructor();
3739
+ /**
3740
+ * Most rows shown per page on mobile (< md). A **cap**, not an override: a data
3741
+ * source asking for fewer rows keeps its own size. Raising a small page size on
3742
+ * a phone is the opposite of what it is for — it pushes the paginator below the
3743
+ * fold, which is most damaging inside a modal, where the sheet is already short
3744
+ * and its footer is pinned over the bottom of the table.
3745
+ */
3746
+ private static readonly MOBILE_PAGE_SIZE;
3747
+ /** The component's own element, measured for every responsive decision. */
3748
+ private readonly host;
3677
3749
  /** Whether the consumer owns filtering (server-side), mirroring {@link isServerSearched}. */
3678
3750
  get isServerFiltered(): boolean;
3679
3751
  /** Every column filter that is actually set, in column order. */
@@ -3745,12 +3817,16 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3745
3817
  get clearFiltersButtonLabel(): string;
3746
3818
  /** Opens/closes the stacked filter panel shown on small screens. */
3747
3819
  toggleFiltersPanel(): void;
3748
- /** Re-evaluate responsive page size and filter layout when the viewport changes. */
3749
- protected onWindowResize(): void;
3820
+ private readonly baseTableClasses;
3821
+ constructor();
3750
3822
  /** Sets sort/filter state seeded from the data source before the first filter pass. */
3751
3823
  protected beforeInitialFilter(): void;
3752
- /** True when the viewport is below the filter-collapse breakpoint. */
3753
- private isFilterViewport;
3824
+ /**
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.
3828
+ */
3829
+ get tableClasses(): string;
3754
3830
  /**
3755
3831
  * Recomputes whether the inline filter row should collapse into the panel.
3756
3832
  * Closes the panel when returning to the wide layout so reopened state never
@@ -3767,19 +3843,28 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3767
3843
  getColumnSkeletonData(column: ColumnDefinition<T>): Partial<MnSkeletonProps>;
3768
3844
  getSortIcon(column: ColumnDefinition<T>): string;
3769
3845
  isSortable(column: ColumnDefinition<T>): boolean;
3770
- /** Rows shown per page on mobile (< md). Forced regardless of any configured pageSize. */
3771
- private static readonly MOBILE_PAGE_SIZE;
3846
+ /** Whether column widths are content-independent (see {@link TableAppearance.layout}). */
3847
+ get isFixedLayout(): boolean;
3772
3848
  /** Page size to use at/above the `md` breakpoint (consumer's pageSize, or the user's selection). */
3773
3849
  private desktopPageSize;
3774
- /** True when the viewport is below the `md` (768px) breakpoint. */
3775
- private isMobileViewport;
3776
3850
  /**
3777
- * Applies the breakpoint-appropriate page size: {@link MOBILE_PAGE_SIZE} below `md`,
3778
- * the desktop size at/above it. When the size actually changes, client-side tables
3779
- * re-slice locally and server-side tables ask the consumer to refetch, so the
3780
- * rendered rows update in every pagination mode (used at init and on window resize).
3851
+ * The `title` tooltip for a cell, so text truncated by the fixed layout stays
3852
+ * readable. Only string cells have text to expose; template cells render their
3853
+ * own markup and are left alone.
3854
+ * @param column The column being rendered.
3855
+ * @param row The row being rendered.
3856
+ * @returns The full cell text, or `null` when there is nothing to expose.
3781
3857
  */
3782
- private applyResponsivePageSize;
3858
+ cellTitle(column: ColumnDefinition<T>, row: T): string | null;
3859
+ /**
3860
+ * Re-evaluate on a window resize too. The ResizeObserver covers every change to
3861
+ * the table's own box, but {@link isMobileViewport} reads the window, which can
3862
+ * change without the table's width following it (a fixed-width table, a modal
3863
+ * pinned to a max width).
3864
+ */
3865
+ protected onWindowResize(): void;
3866
+ /** Re-evaluate responsive page size and filter layout when the table is resized. */
3867
+ private onHostResize;
3783
3868
  /**
3784
3869
  * Resolves table-specific translation keys (column headers/filters) plus the
3785
3870
  * shared keys handled by the base.
@@ -3792,7 +3877,36 @@ declare class MnTable<T = object> extends MnSelectableCollectionBase<T, TableDat
3792
3877
  /** Returns the small-screen cell value for a column with cellSm defined. */
3793
3878
  getCellSmValue(column: ColumnDefinition<T>, row: T): string;
3794
3879
  trackByKey: (_index: number, column: ColumnDefinition<T>) => string;
3795
- readonly tableClasses = "w-full border-collapse overflow-y-hidden";
3880
+ /** True when the table is narrower than the filter-collapse breakpoint. */
3881
+ private isFilterViewport;
3882
+ /**
3883
+ * True when the **window** is below the `md` (768px) breakpoint.
3884
+ *
3885
+ * Deliberately viewport-based, unlike {@link isFilterViewport}: the forced
3886
+ * mobile page size exists to keep a phone screen scrollable, and it is paired
3887
+ * with the rows-per-page selector that mn-collection-pagination hides at the
3888
+ * same viewport breakpoint. Measuring the table's own width instead would let
3889
+ * the two disagree — a 700px table on a desktop would be pinned to the mobile
3890
+ * row count while still offering the selector that overrides it.
3891
+ */
3892
+ private isMobileViewport;
3893
+ /**
3894
+ * The table's own rendered width, which every responsive decision is made
3895
+ * against — the same width the `@container` queries in the template use, so
3896
+ * the TS and CSS halves of the responsive layout can never disagree.
3897
+ *
3898
+ * Falls back to the window width before the host has been laid out (and in
3899
+ * SSR), which is the closest available approximation at that point.
3900
+ * @returns The width in CSS pixels.
3901
+ */
3902
+ private measuredWidth;
3903
+ /**
3904
+ * Applies the breakpoint-appropriate page size: capped at {@link MOBILE_PAGE_SIZE}
3905
+ * below `md`, the desktop size at/above it. When the size actually changes, client-side tables
3906
+ * re-slice locally and server-side tables ask the consumer to refetch, so the
3907
+ * rendered rows update in every pagination mode (used at init and on window resize).
3908
+ */
3909
+ private applyResponsivePageSize;
3796
3910
  get totalColumnCount(): number;
3797
3911
  /**
3798
3912
  * Hands the active filters to the consumer. Locks the body height first so the
@@ -3860,6 +3974,11 @@ declare function matchesColumnFilter<T>(column: ColumnDefinition<T>, row: T, val
3860
3974
  * Attribute directive that applies responsive-hiding classes to table cells/headers.
3861
3975
  * Hides the element by default and shows it as `table-cell` at the specified breakpoint.
3862
3976
  *
3977
+ * The breakpoints are **container** queries against the table's own width, not the
3978
+ * viewport: a table inside a modal (or any narrow column) is far narrower than the
3979
+ * window, so viewport breakpoints would reveal columns the table has no room for.
3980
+ * mn-table marks its chrome `@container` for exactly this.
3981
+ *
3863
3982
  * Uses a static class map so Tailwind CSS can detect the full class names at build time.
3864
3983
  *
3865
3984
  * Usage: `<td [mnHiddenBelow]="column.hiddenBelow">`
@@ -5306,6 +5425,13 @@ declare class MnWizardBodyComponent implements OnInit, AfterViewInit, OnDestroy
5306
5425
  modalRef: MnModalRef<WizardResult>;
5307
5426
  formBodies: QueryList<MnFormBodyComponent>;
5308
5427
  stepWrappers: QueryList<ElementRef<HTMLElement>>;
5428
+ /**
5429
+ * The step body's scroll container. Every step renders into this one element
5430
+ * (inactive steps are hidden, not destroyed), so its scroll offset is shared —
5431
+ * without an explicit reset, arriving on a new step lands you wherever the
5432
+ * previous step was scrolled to. See {@link setCurrentStep}.
5433
+ */
5434
+ stepScroller?: ElementRef<HTMLElement>;
5309
5435
  currentStepId: ModalStepId;
5310
5436
  /**
5311
5437
  * Title of the currently active step, mirrored into a signal so the modal