@libs-ui/components-spreadsheet
v0.2.357-4
Published
> Bảng tính nhúng cho Angular (bọc [Univer](https://univer.ai)) — xem/chỉnh dữ liệu dạng lưới, định dạng số/ngày kiểu Excel, highlight cột đã map và bắt sự kiện cấu trúc cột.
Readme
@libs-ui/components-spreadsheet
Bảng tính nhúng cho Angular (bọc Univer) — xem/chỉnh dữ liệu dạng lưới, định dạng số/ngày kiểu Excel, highlight cột đã map và bắt sự kiện cấu trúc cột.
Giới thiệu
LibsUiComponentsSpreadsheetComponent là một Angular standalone component (OnPush + Signals) bọc thư viện Univer để hiển thị một bảng tính giống Excel/Google Sheets thu gọn. Component giao tiếp với bên ngoài qua getter FunctionControl (lấy bằng viewChild), KHÔNG gọi method private trực tiếp.
⚠️ Component thao tác trực tiếp DOM nội bộ (React/canvas) của Univer cho các tính năng tuỳ biến (panel numfmt, toolbar overflow…). Xem
BUSINESS.mdđể biết các hành vi không được làm vỡ + bộ test hồi quy thủ công.
Tính năng
- ✅ Nạp / lưu workbook dạng JSON snapshot (
loadData,saveData,reset) - ✅ Định dạng số/ngày tuỳ biến kiểu Excel (~21 mẫu Ngày / Ngày+Giờ / Giờ), label là preview giá trị thật
- ✅ Highlight cột đã map (style + prefix + auto-resize) qua luồng auto
mappingIndicatorConfig+mappingTargets - ✅ Tự gỡ indicator khi
saveData()để snapshot gửi server sạch - ✅ Bắt sự kiện double-click header cột (
outColumnDblClick) - ✅ Bắt insert / remove / move cột — kể cả undo/redo (
outColumnStructureChange) + helperremapColumnIndexes - ✅ Đổi ngôn ngữ runtime (vi / en), ẩn/hiện toolbar & sheet tabs, cấu hình ẩn toolbar item
- ✅ Standalone, OnPush, Angular Signals — không cần NgModule
Khi nào sử dụng
- Hiển thị / chỉnh sửa dữ liệu dạng bảng tính ngay trong app
- Cho người dùng map cột dữ liệu sang field nghiệp vụ (highlight cột đã map)
- Cần định dạng số / ngày tuỳ biến kiểu Excel trên dữ liệu nhập
- Nạp workbook từ JSON và xuất lại snapshot để lưu server
Cài đặt
npm install @libs-ui/components-spreadsheetCác package
@univerjs/*đã khai báo làpeerDependenciestrongpackage.jsoncủa lib nên trình quản lý gói sẽ tự cài kèm — không cần cài thủ công.
⚠️ Nạp CSS của Univer (BẮT BUỘC — nếu thiếu, toolbar/panel/canvas sẽ vỡ giao diện)
Univer ship style riêng, KHÔNG đi kèm trong bundle của component. Consumer PHẢI tự nạp 5 file CSS sau, nếu không bảng tính sẽ hiển thị sai (mất toolbar, panel numfmt/menu trắng, layout lệch).
Khai báo trong angular.json / project.json:
// architect.build.options.styles (Angular CLI) HOẶC targets.build.options.styles (Nx)
"styles": [
"src/styles.scss",
"./node_modules/@univerjs/design/lib/index.css",
"./node_modules/@univerjs/ui/lib/index.css",
"./node_modules/@univerjs/sheets-ui/lib/index.css",
"./node_modules/@univerjs/sheets-formula-ui/lib/index.css",
"./node_modules/@univerjs/sheets-numfmt/lib/index.css"
]🔴 Thứ tự không bắt buộc, nhưng phải đủ cả 5 file. Thiếu
@univerjs/uihoặc@univerjs/sheets-uilà nguyên nhân phổ biến nhất khiến toolbar/panel không hiển thị.
Import
import {
LibsUiComponentsSpreadsheetComponent,
defaultMappingIndicatorConfig,
remapColumnIndexes,
toColumnLetter,
applyMappingIndicators,
stripMappingIndicators,
} from '@libs-ui/components-spreadsheet';
import type {
T_indicatorTarget,
T_indicatorStyle,
T_mappingIndicatorConfig,
T_columnStructureChange,
T_cellFormatResult,
T_toolbar_menu_config,
T_toolbar_item_id,
} from '@libs-ui/components-spreadsheet';Ví dụ sử dụng
Basic — bảng tính cơ bản
Component có :host { height: 100% } nên BẮT BUỘC bọc trong khung cố định chiều cao để canvas render đúng.
import { AfterViewInit, Component, viewChild } from '@angular/core';
import { LibsUiComponentsSpreadsheetComponent } from '@libs-ui/components-spreadsheet';
@Component({
selector: 'app-basic-example',
standalone: true,
imports: [LibsUiComponentsSpreadsheetComponent],
template: `
<div class="h-[480px]">
<libs_ui-components-spreadsheet #sheet [locale]="'vi'" />
</div>
`,
})
export class BasicExampleComponent implements AfterViewInit {
private sheet = viewChild<LibsUiComponentsSpreadsheetComponent>('sheet');
ngAfterViewInit(): void {
this.sheet()?.FunctionControl.loadData({
id: 'wb',
sheetOrder: ['sheet1'],
sheets: { sheet1: { id: 'sheet1', name: 'Sheet1', rowCount: 30, columnCount: 26, cellData: {} } },
});
}
}Mapping Indicator — highlight cột đã map
import { Component, signal } from '@angular/core';
import { LibsUiComponentsSpreadsheetComponent, defaultMappingIndicatorConfig, type T_indicatorTarget } from '@libs-ui/components-spreadsheet';
@Component({
selector: 'app-mapping-example',
standalone: true,
imports: [LibsUiComponentsSpreadsheetComponent],
template: `
<div class="h-[480px]">
<libs_ui-components-spreadsheet
[mappingIndicatorConfig]="indicatorConfig"
[mappingTargets]="mappingTargets()"
(outColumnDblClick)="onColumnDblClick($event)"
/>
</div>
`,
})
export class MappingExampleComponent {
// Truyền config = "bật" luồng auto: tự apply khi load, tự strip khi save, tự vẽ lại khi targets đổi.
protected readonly indicatorConfig = defaultMappingIndicatorConfig();
protected mappingTargets = signal<T_indicatorTarget[]>([
{ sheetId: 'sheet1', colIndex: 0 },
{ sheetId: 'sheet1', colIndex: 3 },
]);
// Double-click header → mở modal map cột, rồi cập nhật mappingTargets (component tự vẽ lại).
onColumnDblClick(colIndex: number): void {
this.mappingTargets.update((list) => [...list, { sheetId: 'sheet1', colIndex }]);
}
}Column Events — bắt insert/remove/move cột
import { Component, signal } from '@angular/core';
import { LibsUiComponentsSpreadsheetComponent, remapColumnIndexes, type T_columnStructureChange } from '@libs-ui/components-spreadsheet';
@Component({
selector: 'app-events-example',
standalone: true,
imports: [LibsUiComponentsSpreadsheetComponent],
template: `
<div class="h-[480px]">
<libs_ui-components-spreadsheet
(outColumnDblClick)="onDblClick($event)"
(outColumnStructureChange)="onStructureChange($event)"
/>
</div>
`,
})
export class EventsExampleComponent {
protected mappings = signal<{ sheetId: string; colIndex: number; colLetter?: string }[]>([]);
onDblClick(colIndex: number): void {
console.log('Double-click cột', colIndex);
}
// Insert/remove/move cột → dịch lại mapping cho khớp vị trí mới (tránh lệch khi chèn/xoá cột giữa).
onStructureChange(change: T_columnStructureChange): void {
this.mappings.update((list) => remapColumnIndexes(list, change));
}
}Lưu & nạp lại snapshot
// Xuất snapshot (tự gỡ indicator nếu luồng auto đang bật) → gửi server.
const snapshot = this.sheet()?.FunctionControl.saveData();
// Nạp lại snapshot đã lưu.
this.sheet()?.FunctionControl.loadData(snapshot);API
Selector
libs_ui-components-spreadsheet (khớp với selector trong @Component).
Inputs
| Input | Type | Default | Mô tả | Ví dụ |
|---|---|---|---|---|
| [locale] | 'vi' \| 'en' | 'vi' | Ngôn ngữ khởi tạo của toolbar/menu/panel. Đổi runtime qua FunctionControl.setLocale(). | [locale]="'en'" |
| [mappingIndicatorConfig] | T_mappingIndicatorConfig | undefined | Truyền = bật luồng auto highlight cột đã map. | [mappingIndicatorConfig]="indicatorConfig" |
| [mappingTargets] | T_indicatorTarget[] | [] | Danh sách cột cần highlight. Đổi runtime → tự vẽ lại header. | [mappingTargets]="targets()" |
| [toolbarMenuConfig] | T_toolbar_menu_config | { ... } | Ẩn (hidden) / vô hiệu hoá (disabled) từng toolbar item. Thay đổi sẽ reinit Univer. | [toolbarMenuConfig]="{ 'sheet.command.set-range-bold': { hidden: true } }" |
Outputs
| Output | Type | Mô tả |
|---|---|---|
| (outColumnDblClick) | number | Emit colIndex khi double-click header cột. |
| (outColumnStructureChange) | T_columnStructureChange | Emit khi insert / remove / move cột (gồm undo/redo). |
Handler ví dụ:
// component.ts
onColumnDblClick(colIndex: number): void {
this.openMappingModal(colIndex);
}
onColumnStructureChange(change: T_columnStructureChange): void {
this.mappings.update((list) => remapColumnIndexes(list, change));
}<!-- template.html -->
<libs_ui-components-spreadsheet
(outColumnDblClick)="onColumnDblClick($event)"
(outColumnStructureChange)="onColumnStructureChange($event)"
/>FunctionControl Methods
Lấy qua viewChild → ref().FunctionControl.method().
| Method | Mô tả |
|---|---|
| applyNumfmt(row, col, numRows, numCols, pattern) | Áp numfmt pattern cho một vùng ô. |
| extractAllColumnFormats(snapshot) | Trích pattern numfmt của mọi cột từ snapshot. |
| getActiveSheetInfo() | Lấy { sheetId, sheetName } của sheet đang active (null nếu chưa init). |
| getColumnNumfmt(sheetId, colIndex) | Đọc numfmt pattern đại diện của một cột. |
| loadData(data) | Nạp workbook snapshot (JSON) vào bảng tính. |
| reset(data?) | Destroy + khởi tạo lại với data (mặc định = workbook rỗng). |
| saveData() | Xuất snapshot JSON (tự gỡ indicator nếu luồng auto đang bật). |
| scheduleOnReady(cb) | Chạy callback sau khi toolbar/numfmt model render xong. |
| setLocale(locale) | Đổi ngôn ngữ runtime (kéo theo reinit Univer). |
| toggleSheetTabs() | Ẩn/hiện thanh sheet tabs (footer). |
| toggleToolbar() | Ẩn/hiện toolbar (header). |
Ngoài ra FunctionControl expose các signal read-only: activeCellFormat, activeCellRef, locale, showToolbar, showSheetTabs.
Hàm tiện ích (pure)
| Hàm | Mô tả |
|---|---|
| defaultMappingIndicatorConfig() | Trả config highlight mặc định (style 'm' xanh, revert 'h', prefix '● '). |
| applyMappingIndicators(snapshot, targets, config?) | Đánh dấu header cột đã map trên snapshot (không mutate). |
| stripMappingIndicators(snapshot, config?) | Gỡ indicator khỏi snapshot trước khi gửi server. |
| remapColumnIndexes(items, change) | Dịch lại colIndex (+ colLetter) của mapping theo thao tác cấu trúc cột. |
| toColumnLetter(index) | Chuyển colIndex 0-based → chữ cái Excel (0→'A', 26→'AA'). |
Types & Interfaces
type T_indicatorTarget = { sheetId: string; colIndex: number };
type T_mappingIndicatorConfig = {
mappedStyleId?: string; // @default 'm'
mappedStyle?: T_indicatorStyle;
defaultHeaderStyleId?: string; // @default 'h'
scopeSheetIds?: string[]; // @default mọi sheet trong snapshot
prefix?: string; // @default '● '
headerRowIndex?: number; // @default 0
autoResize?: boolean; // @default true
charWidth?: number; // @default 7.5
padding?: number; // @default 16
};
type T_columnStructureChange = {
sheetId: string;
type: 'insert' | 'remove' | 'move';
startColumn: number;
count: number;
toColumn?: number; // chỉ có với 'move'
};
type T_cellFormatResult = { sheet: string; col: string; colIndex: number; header: string; numfmt: string };
// Ẩn/vô hiệu hoá toolbar item — key tham chiếu T_toolbar_item_id
type T_toolbar_menu_config = Record<string, { hidden?: boolean; disabled?: boolean }>;Lưu ý quan trọng
- ⚠️ CSS Univer (BẮT BUỘC): phải nạp đủ 5 file CSS
@univerjs/*(xem mục Cài đặt). Thiếu = toolbar/panel/menu trắng, layout vỡ. - ⚠️ Chiều cao: Component cao
100%— luôn bọc trong khung có chiều cao cố định, nếu không bảng tính sẽ không hiển thị. - ⚠️ FunctionControl: Tương tác qua getter
FunctionControllấy bằngviewChild, gọi trong/saungAfterViewInit. KHÔNG gọi method private. - ⚠️ Đổi
locale/toolbarMenuConfig: kéo theo reinit toàn bộ Univer (mất undo history hiện tại). - ⚠️ Univer internal DOM: các tính năng numfmt panel/toolbar thao tác DOM nội bộ Univer — đọc
BUSINESS.mdtrước khi sửa.
