@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
JavaScript
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