UNPKG

@yelon/abc

Version:

Common business components of ng-yunzai.

245 lines (240 loc) 14.4 kB
import { CdkObserveContent, ObserversModule } from '@angular/cdk/observers'; import { DOCUMENT, NgTemplateOutlet, CommonModule } from '@angular/common'; import * as i0 from '@angular/core'; import { inject, ElementRef, NgZone, ChangeDetectorRef, booleanAttribute, numberAttribute, Input, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, NgModule } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { take } from 'rxjs'; import { NzTooltipDirective, NzTooltipModule } from 'ng-zorro-antd/tooltip'; class EllipsisComponent { el = inject(ElementRef).nativeElement; ngZone = inject(NgZone); dom = inject(DomSanitizer); doc = inject(DOCUMENT); cdr = inject(ChangeDetectorRef); isSupportLineClamp = this.doc.body.style['webkitLineClamp'] !== undefined; orgEl; shadowOrgEl; shadowTextEl; inited = false; orgHtml; type = 'default'; cls = {}; text = ''; targetCount = 0; tooltip = false; length; lines; fullWidthRecognition = false; tail = '...'; get linsWord() { const { targetCount, text, tail } = this; return ((targetCount > 0 ? text.substring(0, targetCount) : '') + (targetCount > 0 && targetCount < text.length ? tail : '')); } get win() { return this.doc.defaultView || window; } getStrFullLength(str) { return str.split('').reduce((pre, cur) => { const charCode = cur.charCodeAt(0); if (charCode >= 0 && charCode <= 128) { return pre + 1; } return pre + 2; }, 0); } cutStrByFullLength(str, maxLength) { let showLength = 0; return str.split('').reduce((pre, cur) => { const charCode = cur.charCodeAt(0); if (charCode >= 0 && charCode <= 128) { showLength += 1; } else { showLength += 2; } if (showLength <= maxLength) { return pre + cur; } return pre; }, ''); } bisection(targetHeight, mid, begin, end, text, node) { const suffix = this.tail; node.innerHTML = text.substring(0, mid) + suffix; let sh = node.offsetHeight; if (sh <= targetHeight) { node.innerHTML = text.substring(0, mid + 1) + suffix; sh = node.offsetHeight; if (sh > targetHeight || mid === begin) { return mid; } begin = mid; mid = end - begin === 1 ? begin + 1 : Math.floor((end - begin) / 2) + begin; return this.bisection(targetHeight, mid, begin, end, text, node); } if (mid - 1 < 0) { return mid; } node.innerHTML = text.substring(0, mid - 1) + suffix; sh = node.offsetHeight; if (sh <= targetHeight) { return mid - 1; } end = mid; mid = Math.floor((end - begin) / 2) + begin; return this.bisection(targetHeight, mid, begin, end, text, node); } genType() { const { lines, length, isSupportLineClamp } = this; this.cls = { ellipsis: true, ellipsis__lines: lines && !isSupportLineClamp, 'ellipsis__line-clamp': lines && isSupportLineClamp }; if (!lines && !length) { this.type = 'default'; } else if (!lines) { this.type = 'length'; } else if (isSupportLineClamp) { this.type = 'line-clamp'; } else { this.type = 'line'; } } gen() { const { type, lines, length, fullWidthRecognition, tail, orgEl, cdr, ngZone } = this; if (type === 'length') { const el = orgEl.nativeElement; if (el.children.length > 0) { throw new Error('Ellipsis content must be string.'); } const lengthText = el.textContent; const textLength = fullWidthRecognition ? this.getStrFullLength(lengthText) : lengthText.length; if (textLength <= length || length < 0) { this.text = lengthText; } else { let displayText; if (length - tail.length <= 0) { displayText = ''; } else { displayText = fullWidthRecognition ? this.cutStrByFullLength(lengthText, length) : lengthText.slice(0, length); } this.text = displayText + tail; } ngZone.run(() => cdr.detectChanges()); } else if (type === 'line') { const { shadowOrgEl, shadowTextEl } = this; const orgNode = shadowOrgEl.nativeElement; const lineText = orgNode.innerText || orgNode.textContent; const lineHeight = parseInt(this.win.getComputedStyle(this.getEl('.ellipsis')).lineHeight, 10); const targetHeight = lines * lineHeight; const handleEl = this.getEl('.ellipsis__handle'); handleEl.style.height = `${targetHeight}px`; if (orgNode.offsetHeight <= targetHeight) { this.text = lineText; this.targetCount = lineText.length; } else { // bisection const len = lineText.length; const mid = Math.ceil(len / 2); const count = this.bisection(targetHeight, mid, 0, len, lineText, shadowTextEl.nativeElement.firstChild); this.text = lineText; this.targetCount = count; } ngZone.run(() => cdr.detectChanges()); } } getEl(cls) { return this.el.querySelector(cls); } executeOnStable(fn) { if (this.ngZone.isStable) { fn(); } else { this.ngZone.onStable.asObservable().pipe(take(1)).subscribe(fn); } } refresh() { this.genType(); const { type, dom, orgEl, cdr } = this; const html = orgEl.nativeElement.innerHTML; this.orgHtml = dom.bypassSecurityTrustHtml(html); cdr.detectChanges(); this.executeOnStable(() => { this.gen(); if (type !== 'line') { const el = this.getEl('.ellipsis'); if (el) { el.innerHTML = html; } } }); } ngAfterViewInit() { this.inited = true; this.refresh(); } ngOnChanges() { if (this.inited) { this.refresh(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EllipsisComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.3", type: EllipsisComponent, isStandalone: true, selector: "ellipsis", inputs: { tooltip: ["tooltip", "tooltip", booleanAttribute], length: ["length", "length", (v) => (v == null ? null : numberAttribute(v))], lines: ["lines", "lines", (v) => (v == null ? null : numberAttribute(v))], fullWidthRecognition: ["fullWidthRecognition", "fullWidthRecognition", booleanAttribute], tail: "tail" }, viewQueries: [{ propertyName: "orgEl", first: true, predicate: ["orgEl"], descendants: true }, { propertyName: "shadowOrgEl", first: true, predicate: ["shadowOrgEl"], descendants: true }, { propertyName: "shadowTextEl", first: true, predicate: ["shadowTextEl"], descendants: true }], exportAs: ["ellipsis"], usesOnChanges: true, ngImport: i0, template: "<div (cdkObserveContent)=\"refresh()\" #orgEl style=\"display: none\"><ng-content /></div>\n<ng-template #tooltipTpl let-con>\n @if (tooltip) {\n <span\n nz-tooltip\n [nzTooltipTitle]=\"titleTpl\"\n [nzTooltipOverlayStyle]=\"{ 'overflow-wrap': 'break-word', 'word-wrap': 'break-word' }\"\n >\n <ng-container *ngTemplateOutlet=\"con\" />\n <ng-template #titleTpl><div [innerHTML]=\"orgHtml\"></div></ng-template>\n </span>\n } @else {\n <ng-container *ngTemplateOutlet=\"con\" />\n }\n</ng-template>\n@switch (type) {\n @case ('default') {\n <span [class]=\"cls\"></span>\n }\n @case ('length') {\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lengthTpl }\" />\n <ng-template #lengthTpl>{{ text }}</ng-template>\n }\n @case ('line-clamp') {\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lineClampTpl }\" />\n <ng-template #lineClampTpl>\n <div [class]=\"cls\" [style]=\"{ '-webkit-line-clamp': lines, '-webkit-box-orient': 'vertical' }\"></div>\n </ng-template>\n }\n @case ('line') {\n <div [class]=\"cls\">\n <div class=\"ellipsis__handle\">\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lineTpl }\" />\n <ng-template #lineTpl>{{ linsWord }}</ng-template>\n <div class=\"ellipsis__shadow\" #shadowOrgEl [innerHTML]=\"orgHtml\"></div>\n <div class=\"ellipsis__shadow\" #shadowTextEl>\n <span>{{ text }}</span>\n </div>\n </div>\n </div>\n }\n}\n", dependencies: [{ kind: "directive", type: CdkObserveContent, selector: "[cdkObserveContent]", inputs: ["cdkObserveContentDisabled", "debounce"], outputs: ["cdkObserveContent"], exportAs: ["cdkObserveContent"] }, { kind: "directive", type: NzTooltipDirective, selector: "[nz-tooltip]", inputs: ["nzTooltipTitle", "nzTooltipTitleContext", "nz-tooltip", "nzTooltipTrigger", "nzTooltipPlacement", "nzTooltipOrigin", "nzTooltipVisible", "nzTooltipMouseEnterDelay", "nzTooltipMouseLeaveDelay", "nzTooltipOverlayClassName", "nzTooltipOverlayStyle", "nzTooltipArrowPointAtCenter", "cdkConnectedOverlayPush", "nzTooltipColor"], outputs: ["nzTooltipVisibleChange"], exportAs: ["nzTooltip"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EllipsisComponent, decorators: [{ type: Component, args: [{ selector: 'ellipsis', exportAs: 'ellipsis', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [CdkObserveContent, NzTooltipDirective, NgTemplateOutlet], template: "<div (cdkObserveContent)=\"refresh()\" #orgEl style=\"display: none\"><ng-content /></div>\n<ng-template #tooltipTpl let-con>\n @if (tooltip) {\n <span\n nz-tooltip\n [nzTooltipTitle]=\"titleTpl\"\n [nzTooltipOverlayStyle]=\"{ 'overflow-wrap': 'break-word', 'word-wrap': 'break-word' }\"\n >\n <ng-container *ngTemplateOutlet=\"con\" />\n <ng-template #titleTpl><div [innerHTML]=\"orgHtml\"></div></ng-template>\n </span>\n } @else {\n <ng-container *ngTemplateOutlet=\"con\" />\n }\n</ng-template>\n@switch (type) {\n @case ('default') {\n <span [class]=\"cls\"></span>\n }\n @case ('length') {\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lengthTpl }\" />\n <ng-template #lengthTpl>{{ text }}</ng-template>\n }\n @case ('line-clamp') {\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lineClampTpl }\" />\n <ng-template #lineClampTpl>\n <div [class]=\"cls\" [style]=\"{ '-webkit-line-clamp': lines, '-webkit-box-orient': 'vertical' }\"></div>\n </ng-template>\n }\n @case ('line') {\n <div [class]=\"cls\">\n <div class=\"ellipsis__handle\">\n <ng-template [ngTemplateOutlet]=\"tooltipTpl\" [ngTemplateOutletContext]=\"{ $implicit: lineTpl }\" />\n <ng-template #lineTpl>{{ linsWord }}</ng-template>\n <div class=\"ellipsis__shadow\" #shadowOrgEl [innerHTML]=\"orgHtml\"></div>\n <div class=\"ellipsis__shadow\" #shadowTextEl>\n <span>{{ text }}</span>\n </div>\n </div>\n </div>\n }\n}\n" }] }], propDecorators: { orgEl: [{ type: ViewChild, args: ['orgEl', { static: false }] }], shadowOrgEl: [{ type: ViewChild, args: ['shadowOrgEl', { static: false }] }], shadowTextEl: [{ type: ViewChild, args: ['shadowTextEl', { static: false }] }], tooltip: [{ type: Input, args: [{ transform: booleanAttribute }] }], length: [{ type: Input, args: [{ transform: (v) => (v == null ? null : numberAttribute(v)) }] }], lines: [{ type: Input, args: [{ transform: (v) => (v == null ? null : numberAttribute(v)) }] }], fullWidthRecognition: [{ type: Input, args: [{ transform: booleanAttribute }] }], tail: [{ type: Input }] } }); const COMPONENTS = [EllipsisComponent]; class EllipsisModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EllipsisModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.3", ngImport: i0, type: EllipsisModule, imports: [CommonModule, ObserversModule, NzTooltipModule, EllipsisComponent], exports: [EllipsisComponent] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EllipsisModule, imports: [CommonModule, ObserversModule, NzTooltipModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: EllipsisModule, decorators: [{ type: NgModule, args: [{ imports: [CommonModule, ObserversModule, NzTooltipModule, ...COMPONENTS], exports: COMPONENTS }] }] }); /** * Generated bundle index. Do not edit. */ export { EllipsisComponent, EllipsisModule }; //# sourceMappingURL=ellipsis.mjs.map