mn-angular-lib 1.0.121 → 1.0.122

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.
@@ -9482,13 +9482,22 @@ const DEFAULT_SKELETON_TAB_COUNT = 3;
9482
9482
  * Supports translation keys for labels via MnTranslatePipe.
9483
9483
  */
9484
9484
  class MnTabComponent {
9485
+ /** The horizontally-scrolling wrapper the edge fade is painted onto. */
9486
+ scrollContainer;
9487
+ /** How far the fade reaches in from each overflowing edge. */
9488
+ static FADE = '2rem';
9485
9489
  /** Data source containing tab items and default active index. */
9486
9490
  dataSource;
9487
9491
  /**
9488
9492
  * Whether to enable horizontal scrolling when items overflow.
9489
- * If true, tabs will scroll horizontally instead of shrinking too much.
9493
+ * When true, tabs scroll horizontally instead of overflowing their container.
9494
+ *
9495
+ * Defaults to `true`: the tab bar never wraps (`flex-nowrap`), so without
9496
+ * scrolling an overflowing bar would clip its last tabs or push the page width
9497
+ * on narrow screens. When the tabs already fit, `overflow-x-auto` is a no-op,
9498
+ * so this default only ever changes behaviour for the overflow case it fixes.
9490
9499
  */
9491
- scrollable = false;
9500
+ scrollable = true;
9492
9501
  /**
9493
9502
  * Whether tabs should stretch to fill the available width.
9494
9503
  * Defaults to false, so tabs only take as much space as their content.
@@ -9498,6 +9507,8 @@ class MnTabComponent {
9498
9507
  activeChange = new EventEmitter();
9499
9508
  /** The currently active tab item. */
9500
9509
  currentActive;
9510
+ /** Watches the wrapper and the tab row so the fade re-evaluates on width or content changes. */
9511
+ resizeObserver;
9501
9512
  /**
9502
9513
  * Whether the tab bar is loading and should render skeleton tabs, from
9503
9514
  * {@link MnTabDataSource.state}.
@@ -9526,6 +9537,51 @@ class MnTabComponent {
9526
9537
  ngDoCheck() {
9527
9538
  this.syncActiveTab();
9528
9539
  }
9540
+ /**
9541
+ * Starts watching the scroll wrapper so the edge fade stays honest. A scroll
9542
+ * moves the fade to whichever side now hides tabs; a resize (viewport change)
9543
+ * or a change to the tab row's width (tabs added, relabelled, skeleton →
9544
+ * loaded) re-checks whether either edge overflows at all.
9545
+ */
9546
+ ngAfterViewInit() {
9547
+ const el = this.scrollContainer?.nativeElement;
9548
+ if (!el)
9549
+ return;
9550
+ this.resizeObserver = new ResizeObserver(() => this.updateEdgeFades());
9551
+ this.resizeObserver.observe(el);
9552
+ if (el.firstElementChild)
9553
+ this.resizeObserver.observe(el.firstElementChild);
9554
+ this.updateEdgeFades();
9555
+ }
9556
+ ngOnDestroy() {
9557
+ this.resizeObserver?.disconnect();
9558
+ }
9559
+ /**
9560
+ * Paints a fade over whichever edge has tabs scrolled out of view — a soft
9561
+ * dissolve that reads as "more this way", the affordance a hidden scrollbar
9562
+ * otherwise costs us. Uses a mask (content → transparent) rather than a
9563
+ * background-coloured overlay, so it needs no knowledge of the theme.
9564
+ */
9565
+ updateEdgeFades() {
9566
+ const el = this.scrollContainer?.nativeElement;
9567
+ if (!el)
9568
+ return;
9569
+ const fadeStart = el.scrollLeft > 1;
9570
+ const fadeEnd = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
9571
+ const f = MnTabComponent.FADE;
9572
+ let mask = '';
9573
+ if (this.scrollable && fadeStart && fadeEnd) {
9574
+ mask = `linear-gradient(to right, transparent 0, #000 ${f}, #000 calc(100% - ${f}), transparent 100%)`;
9575
+ }
9576
+ else if (this.scrollable && fadeStart) {
9577
+ mask = `linear-gradient(to right, transparent 0, #000 ${f}, #000 100%)`;
9578
+ }
9579
+ else if (this.scrollable && fadeEnd) {
9580
+ mask = `linear-gradient(to right, #000 0, #000 calc(100% - ${f}), transparent 100%)`;
9581
+ }
9582
+ el.style.maskImage = mask;
9583
+ el.style.setProperty('-webkit-mask-image', mask);
9584
+ }
9529
9585
  /**
9530
9586
  * Sets the given tab item as active, invoking deactivate/activate callbacks.
9531
9587
  * @param item - The tab item to activate.
@@ -9567,12 +9623,15 @@ class MnTabComponent {
9567
9623
  this.currentActive = items[index];
9568
9624
  }
9569
9625
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9570
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnTabComponent, isStandalone: true, selector: "mn-tab", inputs: { dataSource: "dataSource", scrollable: "scrollable", justified: "justified" }, outputs: { activeChange: "activeChange" }, ngImport: i0, template: "<div class=\"mb-10\">\n <div\n class=\"flex justify-start scrollbar-hide\"\n [class.overflow-x-auto]=\"scrollable\"\n [class.overflow-y-hidden]=\"scrollable\"\n >\n <div\n role=\"tablist\"\n class=\"tabs flex flex-nowrap -mb-[1px] border-b border-base-300\"\n [class.w-full]=\"justified\"\n >\n @if (isLoadingState) {\n @for (i of skeletonTabs; track i) {\n <div\n [class.flex-1]=\"justified\"\n class=\"tab px-4 py-2 border-b-2 border-transparent flex items-center justify-center\"\n >\n <mn-skeleton [data]=\"{ shape: 'rectangle', width: '4.5rem', height: '1rem' }\"></mn-skeleton>\n </div>\n }\n } @else {\n @for (item of dataSource.items; track item.label) {\n <div\n (click)=\"setActive(item)\"\n (keyup.enter)=\"setActive(item)\"\n (keyup.space)=\"setActive(item)\"\n [attr.aria-selected]=\"currentActive === item\"\n [class.border-primary]=\"currentActive === item\"\n [class.border-transparent]=\"currentActive !== item\"\n [class.flex-1]=\"justified\"\n [class.font-bold]=\"currentActive === item\"\n [class.text-base-content]=\"currentActive !== item\"\n [class.text-primary]=\"currentActive === item\"\n class=\"tab px-4 py-2 border-b-2 cursor-pointer select-none transition-colors whitespace-nowrap text-center flex items-center gap-2\"\n role=\"tab\"\n tabindex=\"0\"\n >\n {{ item.label | mnTranslate }}\n @let badge = getBadge(item);\n @if (badge && badge > 0) {\n <span [data]=\"{ size: 'sm', color: 'accent', variant: 'fill' }\" mnBadge>{{ badge }}</span>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: MnBadge, selector: "span[mnBadge]", inputs: ["data"] }, { kind: "component", type: MnSkeleton, selector: "mn-skeleton", inputs: ["data"] }, { kind: "pipe", type: MnTranslatePipe, name: "mnTranslate" }] });
9626
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: MnTabComponent, isStandalone: true, selector: "mn-tab", inputs: { dataSource: "dataSource", scrollable: "scrollable", justified: "justified" }, outputs: { activeChange: "activeChange" }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }], ngImport: i0, template: "<div class=\"mb-10\">\n <div\n #scrollContainer\n (scroll)=\"updateEdgeFades()\"\n class=\"flex justify-start scrollbar-hide\"\n [class.overflow-x-auto]=\"scrollable\"\n [class.overflow-y-hidden]=\"scrollable\"\n >\n <div\n role=\"tablist\"\n class=\"tabs flex flex-nowrap -mb-[1px] border-b border-base-300\"\n [class.w-full]=\"justified\"\n >\n @if (isLoadingState) {\n @for (i of skeletonTabs; track i) {\n <div\n [class.flex-1]=\"justified\"\n class=\"tab px-4 py-2 border-b-2 border-transparent flex items-center justify-center\"\n >\n <mn-skeleton [data]=\"{ shape: 'rectangle', width: '4.5rem', height: '1rem' }\"></mn-skeleton>\n </div>\n }\n } @else {\n @for (item of dataSource.items; track item.label) {\n <div\n (click)=\"setActive(item)\"\n (keyup.enter)=\"setActive(item)\"\n (keyup.space)=\"setActive(item)\"\n [attr.aria-selected]=\"currentActive === item\"\n [class.border-primary]=\"currentActive === item\"\n [class.border-transparent]=\"currentActive !== item\"\n [class.flex-1]=\"justified\"\n [class.font-bold]=\"currentActive === item\"\n [class.text-base-content]=\"currentActive !== item\"\n [class.text-primary]=\"currentActive === item\"\n class=\"tab px-4 py-2 border-b-2 cursor-pointer select-none transition-colors whitespace-nowrap text-center flex items-center gap-2\"\n role=\"tab\"\n tabindex=\"0\"\n >\n {{ item.label | mnTranslate }}\n @let badge = getBadge(item);\n @if (badge && badge > 0) {\n <span [data]=\"{ size: 'sm', color: 'accent', variant: 'fill' }\" mnBadge>{{ badge }}</span>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: MnBadge, selector: "span[mnBadge]", inputs: ["data"] }, { kind: "component", type: MnSkeleton, selector: "mn-skeleton", inputs: ["data"] }, { kind: "pipe", type: MnTranslatePipe, name: "mnTranslate" }] });
9571
9627
  }
9572
9628
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnTabComponent, decorators: [{
9573
9629
  type: Component,
9574
- args: [{ selector: 'mn-tab', standalone: true, imports: [MnTranslatePipe, CommonModule, MnBadge, MnSkeleton], template: "<div class=\"mb-10\">\n <div\n class=\"flex justify-start scrollbar-hide\"\n [class.overflow-x-auto]=\"scrollable\"\n [class.overflow-y-hidden]=\"scrollable\"\n >\n <div\n role=\"tablist\"\n class=\"tabs flex flex-nowrap -mb-[1px] border-b border-base-300\"\n [class.w-full]=\"justified\"\n >\n @if (isLoadingState) {\n @for (i of skeletonTabs; track i) {\n <div\n [class.flex-1]=\"justified\"\n class=\"tab px-4 py-2 border-b-2 border-transparent flex items-center justify-center\"\n >\n <mn-skeleton [data]=\"{ shape: 'rectangle', width: '4.5rem', height: '1rem' }\"></mn-skeleton>\n </div>\n }\n } @else {\n @for (item of dataSource.items; track item.label) {\n <div\n (click)=\"setActive(item)\"\n (keyup.enter)=\"setActive(item)\"\n (keyup.space)=\"setActive(item)\"\n [attr.aria-selected]=\"currentActive === item\"\n [class.border-primary]=\"currentActive === item\"\n [class.border-transparent]=\"currentActive !== item\"\n [class.flex-1]=\"justified\"\n [class.font-bold]=\"currentActive === item\"\n [class.text-base-content]=\"currentActive !== item\"\n [class.text-primary]=\"currentActive === item\"\n class=\"tab px-4 py-2 border-b-2 cursor-pointer select-none transition-colors whitespace-nowrap text-center flex items-center gap-2\"\n role=\"tab\"\n tabindex=\"0\"\n >\n {{ item.label | mnTranslate }}\n @let badge = getBadge(item);\n @if (badge && badge > 0) {\n <span [data]=\"{ size: 'sm', color: 'accent', variant: 'fill' }\" mnBadge>{{ badge }}</span>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n" }]
9575
- }], propDecorators: { dataSource: [{
9630
+ args: [{ selector: 'mn-tab', standalone: true, imports: [MnTranslatePipe, CommonModule, MnBadge, MnSkeleton], template: "<div class=\"mb-10\">\n <div\n #scrollContainer\n (scroll)=\"updateEdgeFades()\"\n class=\"flex justify-start scrollbar-hide\"\n [class.overflow-x-auto]=\"scrollable\"\n [class.overflow-y-hidden]=\"scrollable\"\n >\n <div\n role=\"tablist\"\n class=\"tabs flex flex-nowrap -mb-[1px] border-b border-base-300\"\n [class.w-full]=\"justified\"\n >\n @if (isLoadingState) {\n @for (i of skeletonTabs; track i) {\n <div\n [class.flex-1]=\"justified\"\n class=\"tab px-4 py-2 border-b-2 border-transparent flex items-center justify-center\"\n >\n <mn-skeleton [data]=\"{ shape: 'rectangle', width: '4.5rem', height: '1rem' }\"></mn-skeleton>\n </div>\n }\n } @else {\n @for (item of dataSource.items; track item.label) {\n <div\n (click)=\"setActive(item)\"\n (keyup.enter)=\"setActive(item)\"\n (keyup.space)=\"setActive(item)\"\n [attr.aria-selected]=\"currentActive === item\"\n [class.border-primary]=\"currentActive === item\"\n [class.border-transparent]=\"currentActive !== item\"\n [class.flex-1]=\"justified\"\n [class.font-bold]=\"currentActive === item\"\n [class.text-base-content]=\"currentActive !== item\"\n [class.text-primary]=\"currentActive === item\"\n class=\"tab px-4 py-2 border-b-2 cursor-pointer select-none transition-colors whitespace-nowrap text-center flex items-center gap-2\"\n role=\"tab\"\n tabindex=\"0\"\n >\n {{ item.label | mnTranslate }}\n @let badge = getBadge(item);\n @if (badge && badge > 0) {\n <span [data]=\"{ size: 'sm', color: 'accent', variant: 'fill' }\" mnBadge>{{ badge }}</span>\n }\n </div>\n }\n }\n </div>\n </div>\n</div>\n" }]
9631
+ }], propDecorators: { scrollContainer: [{
9632
+ type: ViewChild,
9633
+ args: ['scrollContainer']
9634
+ }], dataSource: [{
9576
9635
  type: Input
9577
9636
  }], scrollable: [{
9578
9637
  type: Input