UNPKG

@taiga-ui/kit

Version:

Taiga UI Angular main components kit

198 lines (190 loc) 13.2 kB
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