wms-spreadsheet 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 WMS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # wms-spreadsheet
2
+
3
+ Thư viện Spreadsheet cho React — giao diện giống Google Sheets, hỗ trợ **10.000+ dòng** mượt mà nhờ virtual window.
4
+
5
+ ## Cài đặt
6
+
7
+ ```bash
8
+ npm install wms-spreadsheet
9
+ # hoặc
10
+ pnpm add wms-spreadsheet
11
+ ```
12
+
13
+ **Peer dependencies:** `react` và `react-dom` (^18 hoặc ^19).
14
+
15
+ ## Sử dụng nhanh
16
+
17
+ ```tsx
18
+ import { useRef } from "react";
19
+ import {
20
+ Spreadsheet,
21
+ type ISpreadsheetColumn,
22
+ type ISpreadsheetRef,
23
+ } from "wms-spreadsheet";
24
+ import "wms-spreadsheet/style.css";
25
+
26
+ const COLUMNS: ISpreadsheetColumn[] = [
27
+ { colName: "sku", colText: "Mã SKU", width: 120, showFilter: true },
28
+ { colName: "qty", colText: "SL", width: 80 },
29
+ { colName: "active", colText: "Kích hoạt", width: 90, meta: { type: "switch" } },
30
+ ];
31
+
32
+ const INITIAL_DATA = [
33
+ { sku: "A001", qty: "10", active: "true" },
34
+ { sku: "A002", qty: "5", active: "false" },
35
+ ];
36
+
37
+ function App() {
38
+ const sheetRef = useRef<ISpreadsheetRef>(null);
39
+
40
+ return (
41
+ <div style={{ width: "100%", height: "600px" }}>
42
+ <Spreadsheet
43
+ ref={sheetRef}
44
+ columns={COLUMNS}
45
+ initialData={INITIAL_DATA}
46
+ rowCount={1000}
47
+ />
48
+ </div>
49
+ );
50
+ }
51
+ ```
52
+
53
+ ## Tính năng
54
+
55
+ - Virtual window 2 chiều — chỉ render cell trong viewport
56
+ - API imperative qua `ref` — `setCellValue`, `loadData`, `mergeCells`, …
57
+ - Cell types: `text`, `select`, `multiSelect`, `boolean`, `switch`, `date`, `custom`
58
+ - Copy/paste range (Ctrl+C / Ctrl+V), filter & sort cột, frozen columns
59
+ - Merge cells, resize cột/hàng, đa ngôn ngữ (`locale` prop)
60
+
61
+ ## Tài liệu đầy đủ
62
+
63
+ Xem [README trên GitHub](https://github.com/truongnguyen5x/wms-spreadsheet#readme) để biết API ref, cell meta, custom cells, locale, xử lý lỗi, v.v.
64
+
65
+ ## License
66
+
67
+ MIT
@@ -0,0 +1,337 @@
1
+ import { ForwardRefExoticComponent } from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { RefAttributes } from 'react';
4
+
5
+ export declare function cellKey(row: number, col: number): string;
6
+
7
+ export declare class CellStore {
8
+ private data;
9
+ private listeners;
10
+ getValue(row: number, col: number): string;
11
+ setValue(row: number, col: number, value: string): void;
12
+ setValues(cells: ICellStoreInput[]): void;
13
+ subscribe(row: number, col: number, listener: () => void): () => void;
14
+ getAllData(): ISheetData;
15
+ loadData(data: ISheetData): void;
16
+ clearAndLoad(data: ISheetData): void;
17
+ getKeys(): string[];
18
+ parseKey(key: string): {
19
+ row: number;
20
+ col: number;
21
+ };
22
+ }
23
+
24
+ export declare const COLUMN_HEADER_HEIGHT = 28;
25
+
26
+ export declare function columnLabel(col: number): string;
27
+
28
+ export declare type DeepPartial<T> = {
29
+ [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
30
+ };
31
+
32
+ export declare const DEFAULT_COLUMN_WIDTH = 100;
33
+
34
+ export declare const DEFAULT_DATE_FORMAT = "DD/MM/YYYY";
35
+
36
+ export declare const DEFAULT_OVERSCAN = 3;
37
+
38
+ export declare const DEFAULT_ROW_HEIGHT = 28;
39
+
40
+ export declare const DEFAULT_SPREADSHEET_LOCALE: ISpreadsheetLocale;
41
+
42
+ export declare const FILTER_BLANK_VALUE = "__BLANK__";
43
+
44
+ export declare interface ICellAddress {
45
+ row: number;
46
+ col: number;
47
+ }
48
+
49
+ export declare interface ICellEditorParams extends ICellRenderParams {
50
+ top: number;
51
+ left: number;
52
+ width: number;
53
+ height: number;
54
+ onCommit: (value: string, direction?: ICommitDirection) => void;
55
+ onCancel: () => void;
56
+ }
57
+
58
+ export declare interface ICellInput {
59
+ row: number;
60
+ col?: number | null;
61
+ colName?: string;
62
+ value: TCellValue;
63
+ }
64
+
65
+ export declare interface ICellMeta {
66
+ type?: TCellType;
67
+ options?: ISelectOption[];
68
+ /** Định dạng ngày lưu và hiển thị. Mặc định DD/MM/YYYY */
69
+ dateFormat?: string;
70
+ /** Ngày sớm nhất được chọn (chuỗi theo dateFormat của cell/cột) */
71
+ minDate?: string;
72
+ /** Ngày muộn nhất được chọn (chuỗi theo dateFormat của cell/cột) */
73
+ maxDate?: string;
74
+ customKey?: string;
75
+ customProps?: Record<string, unknown>;
76
+ invalid?: boolean;
77
+ disabled?: boolean;
78
+ }
79
+
80
+ export declare interface ICellMetaInput {
81
+ row: number;
82
+ col?: number | null;
83
+ colName?: string;
84
+ meta: Partial<ICellMeta>;
85
+ }
86
+
87
+ export declare interface ICellRenderParams {
88
+ row: number;
89
+ col: number;
90
+ value: string;
91
+ meta: ICellMeta;
92
+ isActive: boolean;
93
+ }
94
+
95
+ /** Input nội bộ cho CellStore — luôn dùng index cột. */
96
+ declare interface ICellStoreInput {
97
+ row: number;
98
+ col: number;
99
+ value: string;
100
+ }
101
+
102
+ export declare interface IClipboardData {
103
+ range: INormalizedRange;
104
+ values: string[][];
105
+ }
106
+
107
+ export declare interface IColumnFilterState {
108
+ condition: TFilterCondition;
109
+ conditionValue?: string;
110
+ /** null = không giới hạn checkbox value. */
111
+ selectedValues: string[] | null;
112
+ }
113
+
114
+ export declare interface IColumnHeaderRenderParams {
115
+ col: number;
116
+ column: ISpreadsheetColumn;
117
+ }
118
+
119
+ export declare interface IColumnSortState {
120
+ col: number;
121
+ direction: TSortDirection;
122
+ }
123
+
124
+ export declare type ICommitDirection = "stay" | "down" | "right";
125
+
126
+ export declare interface ICustomCellDefinition {
127
+ render: (params: ICellRenderParams) => ReactNode;
128
+ editor?: (params: ICellEditorParams) => ReactNode;
129
+ }
130
+
131
+ export declare interface IMergedRange {
132
+ anchorRow: number;
133
+ anchorCol: number;
134
+ rowSpan: number;
135
+ colSpan: number;
136
+ }
137
+
138
+ declare interface IMetaStoreInput {
139
+ row: number;
140
+ col: number;
141
+ meta: Partial<ICellMeta>;
142
+ }
143
+
144
+ export declare interface INormalizedRange {
145
+ startRow: number;
146
+ endRow: number;
147
+ startCol: number;
148
+ endCol: number;
149
+ }
150
+
151
+ export declare interface ISelection {
152
+ anchor: ICellAddress;
153
+ focus: ICellAddress;
154
+ }
155
+
156
+ export declare interface ISelectOption {
157
+ id: string;
158
+ label: string;
159
+ color?: string;
160
+ }
161
+
162
+ export declare type ISheetData = Record<string, string>;
163
+
164
+ export declare interface ISpreadsheetColumn {
165
+ /** Key trong object data và API colName. */
166
+ colName: string;
167
+ width: number;
168
+ /** Text hiển thị header; mặc định colName. */
169
+ colText?: string;
170
+ /** Custom render header cell; ưu tiên hơn colText. */
171
+ colRender?: (params: IColumnHeaderRenderParams) => ReactNode;
172
+ /** Hiển thị icon/filter popup ở header cột. */
173
+ showFilter?: boolean;
174
+ /** Meta mặc định cho toàn cột; cell meta override từng field. */
175
+ meta?: Partial<ICellMeta>;
176
+ /** Căn ngang nội dung body cell. Mặc định "left". Không áp header. */
177
+ horizontalAlign?: THorizontalAlign;
178
+ /** Căn dọc nội dung body cell. Mặc định "top". Không áp header. */
179
+ verticalAlign?: TVerticalAlign;
180
+ /** Override render cell body; ưu tiên hơn registry. */
181
+ cellRender?: (params: ICellRenderParams) => ReactNode;
182
+ /** Override editor cell; ưu tiên hơn registry. */
183
+ cellEditor?: (params: ICellEditorParams) => ReactNode;
184
+ }
185
+
186
+ export declare interface ISpreadsheetError {
187
+ code: TSpreadsheetErrorCode;
188
+ message: string;
189
+ }
190
+
191
+ export declare interface ISpreadsheetLocale {
192
+ errors: {
193
+ mergeInvalidRange: string;
194
+ mergeOverlap: string;
195
+ mergeWhileSortFilterActive: string;
196
+ filterWhileMerged: string;
197
+ sortWhileMerged: string;
198
+ copyMergedNotAllowed: string;
199
+ pasteMergedNotAllowed: string;
200
+ };
201
+ filter: {
202
+ columnAriaLabel: string;
203
+ dialogAriaLabel: string;
204
+ sortAsc: string;
205
+ sortDesc: string;
206
+ filterByCondition: string;
207
+ filterByValue: string;
208
+ valuePlaceholder: string;
209
+ searchPlaceholder: string;
210
+ selectAll: string;
211
+ clear: string;
212
+ reset: string;
213
+ ok: string;
214
+ cancel: string;
215
+ blankCells: string;
216
+ conditions: Record<TFilterCondition, string>;
217
+ };
218
+ datepicker: {
219
+ dialogAriaLabel: string;
220
+ prevMonthAriaLabel: string;
221
+ nextMonthAriaLabel: string;
222
+ today: string;
223
+ clear: string;
224
+ monthNames: readonly string[];
225
+ weekdayLabels: readonly string[];
226
+ };
227
+ }
228
+
229
+ export declare interface ISpreadsheetProps {
230
+ rowCount: number;
231
+ columnCount: number;
232
+ columns?: ISpreadsheetColumn[];
233
+ rowHeight?: number;
234
+ /** Chiều cao hàng header cột (px). Mặc định COLUMN_HEADER_HEIGHT. */
235
+ colHeaderHeight?: number;
236
+ columnWidth?: number;
237
+ overscan?: number;
238
+ className?: string;
239
+ onChange?: (changes: ICellInput[]) => void;
240
+ onError?: (error: ISpreadsheetError) => void;
241
+ onColumnResize?: (col: number, width: number) => void;
242
+ onRowResize?: (row: number, height: number) => void;
243
+ initialData?: TSheetDataInput;
244
+ /** Số cột cố định từ trái (0 = không cố định). Ví dụ 2 → cột A, B. */
245
+ frozenColumnCount?: number;
246
+ /** Registry custom cell render/editor theo customKey. */
247
+ customCellRegistry?: Record<string, ICustomCellDefinition>;
248
+ /** UI strings; mặc định tiếng Anh. Truyền partial để override đa ngôn ngữ. */
249
+ locale?: DeepPartial<ISpreadsheetLocale>;
250
+ }
251
+
252
+ export declare interface ISpreadsheetRef {
253
+ setCellValue(row: number, col: number | null, value: TCellValue, colName?: string): void;
254
+ getCellValue(row: number, col: number | null, colName?: string): TCellValue;
255
+ setCellValues(cells: ICellInput[]): void;
256
+ loadData(data: TSheetDataInput): void;
257
+ getData(): TSheetDataOutput;
258
+ getRowData(row: number): TSheetRowDataOutput;
259
+ getActiveCell(): ICellAddress | null;
260
+ setActiveCell(cell: ICellAddress): void;
261
+ getSelection(): ISelection | null;
262
+ setSelection(selection: ISelection): void;
263
+ setCellMeta(row: number, col: number | null, meta: Partial<ICellMeta>, colName?: string): void;
264
+ getCellMeta(row: number, col: number | null, colName?: string): ICellMeta;
265
+ setCellsMeta(cells: ICellMetaInput[]): void;
266
+ mergeCells(range?: INormalizedRange): boolean;
267
+ unmergeCells(row?: number, col?: number): boolean;
268
+ getMergedRanges(): IMergedRange[];
269
+ hasMergedCells(): boolean;
270
+ }
271
+
272
+ export declare class MergeStore {
273
+ private anchors;
274
+ private coveredToAnchor;
275
+ private listeners;
276
+ private revision;
277
+ getRevision(): number;
278
+ subscribe(listener: () => void): () => void;
279
+ private notify;
280
+ getAll(): IMergedRange[];
281
+ hasAny(): boolean;
282
+ getRole(row: number, col: number): TMergeCellRole;
283
+ resolveAnchor(row: number, col: number): ICellAddress;
284
+ getSpan(row: number, col: number): {
285
+ rowSpan: number;
286
+ colSpan: number;
287
+ };
288
+ getMergeAt(row: number, col: number): IMergedRange | null;
289
+ rangeIntersectsMerge(range: INormalizedRange): boolean;
290
+ expandRange(range: INormalizedRange): INormalizedRange;
291
+ canMerge(range: INormalizedRange): boolean;
292
+ merge(range: INormalizedRange, store: CellStore, metaStore: MetaStore): boolean;
293
+ unmerge(row: number, col: number): boolean;
294
+ clear(): void;
295
+ }
296
+
297
+ export declare class MetaStore {
298
+ private data;
299
+ private listeners;
300
+ getMeta(row: number, col: number): ICellMeta;
301
+ getStoredMeta(row: number, col: number): Partial<ICellMeta>;
302
+ setMeta(row: number, col: number, meta: Partial<ICellMeta>): void;
303
+ setMetas(cells: IMetaStoreInput[]): void;
304
+ subscribe(row: number, col: number, listener: () => void): () => void;
305
+ }
306
+
307
+ export declare function resolveSpreadsheetLocale(partial?: DeepPartial<ISpreadsheetLocale>): ISpreadsheetLocale;
308
+
309
+ export declare const ROW_HEADER_WIDTH = 46;
310
+
311
+ export declare const Spreadsheet: ForwardRefExoticComponent<ISpreadsheetProps & RefAttributes<ISpreadsheetRef>>;
312
+
313
+ export declare type TCellType = "text" | "select" | "multiSelect" | "boolean" | "switch" | "date" | "custom";
314
+
315
+ export declare type TCellValue = string | string[];
316
+
317
+ export declare type TFilterCondition = "none" | "isEmpty" | "isNotEmpty" | "isEqualTo" | "isNotEqualTo" | "beginsWith" | "endsWith" | "contains" | "doesNotContain";
318
+
319
+ export declare type THorizontalAlign = "left" | "center" | "right";
320
+
321
+ declare type TMergeCellRole = "anchor" | "covered" | "none";
322
+
323
+ export declare type TSheetDataInput = ISheetData | string[][] | TSheetRowRecord[];
324
+
325
+ export declare type TSheetDataOutput = ISheetData | TSheetRowRecord[];
326
+
327
+ export declare type TSheetRowDataOutput = ISheetData | TSheetRowRecord;
328
+
329
+ declare type TSheetRowRecord = Record<string, TCellValue>;
330
+
331
+ export declare type TSortDirection = "asc" | "desc";
332
+
333
+ export declare type TSpreadsheetErrorCode = "MERGE_COPY_NOT_ALLOWED" | "MERGE_PASTE_NOT_ALLOWED" | "MERGE_INVALID_RANGE" | "MERGE_OVERLAP" | "MERGE_SORT_FILTER_ACTIVE";
334
+
335
+ export declare type TVerticalAlign = "top" | "middle" | "bottom";
336
+
337
+ export { }