@extable/core 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/README.md +77 -0
- package/dist/address.d.ts +9 -0
- package/dist/cellValueCodec.d.ts +4 -0
- package/dist/commandQueue.d.ts +20 -0
- package/dist/dataModel.d.ts +127 -0
- package/dist/dateUtils.d.ts +5 -0
- package/dist/fillHandle.d.ts +17 -0
- package/dist/findReplace.d.ts +53 -0
- package/dist/geometry.d.ts +5 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +4445 -0
- package/dist/index.js.map +1 -0
- package/dist/lockManager.d.ts +15 -0
- package/dist/renderers.d.ts +129 -0
- package/dist/selectionManager.d.ts +135 -0
- package/dist/styleResolver.d.ts +10 -0
- package/dist/types.d.ts +300 -0
- package/dist/utils.d.ts +3 -0
- package/dist/validation.d.ts +2 -0
- package/package.json +44 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LockMode, ServerAdapter, UserInfo } from './types';
|
|
2
|
+
export declare class LockManager {
|
|
3
|
+
private lockedRows;
|
|
4
|
+
private mode;
|
|
5
|
+
private server?;
|
|
6
|
+
private user?;
|
|
7
|
+
constructor(mode: LockMode, server?: ServerAdapter, user?: UserInfo);
|
|
8
|
+
setMode(mode: LockMode): void;
|
|
9
|
+
setUser(user?: UserInfo): void;
|
|
10
|
+
selectRow(rowId: string): Promise<void>;
|
|
11
|
+
unlockOnMove(currentRowId?: string): Promise<void>;
|
|
12
|
+
unlockOnCommit(keepRowId?: string): Promise<void>;
|
|
13
|
+
unlockRows(rowIds: string[]): Promise<void>;
|
|
14
|
+
getLockedRows(): Set<string>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { DataModel } from "./dataModel";
|
|
2
|
+
import type { SelectionRange, EditMode } from "./types";
|
|
3
|
+
export interface ViewportState {
|
|
4
|
+
scrollTop: number;
|
|
5
|
+
scrollLeft: number;
|
|
6
|
+
clientWidth: number;
|
|
7
|
+
clientHeight: number;
|
|
8
|
+
deltaX: number;
|
|
9
|
+
deltaY: number;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Renderer {
|
|
13
|
+
mount(root: HTMLElement): void;
|
|
14
|
+
render(state?: ViewportState): void;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
getCellElements(): NodeListOf<HTMLElement> | null;
|
|
17
|
+
hitTest(event: MouseEvent): {
|
|
18
|
+
rowId: string;
|
|
19
|
+
colKey: string | null;
|
|
20
|
+
element?: HTMLElement;
|
|
21
|
+
rect: DOMRect;
|
|
22
|
+
} | null;
|
|
23
|
+
setActiveCell(rowId: string | null, colKey: string | null): void;
|
|
24
|
+
setSelection(ranges: SelectionRange[]): void;
|
|
25
|
+
}
|
|
26
|
+
export declare class HTMLRenderer implements Renderer {
|
|
27
|
+
private dataModel;
|
|
28
|
+
private tableEl;
|
|
29
|
+
private defaultRowHeight;
|
|
30
|
+
private rowHeaderWidth;
|
|
31
|
+
private activeRowId;
|
|
32
|
+
private activeColKey;
|
|
33
|
+
private selection;
|
|
34
|
+
private valueFormatCache;
|
|
35
|
+
private measureCache;
|
|
36
|
+
private frame;
|
|
37
|
+
constructor(dataModel: DataModel);
|
|
38
|
+
mount(root: HTMLElement): void;
|
|
39
|
+
setActiveCell(rowId: string | null, colKey: string | null): void;
|
|
40
|
+
setSelection(ranges: SelectionRange[]): void;
|
|
41
|
+
render(_state?: ViewportState): void;
|
|
42
|
+
destroy(): void;
|
|
43
|
+
getCellElements(): NodeListOf<HTMLElement> | null;
|
|
44
|
+
hitTest(event: MouseEvent): {
|
|
45
|
+
rowId: string;
|
|
46
|
+
colKey: string;
|
|
47
|
+
element: HTMLElement;
|
|
48
|
+
rect: DOMRect;
|
|
49
|
+
} | {
|
|
50
|
+
rowId: string;
|
|
51
|
+
colKey: null;
|
|
52
|
+
element: HTMLElement;
|
|
53
|
+
rect: DOMRect;
|
|
54
|
+
} | null;
|
|
55
|
+
private renderHeader;
|
|
56
|
+
private renderRow;
|
|
57
|
+
private updateActiveClasses;
|
|
58
|
+
private applySelectionClasses;
|
|
59
|
+
private formatValue;
|
|
60
|
+
}
|
|
61
|
+
export declare class CanvasRenderer implements Renderer {
|
|
62
|
+
private getEditMode;
|
|
63
|
+
private static readonly MAX_CANVAS_DIM_PX;
|
|
64
|
+
private static readonly ROW_HEIGHT_MEASURE_CHUNK;
|
|
65
|
+
private static readonly ROW_HEIGHT_MEASURE_TIME_BUDGET_MS;
|
|
66
|
+
private static readonly TEXT_MEASURE_CACHE_MAX;
|
|
67
|
+
private root;
|
|
68
|
+
private canvas;
|
|
69
|
+
private spacer;
|
|
70
|
+
private overlayLayer;
|
|
71
|
+
private tooltip;
|
|
72
|
+
private tooltipTarget;
|
|
73
|
+
private tooltipMessage;
|
|
74
|
+
private dataModel;
|
|
75
|
+
private readonly rowHeight;
|
|
76
|
+
private readonly headerHeight;
|
|
77
|
+
private readonly lineHeight;
|
|
78
|
+
private readonly padding;
|
|
79
|
+
private readonly rowHeaderWidth;
|
|
80
|
+
private activeRowId;
|
|
81
|
+
private activeColKey;
|
|
82
|
+
private selection;
|
|
83
|
+
private valueFormatCache;
|
|
84
|
+
private textMeasureCache;
|
|
85
|
+
private frame;
|
|
86
|
+
private cursorTimer;
|
|
87
|
+
private pendingCursorPoint;
|
|
88
|
+
private hoverHeaderColKey;
|
|
89
|
+
private hoverHeaderIcon;
|
|
90
|
+
private rowHeightCacheKey;
|
|
91
|
+
private rowHeightMeasuredVersion;
|
|
92
|
+
private rowHeightMeasureRaf;
|
|
93
|
+
private rowHeightMeasureTask;
|
|
94
|
+
private heightIndex;
|
|
95
|
+
constructor(dataModel: DataModel, getEditMode?: () => EditMode);
|
|
96
|
+
mount(root: HTMLElement): void;
|
|
97
|
+
setActiveCell(rowId: string | null, colKey: string | null): void;
|
|
98
|
+
setSelection(ranges: SelectionRange[]): void;
|
|
99
|
+
render(state?: ViewportState): void;
|
|
100
|
+
destroy(): void;
|
|
101
|
+
private ensureHeightIndex;
|
|
102
|
+
private applyRowHeightUpdates;
|
|
103
|
+
private getRowHeightCacheKey;
|
|
104
|
+
private cancelRowHeightMeasurement;
|
|
105
|
+
private scheduleRowHeightMeasurement;
|
|
106
|
+
private runRowHeightMeasurement;
|
|
107
|
+
private handleClick;
|
|
108
|
+
getCellElements(): null;
|
|
109
|
+
hitTest(event: MouseEvent): {
|
|
110
|
+
rowId: string;
|
|
111
|
+
colKey: string;
|
|
112
|
+
rect: DOMRect;
|
|
113
|
+
} | {
|
|
114
|
+
rowId: string;
|
|
115
|
+
colKey: null;
|
|
116
|
+
rect: DOMRect;
|
|
117
|
+
} | null;
|
|
118
|
+
private isPointInSelection;
|
|
119
|
+
private handlePointerMove;
|
|
120
|
+
private handlePointerLeave;
|
|
121
|
+
private positionTooltipAtRect;
|
|
122
|
+
private refreshTooltipPosition;
|
|
123
|
+
private getCellRect;
|
|
124
|
+
private updateCanvasCursor;
|
|
125
|
+
private measureRowHeight;
|
|
126
|
+
private wrapLines;
|
|
127
|
+
private drawCellText;
|
|
128
|
+
private formatValue;
|
|
129
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Command, EditMode, SelectionRange } from "./types";
|
|
2
|
+
import type { DataModel } from "./dataModel";
|
|
3
|
+
type EditHandler = (cmd: Command, commit: boolean) => void;
|
|
4
|
+
type RowSelectHandler = (rowId: string) => void;
|
|
5
|
+
type MoveHandler = (rowId?: string) => void;
|
|
6
|
+
type HitTest = (event: MouseEvent) => {
|
|
7
|
+
rowId: string;
|
|
8
|
+
colKey: string | null;
|
|
9
|
+
element?: HTMLElement;
|
|
10
|
+
rect: DOMRect;
|
|
11
|
+
} | null;
|
|
12
|
+
type ActiveChange = (rowId: string | null, colKey: string | null) => void;
|
|
13
|
+
type ContextMenuHandler = (rowId: string | null, colKey: string | null, clientX: number, clientY: number) => void;
|
|
14
|
+
type SelectionChange = (ranges: SelectionRange[]) => void;
|
|
15
|
+
type UndoRedoHandler = () => void;
|
|
16
|
+
export declare class SelectionManager {
|
|
17
|
+
private dataModel;
|
|
18
|
+
private onActiveChange;
|
|
19
|
+
private onSelectionChange;
|
|
20
|
+
private onUndo;
|
|
21
|
+
private onRedo;
|
|
22
|
+
private root;
|
|
23
|
+
private editMode;
|
|
24
|
+
private onEdit;
|
|
25
|
+
private onRowSelect;
|
|
26
|
+
private onMove;
|
|
27
|
+
private hitTest;
|
|
28
|
+
private onContextMenu;
|
|
29
|
+
private handleDocumentContextMenu;
|
|
30
|
+
private selectionRanges;
|
|
31
|
+
private inputEl;
|
|
32
|
+
private floatingInputWrapper;
|
|
33
|
+
private selectionInput;
|
|
34
|
+
private copyToastEl;
|
|
35
|
+
private copyToastTimer;
|
|
36
|
+
private selectionMode;
|
|
37
|
+
private lastBooleanCell;
|
|
38
|
+
private selectionAnchor;
|
|
39
|
+
private dragging;
|
|
40
|
+
private dragStart;
|
|
41
|
+
private pointerDownClient;
|
|
42
|
+
private dragMoved;
|
|
43
|
+
private dragSelectionChanged;
|
|
44
|
+
private suppressNextClick;
|
|
45
|
+
private fillDragging;
|
|
46
|
+
private fillSource;
|
|
47
|
+
private fillEndRowIndex;
|
|
48
|
+
private rootCursorBackup;
|
|
49
|
+
private lastPointerClient;
|
|
50
|
+
private autoScrollRaf;
|
|
51
|
+
private autoScrollActive;
|
|
52
|
+
private activeCell;
|
|
53
|
+
private activeHost;
|
|
54
|
+
private activeHostOriginalText;
|
|
55
|
+
private composing;
|
|
56
|
+
private lastCompositionEnd;
|
|
57
|
+
private readonly handleSelectionBlur;
|
|
58
|
+
private isCellReadonly;
|
|
59
|
+
constructor(root: HTMLElement, editMode: EditMode, onEdit: EditHandler, onRowSelect: RowSelectHandler, onMove: MoveHandler, hitTest: HitTest, dataModel: DataModel, isCellReadonly: (rowId: string, colKey: string) => boolean, onActiveChange: ActiveChange, onContextMenu: ContextMenuHandler, onSelectionChange: SelectionChange, onUndo: UndoRedoHandler, onRedo: UndoRedoHandler);
|
|
60
|
+
setEditMode(mode: EditMode): void;
|
|
61
|
+
syncAfterRowsChanged(): void;
|
|
62
|
+
navigateToCell(rowId: string, colKey: string): void;
|
|
63
|
+
cancelEditing(): void;
|
|
64
|
+
destroy(): void;
|
|
65
|
+
onScroll(scrollTop: number, scrollLeft: number): void;
|
|
66
|
+
private bind;
|
|
67
|
+
private updateFillHandleFlag;
|
|
68
|
+
private findColumn;
|
|
69
|
+
private pad2;
|
|
70
|
+
private formatLocalDateForInput;
|
|
71
|
+
private formatLocalTimeForInput;
|
|
72
|
+
private formatLocalDateTimeForInput;
|
|
73
|
+
private normalizeTemporalInitialValue;
|
|
74
|
+
private ensureCopyToast;
|
|
75
|
+
private teardownCopyToast;
|
|
76
|
+
private positionCopyToast;
|
|
77
|
+
private showCopyToast;
|
|
78
|
+
private ensureSelectionInput;
|
|
79
|
+
private focusSelectionInput;
|
|
80
|
+
private openEditorAtActiveCell;
|
|
81
|
+
private escapeCssAttrValue;
|
|
82
|
+
private findHtmlCellElement;
|
|
83
|
+
private computeCanvasCellRect;
|
|
84
|
+
private computeCanvasCellBoxContent;
|
|
85
|
+
private getCanvasCellMetrics;
|
|
86
|
+
private ensureVisibleCell;
|
|
87
|
+
private moveActiveCell;
|
|
88
|
+
private handlePointerDown;
|
|
89
|
+
private getHitAtClientPoint;
|
|
90
|
+
private updateDragFromClientPoint;
|
|
91
|
+
private updateFillDragFromClientPoint;
|
|
92
|
+
private computeAutoScrollDelta;
|
|
93
|
+
private startAutoScroll;
|
|
94
|
+
private stopAutoScroll;
|
|
95
|
+
private handlePointerMove;
|
|
96
|
+
private handlePointerUp;
|
|
97
|
+
private commitFill;
|
|
98
|
+
private teardownSelectionInput;
|
|
99
|
+
private handleSelectionCopy;
|
|
100
|
+
private handleSelectionCut;
|
|
101
|
+
private handleSelectionPaste;
|
|
102
|
+
private handleSelectionCompositionStart;
|
|
103
|
+
copySelection(): Promise<void>;
|
|
104
|
+
pasteFromClipboard(): Promise<void>;
|
|
105
|
+
private handleSelectionKeydown;
|
|
106
|
+
private getActiveIndices;
|
|
107
|
+
private normalizeRange;
|
|
108
|
+
private getCopyRange;
|
|
109
|
+
private cellToClipboardString;
|
|
110
|
+
private escapeHtml;
|
|
111
|
+
private buildSelectionClipboardPayload;
|
|
112
|
+
private clearSelectionValues;
|
|
113
|
+
private parseClipboardGrid;
|
|
114
|
+
private parseTsv;
|
|
115
|
+
private parseHtmlTable;
|
|
116
|
+
private coerceCellValue;
|
|
117
|
+
private applyClipboardGrid;
|
|
118
|
+
private createEditor;
|
|
119
|
+
private autosize;
|
|
120
|
+
private positionFloatingContentBox;
|
|
121
|
+
private handleClick;
|
|
122
|
+
private toggleBoolean;
|
|
123
|
+
private handleContextMenu;
|
|
124
|
+
private applySelectionFromHit;
|
|
125
|
+
private mergeRanges;
|
|
126
|
+
private activateCellElement;
|
|
127
|
+
private activateFloating;
|
|
128
|
+
private handleKey;
|
|
129
|
+
private commitEdit;
|
|
130
|
+
private readActiveValue;
|
|
131
|
+
private bindImmediateCommit;
|
|
132
|
+
private cancelEdit;
|
|
133
|
+
private teardownInput;
|
|
134
|
+
}
|
|
135
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ColumnSchema, ResolvedCellStyle, StyleDelta } from "./types";
|
|
2
|
+
import type { DataModel } from "./dataModel";
|
|
3
|
+
export declare function columnFormatToStyle(col: ColumnSchema): ResolvedCellStyle;
|
|
4
|
+
export declare function mergeStyle(base: ResolvedCellStyle, override?: StyleDelta | null): ResolvedCellStyle;
|
|
5
|
+
export declare function resolveCellStyles(dataModel: DataModel, rowId: string, col: ColumnSchema): {
|
|
6
|
+
columnStyle: ResolvedCellStyle;
|
|
7
|
+
cellStyle: StyleDelta;
|
|
8
|
+
resolved: ResolvedCellStyle;
|
|
9
|
+
};
|
|
10
|
+
export declare function styleToCssText(style: ResolvedCellStyle): string;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
export type CellPrimitive = string | number | boolean | null;
|
|
2
|
+
export type CellValue = CellPrimitive | Date | {
|
|
3
|
+
kind: "enum";
|
|
4
|
+
value: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "tags";
|
|
7
|
+
values: string[];
|
|
8
|
+
};
|
|
9
|
+
export type ColumnType = "string" | "number" | "boolean" | "datetime" | "date" | "time" | "enum" | "tags";
|
|
10
|
+
export type ResolvedCellStyle = {
|
|
11
|
+
backgroundColor?: string;
|
|
12
|
+
textColor?: string;
|
|
13
|
+
bold?: boolean;
|
|
14
|
+
italic?: boolean;
|
|
15
|
+
underline?: boolean;
|
|
16
|
+
strike?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type StyleDelta = Partial<ResolvedCellStyle>;
|
|
19
|
+
export type DiagnosticLevel = "warning" | "error";
|
|
20
|
+
export type CellDiagnostic = {
|
|
21
|
+
level: DiagnosticLevel;
|
|
22
|
+
message: string;
|
|
23
|
+
source: "formula" | "conditionalStyle";
|
|
24
|
+
};
|
|
25
|
+
export type FormulaOk = string | boolean | number | Date;
|
|
26
|
+
export type FormulaWarn<T extends FormulaOk> = readonly [value: T, warning: Error];
|
|
27
|
+
export type FormulaReturn<T extends FormulaOk = FormulaOk> = T | FormulaWarn<T>;
|
|
28
|
+
export type ConditionalStyleFn<TData extends object = object> = (data: TData) => StyleDelta | null | Error;
|
|
29
|
+
export type Updater<T> = T | ((oldValue: T) => T);
|
|
30
|
+
export type CellAddress = {
|
|
31
|
+
rowId: string;
|
|
32
|
+
colKey: string;
|
|
33
|
+
} | {
|
|
34
|
+
rowIndex: number;
|
|
35
|
+
colIndex: number;
|
|
36
|
+
} | {
|
|
37
|
+
rowId: string;
|
|
38
|
+
colIndex: number;
|
|
39
|
+
} | {
|
|
40
|
+
rowIndex: number;
|
|
41
|
+
colKey: string;
|
|
42
|
+
};
|
|
43
|
+
export type CellTarget = {
|
|
44
|
+
rowId: string;
|
|
45
|
+
colKey: string;
|
|
46
|
+
};
|
|
47
|
+
export interface ColumnSchema<TData extends object = object, RData extends object = TData, TType extends ColumnType = ColumnType, K extends string = string> {
|
|
48
|
+
key: K;
|
|
49
|
+
type: TType;
|
|
50
|
+
header?: string;
|
|
51
|
+
readonly?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* When true, enforce uniqueness within this column (per table).
|
|
54
|
+
* Duplicate (non-empty) values mark all duplicated cells as invalid.
|
|
55
|
+
*/
|
|
56
|
+
unique?: boolean;
|
|
57
|
+
nullable?: boolean;
|
|
58
|
+
string?: {
|
|
59
|
+
maxLength?: number;
|
|
60
|
+
regex?: string;
|
|
61
|
+
};
|
|
62
|
+
number?: {
|
|
63
|
+
precision?: number;
|
|
64
|
+
scale?: number;
|
|
65
|
+
signed?: boolean;
|
|
66
|
+
thousandSeparator?: boolean;
|
|
67
|
+
negativeRed?: boolean;
|
|
68
|
+
format?: string;
|
|
69
|
+
};
|
|
70
|
+
enum?: {
|
|
71
|
+
options: string[];
|
|
72
|
+
allowCustom?: boolean;
|
|
73
|
+
};
|
|
74
|
+
tags?: {
|
|
75
|
+
options: string[];
|
|
76
|
+
allowCustom?: boolean;
|
|
77
|
+
};
|
|
78
|
+
booleanDisplay?: "checkbox" | string | [string, string];
|
|
79
|
+
dateFormat?: string;
|
|
80
|
+
timeFormat?: string;
|
|
81
|
+
dateTimeFormat?: string;
|
|
82
|
+
width?: number;
|
|
83
|
+
wrapText?: boolean;
|
|
84
|
+
style?: {
|
|
85
|
+
textColor?: string;
|
|
86
|
+
backgroundColor?: string;
|
|
87
|
+
align?: "left" | "right" | "center";
|
|
88
|
+
decorations?: {
|
|
89
|
+
strike?: boolean;
|
|
90
|
+
underline?: boolean;
|
|
91
|
+
bold?: boolean;
|
|
92
|
+
italic?: boolean;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
formula?: (data: TData) => unknown;
|
|
96
|
+
conditionalStyle?: ConditionalStyleFn<RData>;
|
|
97
|
+
}
|
|
98
|
+
export type InferSchemaData<TColumns extends readonly ColumnSchema<any>[]> = {
|
|
99
|
+
[I in keyof TColumns]: TColumns[I] extends ColumnSchema<infer TData, any, infer TType, infer K> ? K extends keyof TData ? TData[K] : never : never;
|
|
100
|
+
} extends (infer U)[] ? U extends object ? U : Record<string, unknown> : Record<string, unknown>;
|
|
101
|
+
export interface Schema<TData extends object = object, RData extends object = TData> {
|
|
102
|
+
columns: ColumnSchema<TData, RData>[];
|
|
103
|
+
row?: {
|
|
104
|
+
conditionalStyle?: ConditionalStyleFn<RData>;
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Helper to lock schema generics so `formula`/`conditionalStyle` receive the correct row type.
|
|
109
|
+
* Usage: `const schema = defineSchema<MyRow>({ columns: [...], row: {...} });`
|
|
110
|
+
*/
|
|
111
|
+
export declare const defineSchema: <TData extends object, RData extends object = TData>(schema: Schema<TData, RData>) => Schema<TData, RData>;
|
|
112
|
+
export type RowObject<T extends object = Record<string, unknown>> = {
|
|
113
|
+
_readonly?: boolean;
|
|
114
|
+
} & T & Record<string, unknown>;
|
|
115
|
+
export type NullableData<T extends object = object> = T[] | null | undefined;
|
|
116
|
+
export type ViewFilterOp = {
|
|
117
|
+
kind: "op";
|
|
118
|
+
key: string;
|
|
119
|
+
op: "eq" | "neq" | "lt" | "lte" | "gt" | "gte" | "contains";
|
|
120
|
+
value: unknown;
|
|
121
|
+
};
|
|
122
|
+
export type ViewFilterValues = {
|
|
123
|
+
kind: "values";
|
|
124
|
+
key: string;
|
|
125
|
+
/** Allow-list of selected non-blank values. */
|
|
126
|
+
values: unknown[];
|
|
127
|
+
/** When true, blanks (null/undefined/"") are included. */
|
|
128
|
+
includeBlanks?: boolean;
|
|
129
|
+
};
|
|
130
|
+
export type ViewFilter = ViewFilterOp | ViewFilterValues;
|
|
131
|
+
export type ColumnDiagnosticFilter = {
|
|
132
|
+
errors?: boolean;
|
|
133
|
+
warnings?: boolean;
|
|
134
|
+
};
|
|
135
|
+
export type ColumnDiagnosticFilterMap = Record<string, ColumnDiagnosticFilter>;
|
|
136
|
+
export interface ViewSort {
|
|
137
|
+
key: string;
|
|
138
|
+
dir: "asc" | "desc";
|
|
139
|
+
}
|
|
140
|
+
export interface View {
|
|
141
|
+
hiddenColumns?: string[];
|
|
142
|
+
filters?: ViewFilter[];
|
|
143
|
+
sorts?: ViewSort[];
|
|
144
|
+
/**
|
|
145
|
+
* Column-scoped diagnostic quick filters.
|
|
146
|
+
* Key is the column key (stringified).
|
|
147
|
+
*/
|
|
148
|
+
columnDiagnostics?: ColumnDiagnosticFilterMap;
|
|
149
|
+
userId?: string;
|
|
150
|
+
columnWidths?: Record<string, number>;
|
|
151
|
+
rowHeights?: Record<string, number>;
|
|
152
|
+
wrapText?: Record<string, boolean>;
|
|
153
|
+
}
|
|
154
|
+
export type RenderMode = "html" | "canvas" | "auto";
|
|
155
|
+
export type EditMode = "direct" | "commit" | "readonly";
|
|
156
|
+
export type LockMode = "none" | "row";
|
|
157
|
+
export interface Command {
|
|
158
|
+
kind: "edit" | "deleteRow" | "insertRow" | "updateView" | "lock" | "unlock";
|
|
159
|
+
rowId?: string;
|
|
160
|
+
colKey?: string;
|
|
161
|
+
rowData?: RowObject;
|
|
162
|
+
prev?: unknown;
|
|
163
|
+
next?: unknown;
|
|
164
|
+
payload?: unknown;
|
|
165
|
+
}
|
|
166
|
+
export interface UserInfo {
|
|
167
|
+
id: string;
|
|
168
|
+
name: string;
|
|
169
|
+
}
|
|
170
|
+
export interface ServerAdapter {
|
|
171
|
+
fetchInitial?: () => Promise<{
|
|
172
|
+
data: Record<string, unknown>[];
|
|
173
|
+
view?: View;
|
|
174
|
+
schema?: Schema;
|
|
175
|
+
user: UserInfo;
|
|
176
|
+
}>;
|
|
177
|
+
lockRow: (rowId: string, user: UserInfo) => Promise<void>;
|
|
178
|
+
unlockRows: (rowIds: string[], user: UserInfo) => Promise<void>;
|
|
179
|
+
commit: (commands: Command[], user: UserInfo) => Promise<void>;
|
|
180
|
+
subscribe: (onEvent: (event: ServerEvent) => void) => () => void;
|
|
181
|
+
}
|
|
182
|
+
export type ServerEvent = {
|
|
183
|
+
type: "update";
|
|
184
|
+
commands: Command[];
|
|
185
|
+
user: UserInfo;
|
|
186
|
+
};
|
|
187
|
+
export interface CoreOptions {
|
|
188
|
+
renderMode?: RenderMode;
|
|
189
|
+
editMode?: EditMode;
|
|
190
|
+
lockMode?: LockMode;
|
|
191
|
+
/** Loading UI configuration used when `defaultData` is `null`. */
|
|
192
|
+
loading?: {
|
|
193
|
+
/** Enable built-in loading overlay/spinner. Default: true */
|
|
194
|
+
enabled?: boolean;
|
|
195
|
+
};
|
|
196
|
+
defaultClass?: string | string[];
|
|
197
|
+
defaultStyle?: Partial<CSSStyleDeclaration>;
|
|
198
|
+
server?: ServerAdapter;
|
|
199
|
+
user?: UserInfo;
|
|
200
|
+
findReplace?: {
|
|
201
|
+
/** Enable Find/Replace feature (engine + integrations). Default: true */
|
|
202
|
+
enabled?: boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Enable the default built-in UI and its shortcut bindings. Default: true
|
|
205
|
+
* Note: legacy name is `dialog`; `sidebar` is the preferred name.
|
|
206
|
+
*/
|
|
207
|
+
sidebar?: boolean;
|
|
208
|
+
/** @deprecated Use `sidebar` instead. */
|
|
209
|
+
dialog?: boolean;
|
|
210
|
+
/**
|
|
211
|
+
* When true, always intercept `Ctrl/Cmd+F` and show extable's search sidebar.
|
|
212
|
+
* Use this when the table is the primary focus of the page and browser Find/Reload should be overridden.
|
|
213
|
+
* Default: true (always intercept).
|
|
214
|
+
*/
|
|
215
|
+
enableSearch?: boolean;
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
export interface TableConfig<T extends object = Record<string, unknown>> {
|
|
219
|
+
data: T[] | null;
|
|
220
|
+
view: View;
|
|
221
|
+
schema: Schema;
|
|
222
|
+
}
|
|
223
|
+
export type HistoryCommandKind = Command["kind"];
|
|
224
|
+
export type UndoRedoStep = {
|
|
225
|
+
batchId: string | null;
|
|
226
|
+
kinds: HistoryCommandKind[];
|
|
227
|
+
commandCount: number;
|
|
228
|
+
label: string;
|
|
229
|
+
};
|
|
230
|
+
export type UndoRedoHistory = {
|
|
231
|
+
/** Top (index 0) is the next undo step. */
|
|
232
|
+
undo: UndoRedoStep[];
|
|
233
|
+
/** Top (index 0) is the next redo step. */
|
|
234
|
+
redo: UndoRedoStep[];
|
|
235
|
+
};
|
|
236
|
+
export interface InternalRow {
|
|
237
|
+
id: string;
|
|
238
|
+
raw: RowObject;
|
|
239
|
+
displayIndex: number;
|
|
240
|
+
}
|
|
241
|
+
export type SelectionKind = "cells" | "rows";
|
|
242
|
+
export interface SelectionRange {
|
|
243
|
+
kind: SelectionKind;
|
|
244
|
+
startRow: number;
|
|
245
|
+
endRow: number;
|
|
246
|
+
startCol: number;
|
|
247
|
+
endCol: number;
|
|
248
|
+
}
|
|
249
|
+
export type ToggleState = "on" | "off" | "mixed" | "disabled";
|
|
250
|
+
export type ColorState = string | "mixed" | null | "disabled";
|
|
251
|
+
export type TableError = {
|
|
252
|
+
scope: "validation" | "commit" | "render" | "formula" | "conditionalStyle" | "unknown";
|
|
253
|
+
message: string;
|
|
254
|
+
target?: {
|
|
255
|
+
rowId?: string;
|
|
256
|
+
colKey?: string;
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
export type TableState = {
|
|
260
|
+
canCommit: boolean;
|
|
261
|
+
pendingCommandCount: number;
|
|
262
|
+
pendingCellCount?: number;
|
|
263
|
+
undoRedo: {
|
|
264
|
+
canUndo: boolean;
|
|
265
|
+
canRedo: boolean;
|
|
266
|
+
};
|
|
267
|
+
renderMode: "html" | "canvas";
|
|
268
|
+
ui: {
|
|
269
|
+
searchPanelOpen: boolean;
|
|
270
|
+
};
|
|
271
|
+
activeErrors: TableError[];
|
|
272
|
+
};
|
|
273
|
+
export type TableStateListener = (next: TableState, prev: TableState | null) => void;
|
|
274
|
+
export type RowStateSnapshot<TInput extends object = Record<string, unknown>, TResult extends object = TInput> = {
|
|
275
|
+
rowId: string;
|
|
276
|
+
rowIndex: number;
|
|
277
|
+
data: TResult;
|
|
278
|
+
pending?: Partial<TInput>;
|
|
279
|
+
diagnostics?: TableError[];
|
|
280
|
+
};
|
|
281
|
+
export type RowChangeReason = "new" | "edit" | "delete";
|
|
282
|
+
export type RowStateListener<TInput extends object = Record<string, unknown>, TResult extends object = TInput> = (rowId: string, next: RowStateSnapshot<TInput, TResult> | null, prev: RowStateSnapshot<TInput, TResult> | null, reason: RowChangeReason) => void;
|
|
283
|
+
export type SelectionSnapshot = {
|
|
284
|
+
ranges: SelectionRange[];
|
|
285
|
+
activeRowIndex: number | null;
|
|
286
|
+
activeRowKey: string | null;
|
|
287
|
+
activeColumnIndex: number | null;
|
|
288
|
+
activeColumnKey: string | null;
|
|
289
|
+
activeValueRaw: unknown;
|
|
290
|
+
activeValueDisplay: string;
|
|
291
|
+
activeValueType: ColumnType | null;
|
|
292
|
+
diagnostic: CellDiagnostic | null;
|
|
293
|
+
styles: {
|
|
294
|
+
columnStyle: Partial<ResolvedCellStyle>;
|
|
295
|
+
cellStyle: Partial<ResolvedCellStyle>;
|
|
296
|
+
resolved: Partial<ResolvedCellStyle>;
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
export type SelectionChangeReason = "selection" | "edit" | "style" | "schema" | "view" | "data" | "unknown";
|
|
300
|
+
export type SelectionListener = (next: SelectionSnapshot, prev: SelectionSnapshot | null, reason: SelectionChangeReason) => void;
|
package/dist/utils.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@extable/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./style.css": "./dist/index.css",
|
|
14
|
+
"./index.css": "./dist/index.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"sideEffects": [
|
|
23
|
+
"./dist/index.css"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.build.json --emitDeclarationOnly && vite build",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"lint": "biome lint .",
|
|
29
|
+
"format": "biome format --write ."
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.6.3"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/shibukawa/extable.git"
|
|
40
|
+
},
|
|
41
|
+
"author": "Yoshiki Shibukawa",
|
|
42
|
+
"license": "Apache-2.0",
|
|
43
|
+
"peerDependencies": {}
|
|
44
|
+
}
|