@libs-ui/components-spreadsheet 0.2.356-43 → 0.2.357-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,300 @@
|
|
|
1
|
-
# spreadsheet
|
|
1
|
+
# @libs-ui/components-spreadsheet
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 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.
|
|
4
|
+
|
|
5
|
+
## Giới thiệu
|
|
6
|
+
|
|
7
|
+
`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.
|
|
8
|
+
|
|
9
|
+
> ⚠️ 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.
|
|
10
|
+
|
|
11
|
+
## Tính năng
|
|
12
|
+
|
|
13
|
+
- ✅ Nạp / lưu workbook dạng JSON snapshot (`loadData`, `saveData`, `reset`)
|
|
14
|
+
- ✅ Đị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
|
|
15
|
+
- ✅ Highlight cột đã map (style + prefix + auto-resize) qua luồng auto `mappingIndicatorConfig` + `mappingTargets`
|
|
16
|
+
- ✅ Tự gỡ indicator khi `saveData()` để snapshot gửi server sạch
|
|
17
|
+
- ✅ Bắt sự kiện double-click header cột (`outColumnDblClick`)
|
|
18
|
+
- ✅ Bắt insert / remove / move cột — kể cả undo/redo (`outColumnStructureChange`) + helper `remapColumnIndexes`
|
|
19
|
+
- ✅ Đổi ngôn ngữ runtime (vi / en), ẩn/hiện toolbar & sheet tabs, cấu hình ẩn toolbar item
|
|
20
|
+
- ✅ Standalone, OnPush, Angular Signals — không cần NgModule
|
|
21
|
+
|
|
22
|
+
## Khi nào sử dụng
|
|
23
|
+
|
|
24
|
+
- Hiển thị / chỉnh sửa dữ liệu dạng bảng tính ngay trong app
|
|
25
|
+
- Cho người dùng map cột dữ liệu sang field nghiệp vụ (highlight cột đã map)
|
|
26
|
+
- Cần định dạng số / ngày tuỳ biến kiểu Excel trên dữ liệu nhập
|
|
27
|
+
- Nạp workbook từ JSON và xuất lại snapshot để lưu server
|
|
28
|
+
|
|
29
|
+
## Cài đặt
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @libs-ui/components-spreadsheet
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> Các package `@univerjs/*` đã khai báo là `peerDependencies` trong `package.json` củ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.
|
|
36
|
+
|
|
37
|
+
### ⚠️ Nạp CSS của Univer (BẮT BUỘC — nếu thiếu, toolbar/panel/canvas sẽ vỡ giao diện)
|
|
38
|
+
|
|
39
|
+
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).
|
|
40
|
+
|
|
41
|
+
Khai báo trong `angular.json` / `project.json`:
|
|
42
|
+
|
|
43
|
+
```jsonc
|
|
44
|
+
// architect.build.options.styles (Angular CLI) HOẶC targets.build.options.styles (Nx)
|
|
45
|
+
"styles": [
|
|
46
|
+
"src/styles.scss",
|
|
47
|
+
"./node_modules/@univerjs/design/lib/index.css",
|
|
48
|
+
"./node_modules/@univerjs/ui/lib/index.css",
|
|
49
|
+
"./node_modules/@univerjs/sheets-ui/lib/index.css",
|
|
50
|
+
"./node_modules/@univerjs/sheets-formula-ui/lib/index.css",
|
|
51
|
+
"./node_modules/@univerjs/sheets-numfmt/lib/index.css"
|
|
52
|
+
]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> 🔴 Thứ tự không bắt buộc, nhưng phải đủ **cả 5 file**. Thiếu `@univerjs/ui` hoặc `@univerjs/sheets-ui` là nguyên nhân phổ biến nhất khiến toolbar/panel không hiển thị.
|
|
56
|
+
|
|
57
|
+
## Import
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import {
|
|
61
|
+
LibsUiComponentsSpreadsheetComponent,
|
|
62
|
+
defaultMappingIndicatorConfig,
|
|
63
|
+
remapColumnIndexes,
|
|
64
|
+
toColumnLetter,
|
|
65
|
+
applyMappingIndicators,
|
|
66
|
+
stripMappingIndicators,
|
|
67
|
+
} from '@libs-ui/components-spreadsheet';
|
|
68
|
+
import type {
|
|
69
|
+
T_indicatorTarget,
|
|
70
|
+
T_indicatorStyle,
|
|
71
|
+
T_mappingIndicatorConfig,
|
|
72
|
+
T_columnStructureChange,
|
|
73
|
+
T_cellFormatResult,
|
|
74
|
+
T_toolbar_menu_config,
|
|
75
|
+
T_toolbar_item_id,
|
|
76
|
+
} from '@libs-ui/components-spreadsheet';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Ví dụ sử dụng
|
|
80
|
+
|
|
81
|
+
### Basic — bảng tính cơ bản
|
|
82
|
+
|
|
83
|
+
Component có `:host { height: 100% }` nên BẮT BUỘC bọc trong khung cố định chiều cao để canvas render đúng.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { AfterViewInit, Component, viewChild } from '@angular/core';
|
|
87
|
+
import { LibsUiComponentsSpreadsheetComponent } from '@libs-ui/components-spreadsheet';
|
|
88
|
+
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-basic-example',
|
|
91
|
+
standalone: true,
|
|
92
|
+
imports: [LibsUiComponentsSpreadsheetComponent],
|
|
93
|
+
template: `
|
|
94
|
+
<div class="h-[480px]">
|
|
95
|
+
<libs_ui-components-spreadsheet #sheet [locale]="'vi'" />
|
|
96
|
+
</div>
|
|
97
|
+
`,
|
|
98
|
+
})
|
|
99
|
+
export class BasicExampleComponent implements AfterViewInit {
|
|
100
|
+
private sheet = viewChild<LibsUiComponentsSpreadsheetComponent>('sheet');
|
|
101
|
+
|
|
102
|
+
ngAfterViewInit(): void {
|
|
103
|
+
this.sheet()?.FunctionControl.loadData({
|
|
104
|
+
id: 'wb',
|
|
105
|
+
sheetOrder: ['sheet1'],
|
|
106
|
+
sheets: { sheet1: { id: 'sheet1', name: 'Sheet1', rowCount: 30, columnCount: 26, cellData: {} } },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Mapping Indicator — highlight cột đã map
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { Component, signal } from '@angular/core';
|
|
116
|
+
import { LibsUiComponentsSpreadsheetComponent, defaultMappingIndicatorConfig, type T_indicatorTarget } from '@libs-ui/components-spreadsheet';
|
|
117
|
+
|
|
118
|
+
@Component({
|
|
119
|
+
selector: 'app-mapping-example',
|
|
120
|
+
standalone: true,
|
|
121
|
+
imports: [LibsUiComponentsSpreadsheetComponent],
|
|
122
|
+
template: `
|
|
123
|
+
<div class="h-[480px]">
|
|
124
|
+
<libs_ui-components-spreadsheet
|
|
125
|
+
[mappingIndicatorConfig]="indicatorConfig"
|
|
126
|
+
[mappingTargets]="mappingTargets()"
|
|
127
|
+
(outColumnDblClick)="onColumnDblClick($event)"
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
`,
|
|
131
|
+
})
|
|
132
|
+
export class MappingExampleComponent {
|
|
133
|
+
// Truyền config = "bật" luồng auto: tự apply khi load, tự strip khi save, tự vẽ lại khi targets đổi.
|
|
134
|
+
protected readonly indicatorConfig = defaultMappingIndicatorConfig();
|
|
135
|
+
protected mappingTargets = signal<T_indicatorTarget[]>([
|
|
136
|
+
{ sheetId: 'sheet1', colIndex: 0 },
|
|
137
|
+
{ sheetId: 'sheet1', colIndex: 3 },
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
// Double-click header → mở modal map cột, rồi cập nhật mappingTargets (component tự vẽ lại).
|
|
141
|
+
onColumnDblClick(colIndex: number): void {
|
|
142
|
+
this.mappingTargets.update((list) => [...list, { sheetId: 'sheet1', colIndex }]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Column Events — bắt insert/remove/move cột
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { Component, signal } from '@angular/core';
|
|
151
|
+
import { LibsUiComponentsSpreadsheetComponent, remapColumnIndexes, type T_columnStructureChange } from '@libs-ui/components-spreadsheet';
|
|
152
|
+
|
|
153
|
+
@Component({
|
|
154
|
+
selector: 'app-events-example',
|
|
155
|
+
standalone: true,
|
|
156
|
+
imports: [LibsUiComponentsSpreadsheetComponent],
|
|
157
|
+
template: `
|
|
158
|
+
<div class="h-[480px]">
|
|
159
|
+
<libs_ui-components-spreadsheet
|
|
160
|
+
(outColumnDblClick)="onDblClick($event)"
|
|
161
|
+
(outColumnStructureChange)="onStructureChange($event)"
|
|
162
|
+
/>
|
|
163
|
+
</div>
|
|
164
|
+
`,
|
|
165
|
+
})
|
|
166
|
+
export class EventsExampleComponent {
|
|
167
|
+
protected mappings = signal<{ sheetId: string; colIndex: number; colLetter?: string }[]>([]);
|
|
168
|
+
|
|
169
|
+
onDblClick(colIndex: number): void {
|
|
170
|
+
console.log('Double-click cột', colIndex);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 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).
|
|
174
|
+
onStructureChange(change: T_columnStructureChange): void {
|
|
175
|
+
this.mappings.update((list) => remapColumnIndexes(list, change));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Lưu & nạp lại snapshot
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// Xuất snapshot (tự gỡ indicator nếu luồng auto đang bật) → gửi server.
|
|
184
|
+
const snapshot = this.sheet()?.FunctionControl.saveData();
|
|
185
|
+
|
|
186
|
+
// Nạp lại snapshot đã lưu.
|
|
187
|
+
this.sheet()?.FunctionControl.loadData(snapshot);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## API
|
|
191
|
+
|
|
192
|
+
### Selector
|
|
193
|
+
|
|
194
|
+
`libs_ui-components-spreadsheet` (khớp với `selector` trong `@Component`).
|
|
195
|
+
|
|
196
|
+
### Inputs
|
|
197
|
+
|
|
198
|
+
| Input | Type | Default | Mô tả | Ví dụ |
|
|
199
|
+
|---|---|---|---|---|
|
|
200
|
+
| `[locale]` | `'vi' \| 'en'` | `'vi'` | Ngôn ngữ khởi tạo của toolbar/menu/panel. Đổi runtime qua `FunctionControl.setLocale()`. | `[locale]="'en'"` |
|
|
201
|
+
| `[mappingIndicatorConfig]` | `T_mappingIndicatorConfig` | `undefined` | Truyền = bật luồng auto highlight cột đã map. | `[mappingIndicatorConfig]="indicatorConfig"` |
|
|
202
|
+
| `[mappingTargets]` | `T_indicatorTarget[]` | `[]` | Danh sách cột cần highlight. Đổi runtime → tự vẽ lại header. | `[mappingTargets]="targets()"` |
|
|
203
|
+
| `[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 } }"` |
|
|
204
|
+
|
|
205
|
+
### Outputs
|
|
206
|
+
|
|
207
|
+
| Output | Type | Mô tả |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `(outColumnDblClick)` | `number` | Emit `colIndex` khi double-click header cột. |
|
|
210
|
+
| `(outColumnStructureChange)` | `T_columnStructureChange` | Emit khi insert / remove / move cột (gồm undo/redo). |
|
|
211
|
+
|
|
212
|
+
Handler ví dụ:
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// component.ts
|
|
216
|
+
onColumnDblClick(colIndex: number): void {
|
|
217
|
+
this.openMappingModal(colIndex);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
onColumnStructureChange(change: T_columnStructureChange): void {
|
|
221
|
+
this.mappings.update((list) => remapColumnIndexes(list, change));
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
```html
|
|
226
|
+
<!-- template.html -->
|
|
227
|
+
<libs_ui-components-spreadsheet
|
|
228
|
+
(outColumnDblClick)="onColumnDblClick($event)"
|
|
229
|
+
(outColumnStructureChange)="onColumnStructureChange($event)"
|
|
230
|
+
/>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### FunctionControl Methods
|
|
234
|
+
|
|
235
|
+
Lấy qua `viewChild` → `ref().FunctionControl.method()`.
|
|
236
|
+
|
|
237
|
+
| Method | Mô tả |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `applyNumfmt(row, col, numRows, numCols, pattern)` | Áp numfmt pattern cho một vùng ô. |
|
|
240
|
+
| `extractAllColumnFormats(snapshot)` | Trích pattern numfmt của mọi cột từ snapshot. |
|
|
241
|
+
| `getActiveSheetInfo()` | Lấy `{ sheetId, sheetName }` của sheet đang active (`null` nếu chưa init). |
|
|
242
|
+
| `getColumnNumfmt(sheetId, colIndex)` | Đọc numfmt pattern đại diện của một cột. |
|
|
243
|
+
| `loadData(data)` | Nạp workbook snapshot (JSON) vào bảng tính. |
|
|
244
|
+
| `reset(data?)` | Destroy + khởi tạo lại với `data` (mặc định = workbook rỗng). |
|
|
245
|
+
| `saveData()` | Xuất snapshot JSON (tự gỡ indicator nếu luồng auto đang bật). |
|
|
246
|
+
| `scheduleOnReady(cb)` | Chạy callback sau khi toolbar/numfmt model render xong. |
|
|
247
|
+
| `setLocale(locale)` | Đổi ngôn ngữ runtime (kéo theo reinit Univer). |
|
|
248
|
+
| `toggleSheetTabs()` | Ẩn/hiện thanh sheet tabs (footer). |
|
|
249
|
+
| `toggleToolbar()` | Ẩn/hiện toolbar (header). |
|
|
250
|
+
|
|
251
|
+
Ngoài ra `FunctionControl` expose các signal read-only: `activeCellFormat`, `activeCellRef`, `locale`, `showToolbar`, `showSheetTabs`.
|
|
252
|
+
|
|
253
|
+
### Hàm tiện ích (pure)
|
|
254
|
+
|
|
255
|
+
| Hàm | Mô tả |
|
|
256
|
+
|---|---|
|
|
257
|
+
| `defaultMappingIndicatorConfig()` | Trả config highlight mặc định (style `'m'` xanh, revert `'h'`, prefix `'● '`). |
|
|
258
|
+
| `applyMappingIndicators(snapshot, targets, config?)` | Đánh dấu header cột đã map trên snapshot (không mutate). |
|
|
259
|
+
| `stripMappingIndicators(snapshot, config?)` | Gỡ indicator khỏi snapshot trước khi gửi server. |
|
|
260
|
+
| `remapColumnIndexes(items, change)` | Dịch lại `colIndex` (+ `colLetter`) của mapping theo thao tác cấu trúc cột. |
|
|
261
|
+
| `toColumnLetter(index)` | Chuyển colIndex 0-based → chữ cái Excel (`0→'A'`, `26→'AA'`). |
|
|
262
|
+
|
|
263
|
+
## Types & Interfaces
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
type T_indicatorTarget = { sheetId: string; colIndex: number };
|
|
267
|
+
|
|
268
|
+
type T_mappingIndicatorConfig = {
|
|
269
|
+
mappedStyleId?: string; // @default 'm'
|
|
270
|
+
mappedStyle?: T_indicatorStyle;
|
|
271
|
+
defaultHeaderStyleId?: string; // @default 'h'
|
|
272
|
+
scopeSheetIds?: string[]; // @default mọi sheet trong snapshot
|
|
273
|
+
prefix?: string; // @default '● '
|
|
274
|
+
headerRowIndex?: number; // @default 0
|
|
275
|
+
autoResize?: boolean; // @default true
|
|
276
|
+
charWidth?: number; // @default 7.5
|
|
277
|
+
padding?: number; // @default 16
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
type T_columnStructureChange = {
|
|
281
|
+
sheetId: string;
|
|
282
|
+
type: 'insert' | 'remove' | 'move';
|
|
283
|
+
startColumn: number;
|
|
284
|
+
count: number;
|
|
285
|
+
toColumn?: number; // chỉ có với 'move'
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
type T_cellFormatResult = { sheet: string; col: string; colIndex: number; header: string; numfmt: string };
|
|
289
|
+
|
|
290
|
+
// Ẩn/vô hiệu hoá toolbar item — key tham chiếu T_toolbar_item_id
|
|
291
|
+
type T_toolbar_menu_config = Record<string, { hidden?: boolean; disabled?: boolean }>;
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Lưu ý quan trọng
|
|
295
|
+
|
|
296
|
+
- ⚠️ **CSS Univer (BẮT BUỘC)**: phải nạp đủ 5 file CSS `@univerjs/*` (xem mục [Cài đặt](#2-️-nạp-css-của-univer-bắt-buộc--nếu-thiếu-toolbarpanelcanvas-sẽ-vỡ-giao-diện)). Thiếu = toolbar/panel/menu trắng, layout vỡ.
|
|
297
|
+
- ⚠️ **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ị.
|
|
298
|
+
- ⚠️ **FunctionControl**: Tương tác qua getter `FunctionControl` lấy bằng `viewChild`, gọi trong/sau `ngAfterViewInit`. KHÔNG gọi method private.
|
|
299
|
+
- ⚠️ **Đổi `locale` / `toolbarMenuConfig`**: kéo theo reinit toàn bộ Univer (mất undo history hiện tại).
|
|
300
|
+
- ⚠️ **Univer internal DOM**: các tính năng numfmt panel/toolbar thao tác DOM nội bộ Univer — đọc `BUSINESS.md` trước khi sửa.
|