@taiga-ui/kit
Version:
Taiga UI Angular main components kit
198 lines (190 loc) • 13.2 kB
JavaScript
import { TuiItem } from '@taiga-ui/cdk/directives/item';
import { NgTemplateOutlet } from '@angular/common';
import * as i0 from '@angular/core';
import { input, computed, Directive, inject, Injectable, contentChild, TemplateRef, contentChildren, ChangeDetectionStrategy, Component } from '@angular/core';
import { outputFromObservable, toSignal } from '@angular/core/rxjs-interop';
import { WaMutationObserverService, WA_MUTATION_OBSERVER_INIT } from '@ng-web-apis/mutation-observer';
import { WaResizeObserverService } from '@ng-web-apis/resize-observer';
import { tuiInjectElement } from '@taiga-ui/cdk/utils/dom';
import { Subject, Observable, merge, debounceTime, map, distinctUntilChanged, share } from 'rxjs';
import { tuiZonefreeScheduler, tuiZoneOptimized } from '@taiga-ui/cdk/observables';
import { tuiClamp } from '@taiga-ui/cdk/utils/math';
class TuiItemsWithMoreDirective {
constructor() {
this.el = tuiInjectElement();
this.itemsLimit = input(Infinity);
this.required = input(-1);
this.linesLimit = input(1);
this.side = input('end');
this.align = computed(() => (this.linesLimit() > 1 ? 'end' : this.side()));
this.change$ = new Subject();
}
ngOnChanges() {
this.change$.next();
}
maxWidth() {
return Math.max(...Array.from(this.el.children, ({ clientWidth }) => clientWidth));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.21", type: TuiItemsWithMoreDirective, isStandalone: true, inputs: { itemsLimit: { classPropertyName: "itemsLimit", publicName: "itemsLimit", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, linesLimit: { classPropertyName: "linesLimit", publicName: "linesLimit", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class._multiline": "linesLimit() > 1", "style.--t-min-width.px": "maxWidth()" } }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreDirective, decorators: [{
type: Directive,
args: [{
host: {
'[class._multiline]': 'linesLimit() > 1',
'[style.--t-min-width.px]': 'maxWidth()',
},
}]
}] });
class TuiItemsWithMoreService extends Observable {
constructor() {
super((subscriber) => this.stream$.subscribe(subscriber));
this.el = tuiInjectElement();
this.directive = inject(TuiItemsWithMoreDirective);
this.stream$ = merge(this.directive.change$, inject(WaMutationObserverService, { self: true }), inject(WaResizeObserverService, { self: true })).pipe(debounceTime(0, tuiZonefreeScheduler()), map(() => this.directive.linesLimit() > 1
? this.getOverflowIndexMultiline()
: this.getOverflowIndex(Array.from(this.el.children))), distinctUntilChanged(), tuiZoneOptimized(), share());
}
getOverflowIndex(children) {
const { align, itemsLimit } = this.directive;
const { clientWidth } = this.el;
const items = Array.from(children, ({ clientWidth }) => clientWidth);
const index = align() === 'start' ? 0 : items.length - 1;
const more = children[index]?.tagName === 'SPAN' ? (items[index] ?? 0) : 0;
const total = items.reduce((sum, width) => sum + width, 0) - more;
if (total <= clientWidth && itemsLimit() >= items.length) {
return align() === 'end' ? itemsLimit() : 0;
}
return align() === 'start'
? this.getIndexStart(items, total, more)
: this.getIndexEnd(items, total, more);
}
getIndexStart(items, total, more) {
const { required, itemsLimit } = this.directive;
const { clientWidth } = this.el;
const min = Number.isFinite(itemsLimit()) ? items.length - itemsLimit() - 1 : 0;
const last = items.length - 1;
const mandatory = required() === -1 ? last : required();
for (let i = 1; i < last; i++) {
if (i === mandatory + 1) {
continue;
}
total -= items[i] ?? 0;
if (total + more <= clientWidth) {
return tuiClamp(i, mandatory < min ? min + 1 : min, items.length);
}
}
return items.length;
}
getIndexEnd(items, total, more) {
const { required, itemsLimit } = this.directive;
const { clientWidth } = this.el;
const max = itemsLimit() > required() ? itemsLimit() - 1 : itemsLimit() - 2;
const last = items.length - 1;
const mandatory = required() === -1 ? 0 : required;
for (let i = last - 1; i > 0; i--) {
if (i === mandatory) {
continue;
}
total -= items[i] ?? 0;
if (total + more <= clientWidth) {
return tuiClamp(i - 1, -1, max);
}
}
return -1;
}
getOverflowIndexMultiline() {
const { children } = this.el;
const { linesLimit, itemsLimit } = this.directive;
const items = Array.from(children);
const rows = new Set(items.map((item) => item.offsetTop));
const offset = Array.from(rows)[linesLimit() - 1];
const firstItemLastRow = items.findIndex((i) => i.offsetTop === offset);
const lastRow = items.slice(firstItemLastRow);
const index = firstItemLastRow + this.getOverflowIndex(lastRow);
return Math.min(itemsLimit() - 1, index);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreService, decorators: [{
type: Injectable
}], ctorParameters: () => [] });
class TuiMore {
static ngTemplateContextGuard(_dir, _ctx) {
return true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiMore, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.21", type: TuiMore, isStandalone: true, selector: "[tuiMore]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiMore, decorators: [{
type: Directive,
args: [{ selector: '[tuiMore]' }]
}] });
class TuiItemsWithMoreComponent {
constructor() {
this.service = inject(TuiItemsWithMoreService);
this.directive = inject(TuiItemsWithMoreDirective);
this.more = contentChild(TuiMore, { read: TemplateRef });
this.items = contentChildren(TuiItem, {
read: TemplateRef,
descendants: true,
});
this.isMoreHidden = computed((index = this.lastIndex()) => (index >= this.items().length - 1 && this.directive.align() === 'end') ||
(!index && this.directive.align() === 'start'));
this.lastIndexChange = outputFromObservable(this.service);
this.lastIndex = toSignal(this.service, { initialValue: 0 });
}
isHidden(index) {
const { align, required } = this.directive;
return ((index > this.lastIndex() && index !== required() && align() === 'end') ||
(index < this.lastIndex() && index !== required() && align() === 'start'));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: TuiItemsWithMoreComponent, isStandalone: true, selector: "tui-items-with-more", outputs: { lastIndexChange: "lastIndexChange" }, providers: [
WaMutationObserverService,
WaResizeObserverService,
TuiItemsWithMoreService,
{
provide: WA_MUTATION_OBSERVER_INIT,
useValue: {
childList: true,
characterData: true,
subtree: true,
},
},
], queries: [{ propertyName: "more", first: true, predicate: TuiMore, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "items", predicate: TuiItem, descendants: true, read: TemplateRef, isSignal: true }], hostDirectives: [{ directive: TuiItemsWithMoreDirective, inputs: ["itemsLimit", "itemsLimit", "required", "required", "side", "side", "linesLimit", "linesLimit"] }], ngImport: i0, template: "@if (directive.side() === 'start') {\n <ng-container [ngTemplateOutlet]=\"template\" />\n}\n@for (item of items(); track item) {\n <div\n class=\"t-item\"\n [class.t-item_hidden]=\"isHidden($index)\"\n >\n <ng-container *ngTemplateOutlet=\"item\" />\n </div>\n}\n@if (directive.side() === 'end') {\n <ng-container [ngTemplateOutlet]=\"template\" />\n}\n<ng-template #template>\n @if (!isMoreHidden()) {\n <span class=\"t-item t-item_more\">\n <ng-container\n [ngTemplateOutlet]=\"more() || null\"\n [ngTemplateOutletContext]=\"{$implicit: lastIndex()}\"\n />\n </span>\n }\n</ng-template>\n", styles: [":host{position:relative;display:flex;min-inline-size:0;flex:1;align-items:center;white-space:nowrap;gap:0!important}:host._multiline{flex-wrap:wrap}.t-item{flex:0 0 auto;max-inline-size:100%}.t-item_hidden{position:absolute;inset-block-end:0;visibility:hidden}:host._multiline .t-item_more:not(:empty){min-inline-size:var(--t-min-width, 0)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: TuiItemsWithMoreComponent, decorators: [{
type: Component,
args: [{ selector: 'tui-items-with-more', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
WaMutationObserverService,
WaResizeObserverService,
TuiItemsWithMoreService,
{
provide: WA_MUTATION_OBSERVER_INIT,
useValue: {
childList: true,
characterData: true,
subtree: true,
},
},
], hostDirectives: [
{
directive: TuiItemsWithMoreDirective,
inputs: ['itemsLimit', 'required', 'side', 'linesLimit'],
},
], template: "@if (directive.side() === 'start') {\n <ng-container [ngTemplateOutlet]=\"template\" />\n}\n@for (item of items(); track item) {\n <div\n class=\"t-item\"\n [class.t-item_hidden]=\"isHidden($index)\"\n >\n <ng-container *ngTemplateOutlet=\"item\" />\n </div>\n}\n@if (directive.side() === 'end') {\n <ng-container [ngTemplateOutlet]=\"template\" />\n}\n<ng-template #template>\n @if (!isMoreHidden()) {\n <span class=\"t-item t-item_more\">\n <ng-container\n [ngTemplateOutlet]=\"more() || null\"\n [ngTemplateOutletContext]=\"{$implicit: lastIndex()}\"\n />\n </span>\n }\n</ng-template>\n", styles: [":host{position:relative;display:flex;min-inline-size:0;flex:1;align-items:center;white-space:nowrap;gap:0!important}:host._multiline{flex-wrap:wrap}.t-item{flex:0 0 auto;max-inline-size:100%}.t-item_hidden{position:absolute;inset-block-end:0;visibility:hidden}:host._multiline .t-item_more:not(:empty){min-inline-size:var(--t-min-width, 0)}\n"] }]
}] });
const TuiItemsWithMore = [
TuiItemsWithMoreComponent,
TuiItemsWithMoreDirective,
TuiMore,
TuiItem,
];
/**
* Generated bundle index. Do not edit.
*/
export { TuiItemsWithMore, TuiItemsWithMoreComponent, TuiItemsWithMoreDirective, TuiItemsWithMoreService, TuiMore };
//# sourceMappingURL=taiga-ui-kit-components-items-with-more.mjs.map