UNPKG

@taiga-ui/cdk

Version:

Base library for creating Angular components and applications using Taiga UI principles regarding of actual visual appearance

179 lines (174 loc) 9.45 kB
import * as i0 from '@angular/core'; import { inject, PLATFORM_ID, Injectable, ChangeDetectionStrategy, ViewEncapsulation, Component, input, computed, Directive } from '@angular/core'; import { 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 { TUI_VERSION } from '@taiga-ui/cdk/constants'; import { tuiInjectElement } from '@taiga-ui/cdk/utils/dom'; import { tuiWithStyles } from '@taiga-ui/cdk/utils/miscellaneous'; import { map } from 'rxjs'; import { isPlatformBrowser, DOCUMENT } from '@angular/common'; const ELLIPSIS = '\u2026'; class TuiTruncateService { constructor() { this.el = tuiInjectElement(); this.ctx = isPlatformBrowser(inject(PLATFORM_ID)) ? inject(DOCUMENT).createElement('canvas').getContext('2d') : null; } truncate(text, lines) { if (!this.ctx || !text || lines < 1) { return text; } const style = getComputedStyle(this.el); const fontFamily = style.fontFamily || 'sans-serif'; const fontSize = style.fontSize || '14px'; const fontWeight = style.fontWeight || '400'; const paddingLeft = Number.parseFloat(style.paddingLeft) || 0; const paddingRight = Number.parseFloat(style.paddingRight) || 0; const availableWidth = this.el.clientWidth - paddingLeft - paddingRight; const safeWidth = Math.max(1, availableWidth - 4); this.ctx.font = `${fontWeight} ${fontSize} ${fontFamily}`; if (this.getLines(text, safeWidth).length <= lines) { return text; } let low = 1; let high = Math.max(1, text.length - ELLIPSIS.length); let best = truncate(text, 1, 0); while (low <= high) { const keepLength = Math.floor((low + high) / 2); const normalized = Math.max(1, keepLength); const left = Math.ceil(normalized / 2); const right = Math.floor(normalized / 2); const candidate = truncate(text, left, right); const candidateLines = this.getLines(candidate, safeWidth).length; if (candidateLines <= lines) { best = candidate; low = keepLength + 1; } else { high = keepLength - 1; } } return this.getLines(best, safeWidth).slice(0, lines).join(' '); } getLines(text, maxWidth) { if (!this.ctx || !text) { return []; } const words = text.split(' '); const lines = []; let currentLine = ''; for (const word of words) { const candidate = currentLine ? `${currentLine} ${word}` : word; if (this.ctx.measureText(candidate).width <= maxWidth) { currentLine = candidate; continue; } if (currentLine) { lines.push(currentLine); currentLine = ''; } if (this.ctx.measureText(word).width <= maxWidth) { currentLine = word; continue; } let chunk = ''; for (const char of word) { const nextChunk = `${chunk}${char}`; if (this.ctx.measureText(nextChunk).width <= maxWidth) { chunk = nextChunk; } else { if (chunk) { lines.push(chunk); } chunk = char; } } currentLine = chunk; } if (currentLine) { lines.push(currentLine); } return lines; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiTruncateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiTruncateService }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiTruncateService, decorators: [{ type: Injectable }] }); function truncate(text, left, right) { return `${text.slice(0, left)}${ELLIPSIS}${right > 0 ? text.slice(-right) : ''}`; } class Styles { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: Styles, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.25", type: Styles, isStandalone: true, selector: "ng-component", exportAs: ["tui-truncate-5.14.0"], ngImport: i0, template: '', isInline: true, styles: ["[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"]){display:-webkit-box;letter-spacing:-1rem;-webkit-text-fill-color:transparent;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden}[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"])[data-lines=\"1\"]:before{white-space:nowrap}[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"]):before{content:attr(data-text);inline-size:100%;letter-spacing:normal;-webkit-text-fill-color:currentColor;white-space:normal;overflow-wrap:anywhere}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: Styles, decorators: [{ type: Component, args: [{ template: '', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, exportAs: `tui-truncate-${TUI_VERSION}`, styles: ["[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"]){display:-webkit-box;letter-spacing:-1rem;-webkit-text-fill-color:transparent;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden}[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"])[data-lines=\"1\"]:before{white-space:nowrap}[tuiTruncate]:where(*[data-tui-version=\"5.14.0\"]):before{content:attr(data-text);inline-size:100%;letter-spacing:normal;-webkit-text-fill-color:currentColor;white-space:normal;overflow-wrap:anywhere}\n"] }] }] }); class TuiTruncate { constructor() { this.service = inject(TuiTruncateService); this.el = tuiInjectElement(); this.width = toSignal(inject(WaResizeObserverService).pipe(map(([e]) => e?.contentRect.width ?? 0)), { initialValue: 0 }); this.text = toSignal(inject(WaMutationObserverService).pipe(map(() => this.innerText)), { initialValue: this.innerText }); this.nothing = tuiWithStyles(Styles); this.lines = input(1, { alias: 'tuiTruncate', transform: (value) => Math.max(Number(value) || 1, 1), }); this.truncated = computed((_ = this.width()) => this.service.truncate(this.text(), this.lines())); } get innerText() { return this.el.innerText.trim(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiTruncate, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.25", type: TuiTruncate, isStandalone: true, selector: "[tuiTruncate]", inputs: { lines: { classPropertyName: "lines", publicName: "tuiTruncate", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-tui-version": "5.14.0", "tuiTruncate": "" }, properties: { "attr.data-lines": "lines()", "attr.data-text": "truncated()", "style.-webkit-line-clamp": "lines()" } }, providers: [ TuiTruncateService, WaResizeObserverService, WaMutationObserverService, { provide: WA_MUTATION_OBSERVER_INIT, useValue: { attributes: true, characterData: true, subtree: true, }, }, ], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImport: i0, type: TuiTruncate, decorators: [{ type: Directive, args: [{ selector: '[tuiTruncate]', providers: [ TuiTruncateService, WaResizeObserverService, WaMutationObserverService, { provide: WA_MUTATION_OBSERVER_INIT, useValue: { attributes: true, characterData: true, subtree: true, }, }, ], host: { 'data-tui-version': TUI_VERSION, tuiTruncate: '', '[attr.data-lines]': 'lines()', '[attr.data-text]': 'truncated()', '[style.-webkit-line-clamp]': 'lines()', }, }] }] }); /** * Generated bundle index. Do not edit. */ export { TuiTruncate, TuiTruncateService }; //# sourceMappingURL=taiga-ui-cdk-directives-truncate.mjs.map