argent-grid 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/.github/workflows/pages.yml +68 -0
- package/AGENTS.md +179 -0
- package/README.md +222 -0
- package/demo-app/README.md +70 -0
- package/demo-app/angular.json +78 -0
- package/demo-app/e2e/benchmark.spec.ts +53 -0
- package/demo-app/e2e/demo-page.spec.ts +77 -0
- package/demo-app/e2e/grid-features.spec.ts +269 -0
- package/demo-app/package-lock.json +14023 -0
- package/demo-app/package.json +36 -0
- package/demo-app/playwright-test-menu.js +19 -0
- package/demo-app/playwright.config.ts +23 -0
- package/demo-app/src/app/app.component.ts +10 -0
- package/demo-app/src/app/app.config.ts +13 -0
- package/demo-app/src/app/app.routes.ts +7 -0
- package/demo-app/src/app/demo-page/demo-page.component.css +313 -0
- package/demo-app/src/app/demo-page/demo-page.component.html +124 -0
- package/demo-app/src/app/demo-page/demo-page.component.ts +366 -0
- package/demo-app/src/index.html +19 -0
- package/demo-app/src/main.ts +6 -0
- package/demo-app/tsconfig.json +31 -0
- package/ng-package.json +8 -0
- package/package.json +60 -0
- package/plan.md +131 -0
- package/setup-vitest.ts +18 -0
- package/src/lib/argent-grid.module.ts +21 -0
- package/src/lib/components/argent-grid.component.css +483 -0
- package/src/lib/components/argent-grid.component.html +320 -0
- package/src/lib/components/argent-grid.component.spec.ts +189 -0
- package/src/lib/components/argent-grid.component.ts +1188 -0
- package/src/lib/directives/ag-grid-compatibility.directive.ts +92 -0
- package/src/lib/rendering/canvas-renderer.ts +962 -0
- package/src/lib/rendering/render/blit.spec.ts +453 -0
- package/src/lib/rendering/render/blit.ts +393 -0
- package/src/lib/rendering/render/cells.ts +369 -0
- package/src/lib/rendering/render/index.ts +105 -0
- package/src/lib/rendering/render/lines.ts +363 -0
- package/src/lib/rendering/render/theme.spec.ts +282 -0
- package/src/lib/rendering/render/theme.ts +201 -0
- package/src/lib/rendering/render/types.ts +279 -0
- package/src/lib/rendering/render/walk.spec.ts +360 -0
- package/src/lib/rendering/render/walk.ts +360 -0
- package/src/lib/rendering/utils/damage-tracker.spec.ts +444 -0
- package/src/lib/rendering/utils/damage-tracker.ts +423 -0
- package/src/lib/rendering/utils/index.ts +7 -0
- package/src/lib/services/grid.service.spec.ts +1039 -0
- package/src/lib/services/grid.service.ts +1284 -0
- package/src/lib/types/ag-grid-types.ts +970 -0
- package/src/public-api.ts +22 -0
- package/tsconfig.json +32 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.spec.json +8 -0
- package/vitest.config.ts +55 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell Rendering for Canvas Renderer
|
|
3
|
+
*
|
|
4
|
+
* Handles drawing of individual cells with prep/draw cycle optimization.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Column, IRowNode, ColDef, GridApi } from '../../types/ag-grid-types';
|
|
8
|
+
import { CellDrawContext, ColumnPrepResult, GridTheme } from './types';
|
|
9
|
+
import { getFontFromTheme } from './theme';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get value from object using path (e.g. 'pivotData.NY.salary')
|
|
13
|
+
*/
|
|
14
|
+
export function getValueByPath(obj: any, path: string): any {
|
|
15
|
+
if (!path || !obj) return undefined;
|
|
16
|
+
if (!path.includes('.')) return obj[path];
|
|
17
|
+
|
|
18
|
+
return path.split('.').reduce((acc, part) => acc && acc[part], obj);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// CELL PREP PHASE
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Prepare column for rendering
|
|
27
|
+
* Called once per column before rendering all cells in that column
|
|
28
|
+
*/
|
|
29
|
+
export function prepColumn<TData = any>(
|
|
30
|
+
ctx: CanvasRenderingContext2D,
|
|
31
|
+
column: Column,
|
|
32
|
+
colDef: ColDef<TData> | null,
|
|
33
|
+
theme: GridTheme
|
|
34
|
+
): ColumnPrepResult<TData> {
|
|
35
|
+
// Set font once per column (expensive operation)
|
|
36
|
+
const font = getFontFromTheme(theme);
|
|
37
|
+
ctx.font = font;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
column,
|
|
41
|
+
colDef,
|
|
42
|
+
theme,
|
|
43
|
+
font,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Prepare multiple columns
|
|
49
|
+
*/
|
|
50
|
+
export function prepColumns<TData = any>(
|
|
51
|
+
ctx: CanvasRenderingContext2D,
|
|
52
|
+
columns: Column[],
|
|
53
|
+
getColDef: (col: Column) => ColDef<TData> | null,
|
|
54
|
+
theme: GridTheme
|
|
55
|
+
): Map<string, ColumnPrepResult<TData>> {
|
|
56
|
+
const results = new Map<string, ColumnPrepResult<TData>>();
|
|
57
|
+
|
|
58
|
+
for (const column of columns) {
|
|
59
|
+
const colDef = getColDef(column);
|
|
60
|
+
results.set(column.colId, prepColumn(ctx, column, colDef, theme));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// CELL DRAW PHASE
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Draw a single cell
|
|
72
|
+
*/
|
|
73
|
+
export function drawCell<TData = any>(
|
|
74
|
+
ctx: CanvasRenderingContext2D,
|
|
75
|
+
prep: ColumnPrepResult<TData>,
|
|
76
|
+
context: CellDrawContext<TData>
|
|
77
|
+
): void {
|
|
78
|
+
const { x, y, width, height, value, formattedValue, column, rowNode } = context;
|
|
79
|
+
|
|
80
|
+
// Draw cell background
|
|
81
|
+
drawCellBackground(ctx, context);
|
|
82
|
+
|
|
83
|
+
// Draw cell content based on column type
|
|
84
|
+
drawCellContent(ctx, prep, context);
|
|
85
|
+
|
|
86
|
+
// Draw group indicators if needed
|
|
87
|
+
if (rowNode && (rowNode.group || rowNode.level > 0)) {
|
|
88
|
+
drawGroupIndicators(ctx, prep, context);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Draw cell background
|
|
94
|
+
*/
|
|
95
|
+
export function drawCellBackground<TData = any>(
|
|
96
|
+
ctx: CanvasRenderingContext2D,
|
|
97
|
+
context: CellDrawContext<TData>
|
|
98
|
+
): void {
|
|
99
|
+
const { x, y, width, height, isSelected, isHovered, isEvenRow } = context;
|
|
100
|
+
const { theme } = context;
|
|
101
|
+
|
|
102
|
+
// Determine background color
|
|
103
|
+
let bgColor = isEvenRow ? theme.bgCellEven : theme.bgCell;
|
|
104
|
+
if (isSelected) bgColor = theme.bgSelection;
|
|
105
|
+
if (isHovered) bgColor = theme.bgHover;
|
|
106
|
+
|
|
107
|
+
ctx.fillStyle = bgColor;
|
|
108
|
+
ctx.fillRect(Math.floor(x), Math.floor(y), Math.floor(width), Math.floor(height));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Draw cell content (text)
|
|
113
|
+
*/
|
|
114
|
+
export function drawCellContent<TData = any>(
|
|
115
|
+
ctx: CanvasRenderingContext2D,
|
|
116
|
+
prep: ColumnPrepResult<TData>,
|
|
117
|
+
context: CellDrawContext<TData>
|
|
118
|
+
): void {
|
|
119
|
+
const { x, y, width, height, formattedValue, theme } = context;
|
|
120
|
+
|
|
121
|
+
if (!formattedValue) return;
|
|
122
|
+
|
|
123
|
+
// Calculate text position with padding
|
|
124
|
+
const textX = x + theme.cellPadding;
|
|
125
|
+
const textY = y + height / 2; // Centered vertically
|
|
126
|
+
|
|
127
|
+
// Set text properties
|
|
128
|
+
ctx.fillStyle = theme.textCell;
|
|
129
|
+
ctx.textBaseline = 'middle';
|
|
130
|
+
|
|
131
|
+
// Truncate text if needed
|
|
132
|
+
const maxWidth = width - theme.cellPadding * 2;
|
|
133
|
+
const truncatedText = truncateText(ctx, formattedValue, maxWidth);
|
|
134
|
+
|
|
135
|
+
if (truncatedText) {
|
|
136
|
+
ctx.fillText(truncatedText, Math.floor(textX), Math.floor(textY));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Draw group/tree indicators
|
|
142
|
+
*/
|
|
143
|
+
export function drawGroupIndicators<TData = any>(
|
|
144
|
+
ctx: CanvasRenderingContext2D,
|
|
145
|
+
prep: ColumnPrepResult<TData>,
|
|
146
|
+
context: CellDrawContext<TData>
|
|
147
|
+
): void {
|
|
148
|
+
const { x, y, width, height, column, rowNode, theme, isEvenRow } = context;
|
|
149
|
+
|
|
150
|
+
if (!rowNode) return;
|
|
151
|
+
|
|
152
|
+
// Only draw on first visible column or auto-group column
|
|
153
|
+
const isAutoGroupCol = column.colId === 'ag-Grid-AutoColumn';
|
|
154
|
+
|
|
155
|
+
if (!isAutoGroupCol) return;
|
|
156
|
+
|
|
157
|
+
// Calculate indent
|
|
158
|
+
const indent = rowNode.level * theme.groupIndentWidth;
|
|
159
|
+
const indicatorX = x + theme.cellPadding + indent;
|
|
160
|
+
const indicatorY = y + height / 2;
|
|
161
|
+
|
|
162
|
+
// Draw expand/collapse indicator for groups
|
|
163
|
+
if (rowNode.group) {
|
|
164
|
+
ctx.fillStyle = theme.textCell;
|
|
165
|
+
ctx.beginPath();
|
|
166
|
+
|
|
167
|
+
if (rowNode.expanded) {
|
|
168
|
+
// Expanded: horizontal line (minus sign)
|
|
169
|
+
const lineY = Math.floor(indicatorY);
|
|
170
|
+
ctx.moveTo(Math.floor(indicatorX), lineY);
|
|
171
|
+
ctx.lineTo(Math.floor(indicatorX + theme.groupIndicatorSize), lineY);
|
|
172
|
+
} else {
|
|
173
|
+
// Collapsed: plus sign
|
|
174
|
+
const size = theme.groupIndicatorSize;
|
|
175
|
+
const centerX = Math.floor(indicatorX + size / 2);
|
|
176
|
+
const centerY = Math.floor(indicatorY);
|
|
177
|
+
|
|
178
|
+
// Horizontal line
|
|
179
|
+
ctx.moveTo(Math.floor(indicatorX), centerY);
|
|
180
|
+
ctx.lineTo(Math.floor(indicatorX + size), centerY);
|
|
181
|
+
// Vertical line
|
|
182
|
+
ctx.moveTo(centerX, centerY - size / 2);
|
|
183
|
+
ctx.lineTo(centerX, centerY + size / 2);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
ctx.stroke();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ============================================================================
|
|
191
|
+
// TEXT UTILITIES
|
|
192
|
+
// ============================================================================
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Truncate text to fit within max width
|
|
196
|
+
*/
|
|
197
|
+
export function truncateText(
|
|
198
|
+
ctx: CanvasRenderingContext2D,
|
|
199
|
+
text: string,
|
|
200
|
+
maxWidth: number
|
|
201
|
+
): string {
|
|
202
|
+
if (maxWidth <= 0) return '';
|
|
203
|
+
|
|
204
|
+
const metrics = ctx.measureText(text);
|
|
205
|
+
if (metrics.width <= maxWidth) {
|
|
206
|
+
return text;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Binary search for optimal truncation point
|
|
210
|
+
let start = 0;
|
|
211
|
+
let end = text.length;
|
|
212
|
+
|
|
213
|
+
while (start < end) {
|
|
214
|
+
const mid = Math.floor((start + end) / 2);
|
|
215
|
+
const truncated = text.slice(0, mid) + '...';
|
|
216
|
+
|
|
217
|
+
if (ctx.measureText(truncated).width <= maxWidth) {
|
|
218
|
+
start = mid + 1;
|
|
219
|
+
} else {
|
|
220
|
+
end = mid;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return text.slice(0, Math.max(0, start - 1)) + '...';
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Measure text width
|
|
229
|
+
*/
|
|
230
|
+
export function measureText(
|
|
231
|
+
ctx: CanvasRenderingContext2D,
|
|
232
|
+
text: string
|
|
233
|
+
): number {
|
|
234
|
+
return ctx.measureText(text).width;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Calculate optimal column width based on content
|
|
239
|
+
*/
|
|
240
|
+
export function calculateColumnWidth<TData = any>(
|
|
241
|
+
ctx: CanvasRenderingContext2D,
|
|
242
|
+
column: Column,
|
|
243
|
+
colDef: ColDef<TData> | null,
|
|
244
|
+
theme: GridTheme,
|
|
245
|
+
sampleData: any[],
|
|
246
|
+
maxRows: number = 100
|
|
247
|
+
): number {
|
|
248
|
+
const font = getFontFromTheme(theme);
|
|
249
|
+
ctx.font = font;
|
|
250
|
+
|
|
251
|
+
let maxWidth = 0;
|
|
252
|
+
|
|
253
|
+
// Check header width
|
|
254
|
+
const headerText = column.headerName || column.field || '';
|
|
255
|
+
maxWidth = Math.max(maxWidth, ctx.measureText(headerText).width);
|
|
256
|
+
|
|
257
|
+
// Check sample data widths
|
|
258
|
+
const field = column.field;
|
|
259
|
+
if (field) {
|
|
260
|
+
const rowsToCheck = Math.min(sampleData.length, maxRows);
|
|
261
|
+
for (let i = 0; i < rowsToCheck; i++) {
|
|
262
|
+
const value = sampleData[i]?.[field];
|
|
263
|
+
if (value != null) {
|
|
264
|
+
const text = String(value);
|
|
265
|
+
maxWidth = Math.max(maxWidth, ctx.measureText(text).width);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Add padding
|
|
271
|
+
return Math.ceil(maxWidth + theme.cellPadding * 2);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// CELL VALUE FORMATTING
|
|
276
|
+
// ============================================================================
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get formatted cell value
|
|
280
|
+
*/
|
|
281
|
+
export function getFormattedValue<TData = any>(
|
|
282
|
+
value: any,
|
|
283
|
+
colDef: ColDef<TData> | null,
|
|
284
|
+
data: TData,
|
|
285
|
+
rowNode: IRowNode<TData>,
|
|
286
|
+
api: GridApi<TData>
|
|
287
|
+
): string {
|
|
288
|
+
if (value === null || value === undefined) {
|
|
289
|
+
return '';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Use custom formatter if provided
|
|
293
|
+
if (colDef && typeof colDef.valueFormatter === 'function') {
|
|
294
|
+
try {
|
|
295
|
+
return colDef.valueFormatter({
|
|
296
|
+
value,
|
|
297
|
+
data,
|
|
298
|
+
node: rowNode,
|
|
299
|
+
colDef,
|
|
300
|
+
api,
|
|
301
|
+
});
|
|
302
|
+
} catch (e) {
|
|
303
|
+
console.warn('Value formatter error:', e);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return String(value);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ============================================================================
|
|
311
|
+
// BATCH CELL RENDERING
|
|
312
|
+
// ============================================================================
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Render all cells in a row
|
|
316
|
+
*/
|
|
317
|
+
export function renderRow<TData = any>(
|
|
318
|
+
ctx: CanvasRenderingContext2D,
|
|
319
|
+
columns: Column[],
|
|
320
|
+
colPreps: Map<string, ColumnPrepResult<TData>>,
|
|
321
|
+
rowNode: IRowNode<TData>,
|
|
322
|
+
rowIndex: number,
|
|
323
|
+
y: number,
|
|
324
|
+
height: number,
|
|
325
|
+
getCellX: (column: Column) => number,
|
|
326
|
+
api: GridApi<TData>,
|
|
327
|
+
theme: GridTheme,
|
|
328
|
+
options: {
|
|
329
|
+
isSelected?: boolean;
|
|
330
|
+
isHovered?: boolean;
|
|
331
|
+
} = {}
|
|
332
|
+
): void {
|
|
333
|
+
const isEvenRow = rowIndex % 2 === 0;
|
|
334
|
+
|
|
335
|
+
for (const column of columns) {
|
|
336
|
+
const prep = colPreps.get(column.colId);
|
|
337
|
+
if (!prep) continue;
|
|
338
|
+
|
|
339
|
+
const x = getCellX(column);
|
|
340
|
+
const value = column.field ? getValueByPath(rowNode.data, column.field) : undefined;
|
|
341
|
+
const formattedValue = getFormattedValue(
|
|
342
|
+
value,
|
|
343
|
+
prep.colDef,
|
|
344
|
+
rowNode.data,
|
|
345
|
+
rowNode,
|
|
346
|
+
api
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const context: CellDrawContext<TData> = {
|
|
350
|
+
ctx,
|
|
351
|
+
theme,
|
|
352
|
+
column,
|
|
353
|
+
colDef: prep.colDef,
|
|
354
|
+
rowNode,
|
|
355
|
+
rowIndex,
|
|
356
|
+
x,
|
|
357
|
+
y,
|
|
358
|
+
width: column.width,
|
|
359
|
+
height,
|
|
360
|
+
value,
|
|
361
|
+
formattedValue,
|
|
362
|
+
isSelected: options.isSelected || rowNode.selected,
|
|
363
|
+
isHovered: options.isHovered || false,
|
|
364
|
+
isEvenRow,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
drawCell(ctx, prep, context);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering Module Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all rendering-related modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types (base definitions)
|
|
8
|
+
export {
|
|
9
|
+
Rectangle,
|
|
10
|
+
Point,
|
|
11
|
+
Size,
|
|
12
|
+
ScrollPosition,
|
|
13
|
+
PositionedColumn,
|
|
14
|
+
ColumnWalkCallback,
|
|
15
|
+
RowWalkCallback,
|
|
16
|
+
CellWalkCallback,
|
|
17
|
+
CellDrawContext,
|
|
18
|
+
ColumnPrepResult,
|
|
19
|
+
BlitResult,
|
|
20
|
+
BufferPair,
|
|
21
|
+
DamageType,
|
|
22
|
+
DirtyRegions,
|
|
23
|
+
GridTheme,
|
|
24
|
+
PartialTheme,
|
|
25
|
+
RenderState,
|
|
26
|
+
VisibleRange,
|
|
27
|
+
HitTestResult,
|
|
28
|
+
GridMouseEvent,
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
// Theme (re-export the DEFAULT_THEME and utilities)
|
|
32
|
+
export {
|
|
33
|
+
DEFAULT_THEME,
|
|
34
|
+
DARK_THEME,
|
|
35
|
+
THEME_PRESETS,
|
|
36
|
+
mergeTheme,
|
|
37
|
+
getFontFromTheme,
|
|
38
|
+
getRowTheme,
|
|
39
|
+
getCellBackgroundColor,
|
|
40
|
+
getThemePreset,
|
|
41
|
+
createTheme,
|
|
42
|
+
} from './theme';
|
|
43
|
+
|
|
44
|
+
// Walker functions
|
|
45
|
+
export {
|
|
46
|
+
walkColumns,
|
|
47
|
+
getPositionedColumns,
|
|
48
|
+
getPinnedWidths,
|
|
49
|
+
walkRows,
|
|
50
|
+
getVisibleRowRange,
|
|
51
|
+
getRowY,
|
|
52
|
+
walkCells,
|
|
53
|
+
getColumnAtX,
|
|
54
|
+
getColumnIndex,
|
|
55
|
+
getTotalColumnWidth,
|
|
56
|
+
getRowAtY,
|
|
57
|
+
isRowVisible,
|
|
58
|
+
calculateVisibleRange,
|
|
59
|
+
} from './walk';
|
|
60
|
+
|
|
61
|
+
// Blitting optimization
|
|
62
|
+
export {
|
|
63
|
+
MIN_BLIT_DELTA,
|
|
64
|
+
MAX_BLIT_DELTA_RATIO,
|
|
65
|
+
shouldBlit,
|
|
66
|
+
calculateBlit,
|
|
67
|
+
blitLastFrame,
|
|
68
|
+
createBufferPair,
|
|
69
|
+
swapBuffers,
|
|
70
|
+
displayBuffer,
|
|
71
|
+
resizeBufferPair,
|
|
72
|
+
BlitState,
|
|
73
|
+
} from './blit';
|
|
74
|
+
|
|
75
|
+
// Cell rendering (explicit exports to avoid conflicts)
|
|
76
|
+
export {
|
|
77
|
+
prepColumn,
|
|
78
|
+
prepColumns,
|
|
79
|
+
drawCell,
|
|
80
|
+
drawCellBackground,
|
|
81
|
+
drawCellContent,
|
|
82
|
+
drawGroupIndicators,
|
|
83
|
+
truncateText,
|
|
84
|
+
measureText,
|
|
85
|
+
calculateColumnWidth,
|
|
86
|
+
getFormattedValue,
|
|
87
|
+
getValueByPath,
|
|
88
|
+
renderRow,
|
|
89
|
+
} from './cells';
|
|
90
|
+
|
|
91
|
+
// Grid lines
|
|
92
|
+
export {
|
|
93
|
+
drawCrispLine,
|
|
94
|
+
drawHorizontalLine,
|
|
95
|
+
drawVerticalLine,
|
|
96
|
+
drawRowLines,
|
|
97
|
+
drawColumnLines,
|
|
98
|
+
getColumnBorderPositions,
|
|
99
|
+
drawGridLines,
|
|
100
|
+
drawBorder,
|
|
101
|
+
drawCellSelectionBorder,
|
|
102
|
+
drawRangeSelectionBorder,
|
|
103
|
+
drawPinnedRegionBorders,
|
|
104
|
+
drawPinnedRegionShadows,
|
|
105
|
+
} from './lines';
|