@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.
Files changed (2) hide show
  1. package/README.md +299 -2
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,3 +1,300 @@
1
- # spreadsheet
1
+ # @libs-ui/components-spreadsheet
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libs-ui/components-spreadsheet",
3
- "version": "0.2.356-43",
3
+ "version": "0.2.357-1",
4
4
  "peerDependencies": {
5
5
  "@angular/core": "^18.2.0",
6
6
  "@univerjs/core": "0.4.2",