@libs-ui/components-line-clamp
v0.2.357-11
Published
> Component giới hạn số dòng văn bản, hỗ trợ nút Xem thêm / Thu gọn và Tooltip tích hợp sẵn.
Readme
@libs-ui/components-line-clamp
Component giới hạn số dòng văn bản, hỗ trợ nút Xem thêm / Thu gọn và Tooltip tích hợp sẵn.
Giới thiệu
@libs-ui/components-line-clamp là component Angular dùng để cắt ngắn đoạn văn bản dài theo chiều cao tối đa (px) và tự động hiển thị nút điều khiển "Xem thêm" / "Thu gọn" khi nội dung vượt giới hạn. Component hỗ trợ render cả HTML (có lọc XSS) lẫn plain text, tích hợp Tooltip khi hover, hiệu ứng gradient mờ dần, và cung cấp FunctionsControl để component cha điều khiển trực tiếp qua ViewChild.
Tính năng
- Giới hạn chiều cao hiển thị dựa trên
maxHeight(px) — tính toán thực tế từ DOM - Tự động hiển thị / ẩn nút "Xem thêm" và "Thu gọn" khi nội dung thay đổi
- Hỗ trợ render HTML an toàn qua
useXssFilterhoặc plain text quaisInnerText - Tích hợp Tooltip (Popover) hiển thị nội dung đầy đủ khi hover
- Hiệu ứng gradient mờ dần ở cuối đoạn văn bị cắt (
hasBackgroundGradient) - Tự động refresh khi cửa sổ trình duyệt resize (
window:resize) - Cho phép tùy chỉnh nhãn nút, CSS class nội dung và nút điều khiển
- Cung cấp
FunctionsControl(refresh, viewMore, hiddenMore, checkIsDisplayLineClamp) để điều khiển từ bên ngoài - Hỗ trợ i18n thông qua
@ngx-translate/core
Khi nào sử dụng
- Hiển thị phần giới thiệu ngắn trong danh sách thẻ (Card list) khi không đủ không gian
- Cắt bớt bình luận, mô tả sản phẩm hoặc ghi chú dài trong giao diện Dashboard
- Cần render HTML từ backend nhưng vẫn đảm bảo an toàn XSS và hỗ trợ rút gọn
- Muốn người dùng xem nhanh nội dung đầy đủ qua Tooltip mà không cần mở rộng trang
Cài đặt
npm install @libs-ui/components-line-clampImport
import { LibsUiComponentsLineClampComponent } from '@libs-ui/components-line-clamp';
import { ILineClampFunctionControlEvent, ILineClampConfig } from '@libs-ui/components-line-clamp';
@Component({
standalone: true,
imports: [LibsUiComponentsLineClampComponent],
})
export class YourComponent {}Ví dụ sử dụng
Cắt dòng cơ bản (2 dòng, line-height 20px)
// your.component.ts
import { Component } from '@angular/core';
import { LibsUiComponentsLineClampComponent } from '@libs-ui/components-line-clamp';
@Component({
standalone: true,
imports: [LibsUiComponentsLineClampComponent],
template: `
<libs_ui-components-line_clamp
[content]="longText"
[maxHeight]="40"
[labelButtonViewMore]="'Xem thêm nội dung'"
[labelButtonCollapse]="'Thu gọn lại'">
</libs_ui-components-line_clamp>
`,
})
export class BasicExampleComponent {
readonly longText = `Angular là một nền tảng và framework để xây dựng các ứng dụng khách đơn trang
sử dụng HTML và TypeScript. Nó triển khai chức năng cốt lõi và tùy chọn dưới dạng
một tập hợp các thư viện TypeScript mà bạn import vào ứng dụng của mình.`;
}Tooltip và hiệu ứng gradient
// your.component.ts
import { Component } from '@angular/core';
import { LibsUiComponentsLineClampComponent } from '@libs-ui/components-line-clamp';
@Component({
standalone: true,
imports: [LibsUiComponentsLineClampComponent],
template: `
<libs_ui-components-line_clamp
[content]="htmlContent"
[maxHeight]="60"
[showTooltip]="true"
[hasBackgroundGradient]="true"
[maxWidthTooltip]="350"
[directionTooltip]="'bottom'">
</libs_ui-components-line_clamp>
`,
})
export class TooltipExampleComponent {
readonly htmlContent = `
<p style="color: #ef4444; font-weight: bold;">Nội dung HTML quan trọng:</p>
<ul>
<li>Signal là một cách mới để quản lý state.</li>
<li>Control Flow Syntax giúp template sạch hơn.</li>
<li>Deferrable Views giúp tối ưu lazy loading.</li>
</ul>
`;
}Plain text mode (không render HTML)
// your.component.ts
import { Component } from '@angular/core';
import { LibsUiComponentsLineClampComponent } from '@libs-ui/components-line-clamp';
@Component({
standalone: true,
imports: [LibsUiComponentsLineClampComponent],
template: `
<libs_ui-components-line_clamp
[content]="plainText"
[maxHeight]="40"
[isInnerText]="true"
[ngClassObject]="{ 'text-gray-500 italic': true }">
</libs_ui-components-line_clamp>
`,
})
export class PlainTextExampleComponent {
readonly plainText = `Đây là văn bản thuần túy, sẽ được render bằng innerText.
Mọi thẻ HTML trong chuỗi này sẽ hiển thị dưới dạng ký tự thay vì được phân tích cú pháp.`;
}Điều khiển từ component cha qua FunctionsControl
// parent.component.ts
import { Component, signal, viewChild } from '@angular/core';
import {
LibsUiComponentsLineClampComponent,
ILineClampFunctionControlEvent,
} from '@libs-ui/components-line-clamp';
@Component({
standalone: true,
imports: [LibsUiComponentsLineClampComponent],
template: `
<libs_ui-components-line_clamp
[content]="description"
[maxHeight]="60"
(outFunctionControl)="handlerFunctionControl($event)"
(outAction)="handlerAction($event)"
(outDisplayLineClamp)="handlerDisplayLineClamp($event)">
</libs_ui-components-line_clamp>
<button (click)="handlerRefresh()">Refresh</button>
<button (click)="handlerExpandAll()">Mở rộng tất cả</button>
`,
})
export class ParentExampleComponent {
protected description = `Mô tả dài của sản phẩm sẽ được cắt sau 3 dòng (60px với line-height 20px).
Nhấn nút bên dưới để điều khiển trạng thái từ component cha.`;
private lineClampControl: ILineClampFunctionControlEvent | undefined;
protected handlerFunctionControl(control: ILineClampFunctionControlEvent): void {
this.lineClampControl = control;
}
protected handlerAction(action: string): void {
// action: 'view-more' | 'hidden-more'
console.log('Line clamp action:', action);
}
protected handlerDisplayLineClamp(isCollapsed: boolean): void {
console.log('Is collapsed:', isCollapsed);
}
protected handlerRefresh(): void {
this.lineClampControl?.refresh();
}
protected handlerExpandAll(): void {
this.lineClampControl?.viewMore();
}
}Ẩn nút thu gọn (chỉ hiển thị "Xem thêm")
<libs_ui-components-line_clamp
[content]="shortDescription"
[maxHeight]="40"
[ignoreShowButtonCollapse]="true"
[labelButtonViewMore]="'Đọc thêm'">
</libs_ui-components-line_clamp>@Input()
| Input | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| classClassIncludeButtonCollapseExpand | string | '!p-0' | Class CSS cho container wrapper của nút collapse/expand | [classClassIncludeButtonCollapseExpand]="'mt-2 !p-0'" |
| classClassLabelButtonCollapseExpand | string | 'libs-ui-font-h5r' | Class CSS typography cho nhãn nút collapse/expand | [classClassLabelButtonCollapseExpand]="'libs-ui-font-h6r'" |
| content | string | bắt buộc | Nội dung văn bản hoặc HTML cần hiển thị và cắt ngắn | [content]="description" |
| defaultIsCollapse | boolean | true | Trạng thái thu gọn mặc định khi nội dung vượt maxHeight | [defaultIsCollapse]="false" |
| directionTooltip | TYPE_POPOVER_DIRECTION | 'top' | Hướng xuất hiện của Tooltip (top, bottom, left, right) | [directionTooltip]="'bottom'" |
| hasBackgroundGradient | boolean | false | Hiển thị hiệu ứng mờ dần gradient ở cuối đoạn văn bị cắt | [hasBackgroundGradient]="true" |
| ignoreShowButtonCollapse | boolean | undefined | Ẩn nút "Thu gọn", chỉ giữ lại nút "Xem thêm" | [ignoreShowButtonCollapse]="true" |
| ignoreShowButtonCollapseExpand | boolean | false | Ẩn hoàn toàn cả hai nút Xem thêm và Thu gọn | [ignoreShowButtonCollapseExpand]="true" |
| ignoreStopPropagationTooltipEvent | boolean | false | Cho phép sự kiện Tooltip nổi bọt lên component cha | [ignoreStopPropagationTooltipEvent]="true" |
| isInnerText | boolean | undefined | Render nội dung bằng innerText thay vì innerHTML, mọi thẻ HTML sẽ hiển thị dạng ký tự | [isInnerText]="true" |
| labelButtonCollapse | string | undefined (dùng i18n i18n_collapse) | Nhãn tùy chỉnh cho nút Thu gọn | [labelButtonCollapse]="'Thu gọn lại'" |
| labelButtonViewMore | string | undefined (dùng i18n i18n_view_more) | Nhãn tùy chỉnh cho nút Xem thêm | [labelButtonViewMore]="'Xem thêm nội dung'" |
| lengthLimitDisplay | number | 200 | Giới hạn số ký tự dùng để tính toán cắt chuỗi ban đầu trước khi đo DOM | [lengthLimitDisplay]="300" |
| maxHeight | number | bắt buộc | Chiều cao tối đa (px) trước khi cắt. Công thức: line-height × số dòng. Ví dụ: 20px × 3 dòng = 60 | [maxHeight]="60" |
| maxHeightTooltip | number | 150 | Chiều cao tối đa (px) của Tooltip | [maxHeightTooltip]="200" |
| maxWidthTooltip | number | 250 | Chiều rộng tối đa (px) của Tooltip | [maxWidthTooltip]="350" |
| ngClassObject | TYPE_OBJECT | { 'libs-ui-line-clamp-content libs-ui-font-h5r': true } | Object ngClass để apply class CSS vào div chứa nội dung | [ngClassObject]="{ 'libs-ui-font-h6r text-gray-700': true }" |
| showTooltip | boolean | undefined | Bật Tooltip hiển thị toàn bộ nội dung khi hover vào vùng văn bản đã cắt | [showTooltip]="true" |
| timeHidePopoverOnMouseout | number | 50 | Thời gian delay (ms) trước khi ẩn Tooltip sau sự kiện mouseout | [timeHidePopoverOnMouseout]="100" |
| useXssFilter | boolean | true | Bật bộ lọc XSS cho nội dung HTML (chỉ có hiệu lực khi isInnerText là false) | [useXssFilter]="false" |
| zIndexPopover | number | 1001 | Giá trị z-index của Tooltip/Popover | [zIndexPopover]="1050" |
@Output()
| Output | Type | Mô tả | Handler TS | Binding HTML |
|---|---|---|---|---|
| (outAction) | string | Phát ra chuỗi 'view-more' khi nhấn nút Xem thêm, hoặc 'hidden-more' khi nhấn nút Thu gọn | handlerAction(action: string): void { console.log(action); } | (outAction)="handlerAction($event)" |
| (outClick) | Event \| TYPE_POPOVER_EVENT | Phát ra khi người dùng click vào vùng nội dung (giá trị là Event) hoặc khi Tooltip nhận sự kiện click (giá trị là chuỗi 'click' từ TYPE_POPOVER_EVENT) | handlerClick(event: Event \| string): void { if (event instanceof Event) { event.stopPropagation(); } console.log(event); } | (outClick)="handlerClick($event)" |
| (outDisplayLineClamp) | boolean | Phát ra trạng thái thu gọn hiện tại (true = đang thu gọn, false = đã mở rộng). Kích hoạt khi nội dung vượt maxHeight | handlerDisplayLineClamp(isCollapsed: boolean): void { this.isCollapsed.set(isCollapsed); } | (outDisplayLineClamp)="handlerDisplayLineClamp($event)" |
| (outFunctionControl) | ILineClampFunctionControlEvent | Phát ra ngay khi component khởi tạo (ngOnInit), cung cấp các method điều khiển để component cha gọi từ bên ngoài | handlerFunctionControl(control: ILineClampFunctionControlEvent): void { this.lineClampControl = control; } | (outFunctionControl)="handlerFunctionControl($event)" |
Types & Interfaces
import {
ILineClampFunctionControlEvent,
ILineClampConfig,
} from '@libs-ui/components-line-clamp';ILineClampFunctionControlEvent
Interface nhận được qua event (outFunctionControl), dùng để điều khiển component từ bên ngoài:
export interface ILineClampFunctionControlEvent {
/** Làm mới trạng thái, tính toán lại từ đầu */
refresh: () => Promise<void>;
/** Kiểm tra component có đang ở trạng thái clamp (nội dung bị cắt) không */
checkIsDisplayLineClamp: () => Promise<boolean>;
/** Mở rộng nội dung (tương đương nhấn nút "Xem thêm") */
viewMore: () => Promise<void>;
/** Thu gọn nội dung (tương đương nhấn nút "Thu gọn") */
hiddenMore: () => Promise<void>;
}ILineClampConfig
Interface cấu hình tùy chọn (dùng cho các trường hợp cần type hóa config object):
import { TYPE_OBJECT } from '@libs-ui/interfaces-types';
export interface ILineClampConfig {
maxHeight?: number;
class?: string;
ignoreShowButtonCollapseExpand?: boolean;
showTooltip?: boolean;
maxWidthTooltip?: number;
maxHeightTooltip?: number;
ngClassObject?: TYPE_OBJECT;
isInnerText?: boolean;
}Lưu ý quan trọng
⚠️ Tính toán maxHeight: maxHeight phải bằng line-height (px) × số dòng mong muốn. Ví dụ: text có line-height: 20px, muốn hiển thị 3 dòng thì set [maxHeight]="60". Sai giá trị này sẽ khiến việc phát hiện clamp không chính xác.
⚠️ isInnerText và useXssFilter: Khi bật [isInnerText]="true", nội dung được render bằng innerText — mọi thẻ HTML đều hiển thị dưới dạng ký tự. Trong trường hợp này useXssFilter không có tác dụng. Chỉ dùng useXssFilter khi render HTML (mặc định).
⚠️ ngClassObject — CSS typography: Object ngClassObject được apply vào thẻ div bao quanh nội dung. Mặc định là { 'libs-ui-line-clamp-content libs-ui-font-h5r': true }. Khi ghi đè, cần bao gồm cả class layout cần thiết, không chỉ class typography.
⚠️ outFunctionControl phát ra khi ngOnInit: Event (outFunctionControl) chỉ phát ra một lần duy nhất khi component khởi tạo. Cần lưu lại reference để dùng sau: private lineClampControl: ILineClampFunctionControlEvent | undefined.
⚠️ Resize tự động: Component lắng nghe sự kiện window:resize và tự động gọi refresh(). Việc này đảm bảo trạng thái clamp luôn chính xác khi kích thước viewport thay đổi.
