• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

glideapps / glide-data-grid / 16143501631

08 Jul 2025 12:40PM UTC coverage: 89.936% (-1.2%) from 91.171%
16143501631

Pull #1059

github

web-flow
Merge 6ec6036d7 into 3041b6f44
Pull Request #1059: Freeze right columns

2968 of 3678 branches covered (80.7%)

610 of 707 new or added lines in 12 files covered. (86.28%)

210 existing lines in 7 files now uncovered.

18061 of 20082 relevant lines covered (89.94%)

4368.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.06
/packages/core/src/internal/data-grid/render/data-grid-render.cells.ts
1
/* eslint-disable sonarjs/no-duplicate-string */
1✔
2
/* eslint-disable unicorn/no-for-loop */
1✔
3
import {
1✔
4
    type GridSelection,
1✔
5
    type InnerGridCell,
1✔
6
    type Rectangle,
1✔
7
    CompactSelection,
1✔
8
    GridColumnIcon,
1✔
9
    type Item,
1✔
10
    type CellList,
1✔
11
    GridCellKind,
1✔
12
    type DrawCellCallback,
1✔
13
    isInnerOnlyCell,
1✔
14
    type GridCell,
1✔
15
} from "../data-grid-types.js";
1✔
16
import { CellSet } from "../cell-set.js";
1✔
17
import type { HoverValues } from "../animation-manager.js";
1✔
18
import {
1✔
19
    type MappedGridColumn,
1✔
20
    cellIsSelected,
1✔
21
    cellIsInRange,
1✔
22
    getFreezeTrailingHeight,
1✔
23
    drawLastUpdateUnderlay,
1✔
24
} from "./data-grid-lib.js";
1✔
25
import type { SpriteManager } from "../data-grid-sprites.js";
1✔
26
import { mergeAndRealizeTheme, type FullTheme, type Theme } from "../../../common/styles.js";
1✔
27
import { blend } from "../color-parser.js";
1✔
28
import type { DrawArgs, DrawStateTuple, GetCellRendererCallback, PrepResult } from "../../../cells/cell-types.js";
1✔
29
import type { HoverInfo } from "./draw-grid-arg.js";
1✔
30
import type { EnqueueCallback } from "../use-animation-queue.js";
1✔
31
import type { RenderStateProvider } from "../../../common/render-state-provider.js";
1✔
32
import type { ImageWindowLoader } from "../image-window-loader-interface.js";
1✔
33
import { intersectRect } from "../../../common/math.js";
1✔
34
import type { GridMouseGroupHeaderEventArgs } from "../event-args.js";
1✔
35
import { getSkipPoint, getSpanBounds, walkColumns, walkRowsInCol } from "./data-grid-render.walk.js";
1✔
36

1✔
37
const loadingCell: InnerGridCell = {
1✔
38
    kind: GridCellKind.Loading,
1✔
39
    allowOverlay: false,
1✔
40
};
1✔
41

1✔
42
export interface GroupDetails {
1✔
43
    readonly name: string;
1✔
44
    readonly icon?: string;
1✔
45
    readonly overrideTheme?: Partial<Theme>;
1✔
46
    readonly actions?: readonly {
1✔
47
        readonly title: string;
1✔
48
        readonly onClick: (e: GridMouseGroupHeaderEventArgs) => void;
1✔
49
        readonly icon: GridColumnIcon | string;
1✔
50
    }[];
1✔
51
}
1✔
52

1✔
53
export type GroupDetailsCallback = (groupName: string) => GroupDetails;
1✔
54
export type GetRowThemeCallback = (row: number) => Partial<Theme> | undefined;
1✔
55

1✔
56
export interface Highlight {
1✔
57
    readonly color: string;
1✔
58
    readonly range: Rectangle;
1✔
59
    readonly style?: "dashed" | "solid" | "no-outline" | "solid-outline";
1✔
60
}
1✔
61

1✔
62
// preppable items:
1✔
63
// - font
1✔
64
// - fillStyle
1✔
65

1✔
66
// Column draw loop prep cycle
1✔
67
// - Prep item
1✔
68
// - Prep sets props
1✔
69
// - Prep returns list of cared about props
1✔
70
// - Draw item
1✔
71
// - Loop may set some items, if present in args list, set undefined
1✔
72
// - Prep next item, giving previous result
1✔
73
// - If next item type is different, de-prep
1✔
74
// - Result per column
1✔
75
export function drawCells(
1✔
76
    ctx: CanvasRenderingContext2D,
666✔
77
    effectiveColumns: readonly MappedGridColumn[],
666✔
78
    allColumns: readonly MappedGridColumn[],
666✔
79
    height: number,
666✔
80
    width: number,
666✔
81
    totalHeaderHeight: number,
666✔
82
    translateX: number,
666✔
83
    translateY: number,
666✔
84
    cellYOffset: number,
666✔
85
    rows: number,
666✔
86
    getRowHeight: (row: number) => number,
666✔
87
    getCellContent: (cell: Item) => InnerGridCell,
666✔
88
    getGroupDetails: GroupDetailsCallback,
666✔
89
    getRowThemeOverride: GetRowThemeCallback | undefined,
666✔
90
    disabledRows: CompactSelection,
666✔
91
    isFocused: boolean,
666✔
92
    drawFocus: boolean,
666✔
93
    freezeTrailingRows: number,
666✔
94
    freezeTrailingColumns: number,
666✔
95
    hasAppendRow: boolean,
666✔
96
    drawRegions: readonly Rectangle[],
666✔
97
    damage: CellSet | undefined,
666✔
98
    selection: GridSelection,
666✔
99
    prelightCells: CellList | undefined,
666✔
100
    highlightRegions: readonly Highlight[] | undefined,
666✔
101
    imageLoader: ImageWindowLoader,
666✔
102
    spriteManager: SpriteManager,
666✔
103
    hoverValues: HoverValues,
666✔
104
    hoverInfo: HoverInfo | undefined,
666✔
105
    drawCellCallback: DrawCellCallback | undefined,
666✔
106
    hyperWrapping: boolean,
666✔
107
    outerTheme: FullTheme,
666✔
108
    enqueue: EnqueueCallback,
666✔
109
    renderStateProvider: RenderStateProvider,
666✔
110
    getCellRenderer: GetCellRendererCallback,
666✔
111
    overrideCursor: (cursor: React.CSSProperties["cursor"]) => void,
666✔
112
    minimumCellWidth: number
666✔
113
): Rectangle[] | undefined {
666✔
114
    let toDraw = damage?.size ?? Number.MAX_SAFE_INTEGER;
666✔
115
    const frameTime = performance.now();
666✔
116
    let font = outerTheme.baseFontFull;
666✔
117
    ctx.font = font;
666✔
118
    const deprepArg = { ctx };
666✔
119
    const cellIndex: [number, number] = [0, 0];
666✔
120
    const freezeTrailingRowsHeight =
666✔
121
        freezeTrailingRows > 0 ? getFreezeTrailingHeight(rows, freezeTrailingRows, getRowHeight) : 0;
666✔
122
    let result: Rectangle[] | undefined;
666✔
123
    let handledSpans: Set<string> | undefined = undefined;
666✔
124

666✔
125
    const skipPoint = getSkipPoint(drawRegions);
666✔
126

666✔
127
    walkColumns(
666✔
128
        effectiveColumns,
666✔
129
        width,
666✔
130
        cellYOffset,
666✔
131
        translateX,
666✔
132
        translateY,
666✔
133
        totalHeaderHeight,
666✔
134
        freezeTrailingColumns,
666✔
135
        (c, drawX, colDrawStartY, clipX, clipXRight, startRow) => {
666✔
136
            const diff = Math.max(0, clipX - drawX);
6,385✔
137

6,385✔
138
            const colDrawX = drawX + diff;
6,385✔
139
            const colDrawY = totalHeaderHeight + 1;
6,385✔
140
            const colWidth = c.stickyPosition === "right" ? c.width - diff : Math.min(c.width - diff, width - drawX - clipXRight);
6,385✔
141
            const colHeight = height - totalHeaderHeight - 1;
6,385✔
142
            if (drawRegions.length > 0) {
6,385!
UNCOV
143
                let found = false;
×
UNCOV
144
                for (let i = 0; i < drawRegions.length; i++) {
×
UNCOV
145
                    const dr = drawRegions[i];
×
UNCOV
146
                    if (intersectRect(colDrawX, colDrawY, colWidth, colHeight, dr.x, dr.y, dr.width, dr.height)) {
×
UNCOV
147
                        found = true;
×
UNCOV
148
                        break;
×
UNCOV
149
                    }
×
UNCOV
150
                }
×
UNCOV
151
                if (!found) return;
×
UNCOV
152
            }
×
153

6,385✔
154
            const reclip = () => {
6,385✔
155
                ctx.save();
6,390✔
156
                ctx.beginPath();
6,390✔
157
                ctx.rect(colDrawX, colDrawY, colWidth, colHeight);
6,390✔
158
                ctx.clip();
6,390✔
159
            };
6,390✔
160

6,385✔
161
            const colSelected = selection.columns.hasIndex(c.sourceIndex);
6,385✔
162

6,385✔
163
            const groupTheme = getGroupDetails(c.group ?? "").overrideTheme;
6,385✔
164
            const colTheme =
6,385✔
165
                c.themeOverride === undefined && groupTheme === undefined
6,385✔
166
                    ? outerTheme
6,385!
167
                    : mergeAndRealizeTheme(outerTheme, groupTheme, c.themeOverride);
×
168
            const colFont = colTheme.baseFontFull;
6,385✔
169
            if (colFont !== font) {
6,385!
170
                font = colFont;
×
171
                ctx.font = colFont;
×
172
            }
×
173
            reclip();
6,385✔
174
            let prepResult: PrepResult | undefined = undefined;
6,385✔
175

6,385✔
176
            walkRowsInCol(
6,385✔
177
                startRow,
6,385✔
178
                colDrawStartY,
6,385✔
179
                height,
6,385✔
180
                rows,
6,385✔
181
                getRowHeight,
6,385✔
182
                freezeTrailingRows,
6,385✔
183
                hasAppendRow,
6,385✔
184
                skipPoint,
6,385✔
185
                (drawY, row, rh, isSticky, isTrailingRow) => {
6,385✔
186
                    if (row < 0) return;
196,120!
187

196,120✔
188
                    cellIndex[0] = c.sourceIndex;
196,120✔
189
                    cellIndex[1] = row;
196,120✔
190
                    // if (damage !== undefined && !damage.some(d => d[0] === c.sourceIndex && d[1] === row)) {
196,120✔
191
                    //     return;
196,120✔
192
                    // }
196,120✔
193
                    // if (
196,120✔
194
                    //     drawRegions.length > 0 &&
196,120✔
195
                    //     !drawRegions.some(dr => intersectRect(drawX, drawY, c.width, rh, dr.x, dr.y, dr.width, dr.height))
196,120✔
196
                    // ) {
196,120✔
197
                    //     return;
196,120✔
198
                    // }
196,120✔
199

196,120✔
200
                    // These are dumb versions of the above. I cannot for the life of believe that this matters but this is
196,120✔
201
                    // the tightest part of the draw loop and the allocations above actually has a very measurable impact
196,120✔
202
                    // on performance. For the love of all that is unholy please keep checking this again in the future.
196,120✔
203
                    // As soon as this doesn't have any impact of note go back to the saner looking code. The smoke test
196,120✔
204
                    // here is to scroll to the bottom of a test case first, then scroll back up while profiling and see
196,120✔
205
                    // how many major GC collections you get. These allocate a lot of objects.
196,120✔
206
                    if (damage !== undefined && !damage.has(cellIndex)) {
196,120✔
207
                        return;
11,094✔
208
                    }
11,094✔
209
                    if (drawRegions.length > 0) {
196,120!
UNCOV
210
                        let found = false;
×
UNCOV
211
                        for (let i = 0; i < drawRegions.length; i++) {
×
UNCOV
212
                            const dr = drawRegions[i];
×
UNCOV
213
                            if (intersectRect(drawX, drawY, c.width, rh, dr.x, dr.y, dr.width, dr.height)) {
×
UNCOV
214
                                found = true;
×
UNCOV
215
                                break;
×
UNCOV
216
                            }
×
UNCOV
217
                        }
×
UNCOV
218
                        if (!found) return;
×
UNCOV
219
                    }
✔
220

185,026✔
221
                    const rowSelected = selection.rows.hasIndex(row);
185,026✔
222
                    const rowDisabled = disabledRows.hasIndex(row);
185,026✔
223

185,026✔
224
                    const cell: InnerGridCell = row < rows ? getCellContent(cellIndex) : loadingCell;
196,120!
225

196,120✔
226
                    let cellX = drawX;
196,120✔
227
                    let cellWidth = c.width;
196,120✔
228
                    let drawingSpan = false;
196,120✔
229
                    let skipContents = false;
196,120✔
230
                    if (cell.span !== undefined) {
196,120✔
231
                        const [startCol, endCol] = cell.span;
10✔
232
                        const spanKey = `${row},${startCol},${endCol},${c.sticky}`; //alloc
10✔
233
                        if (handledSpans === undefined) handledSpans = new Set();
10✔
234
                        if (!handledSpans.has(spanKey)) {
10✔
235
                            const areas = getSpanBounds(cell.span, drawX, drawY, c.width, rh, c, allColumns);
5✔
236
                            const area = c.sticky ? areas[0] : areas[1];
5!
237
                            if (!c.sticky && areas[0] !== undefined) {
5!
238
                                skipContents = true;
×
239
                            }
×
240
                            if (area !== undefined) {
5✔
241
                                cellX = area.x;
5✔
242
                                cellWidth = area.width;
5✔
243
                                handledSpans.add(spanKey);
5✔
244
                                ctx.restore();
5✔
245
                                prepResult = undefined;
5✔
246
                                ctx.save();
5✔
247
                                ctx.beginPath();
5✔
248
                                const d = Math.max(0, clipX - area.x);
5✔
249
                                ctx.rect(area.x + d, drawY, area.width - d, rh);
5✔
250
                                if (result === undefined) {
5✔
251
                                    result = [];
5✔
252
                                }
5✔
253
                                result.push({
5✔
254
                                    x: area.x + d,
5✔
255
                                    y: drawY,
5✔
256
                                    width: area.width - d,
5✔
257
                                    height: rh,
5✔
258
                                });
5✔
259
                                ctx.clip();
5✔
260
                                drawingSpan = true;
5✔
261
                            }
5✔
262
                        } else {
5✔
263
                            toDraw--;
5✔
264
                            return;
5✔
265
                        }
5✔
266
                    }
10✔
267

185,021✔
268
                    const rowTheme = getRowThemeOverride?.(row);
196,120!
269
                    const trailingTheme =
196,120✔
270
                        isTrailingRow && c.trailingRowOptions?.themeOverride !== undefined
196,120!
271
                            ? c.trailingRowOptions?.themeOverride
✔
272
                            : undefined;
185,021✔
273
                    const theme =
196,120✔
274
                        cell.themeOverride === undefined && rowTheme === undefined && trailingTheme === undefined
196,120✔
275
                            ? colTheme
185,021!
276
                            : mergeAndRealizeTheme(colTheme, rowTheme, trailingTheme, cell.themeOverride); //alloc
×
277

196,120✔
278
                    ctx.beginPath();
196,120✔
279

196,120✔
280
                    const isSelected = cellIsSelected(cellIndex, cell, selection);
196,120✔
281
                    let accentCount = cellIsInRange(cellIndex, cell, selection, drawFocus);
196,120✔
282
                    const spanIsHighlighted =
196,120✔
283
                        cell.span !== undefined &&
196,120✔
284
                        selection.columns.some(
5✔
285
                            index => cell.span !== undefined && index >= cell.span[0] && index <= cell.span[1] //alloc
5✔
286
                        );
5✔
287
                    if (isSelected && !isFocused && drawFocus) {
196,120✔
288
                        accentCount = 0;
270✔
289
                    } else if (isSelected && drawFocus) {
196,120✔
290
                        accentCount = Math.max(accentCount, 1);
84✔
291
                    }
84✔
292
                    if (spanIsHighlighted) {
196,120!
293
                        accentCount++;
×
294
                    }
✔
295
                    if (!isSelected) {
196,089✔
296
                        if (rowSelected) accentCount++;
184,667✔
297
                        if (colSelected && !isTrailingRow) accentCount++;
184,667✔
298
                    }
184,667✔
299

185,021✔
300
                    const bgCell = cell.kind === GridCellKind.Protected ? theme.bgCellMedium : theme.bgCell;
196,120✔
301
                    let fill: string | undefined;
196,120✔
302
                    if (isSticky || bgCell !== outerTheme.bgCell || c.sticky) {
196,120✔
303
                        fill = blend(bgCell, fill);
26,156✔
304
                    }
26,156✔
305

185,021✔
306
                    if (accentCount > 0 || rowDisabled) {
196,120✔
307
                        if (rowDisabled) {
12,552✔
308
                            fill = blend(theme.bgHeader, fill);
5,832✔
309
                        }
5,832✔
310
                        for (let i = 0; i < accentCount; i++) {
12,552✔
311
                            fill = blend(theme.accentLight, fill);
6,725✔
312
                        }
6,725✔
313
                    } else if (prelightCells !== undefined) {
196,120✔
314
                        for (const pre of prelightCells) {
170,473✔
315
                            if (pre[0] === c.sourceIndex && pre[1] === row) {
99,765✔
316
                                fill = blend(theme.bgSearchResult, fill);
33✔
317
                                break;
33✔
318
                            }
33✔
319
                        }
99,765✔
320
                    }
170,473✔
321

185,021✔
322
                    if (highlightRegions !== undefined) {
196,120✔
323
                        for (let i = 0; i < highlightRegions.length; i++) {
106,917✔
324
                            const region = highlightRegions[i];
119,944✔
325
                            const r = region.range;
119,944✔
326
                            if (
119,944✔
327
                                region.style !== "solid-outline" &&
119,944✔
328
                                r.x <= c.sourceIndex &&
2,056✔
329
                                c.sourceIndex < r.x + r.width &&
1,788✔
330
                                r.y <= row &&
412✔
331
                                row < r.y + r.height
388✔
332
                            ) {
119,944✔
333
                                fill = blend(region.color, fill);
124✔
334
                            }
124✔
335
                        }
119,944✔
336
                    }
106,917✔
337

185,021✔
338
                    let didDamageClip = false;
185,021✔
339
                    if (damage !== undefined) {
196,120✔
340
                        // we want to clip each cell individually rather than form a super clip region. The reason for
141✔
341
                        // this is passing too many clip regions to the GPU at once can cause a performance hit. This
141✔
342
                        // allows us to damage a large number of cells at once without issue.
141✔
343
                        const top = drawY + 1;
141✔
344
                        const bottom = isSticky
141!
345
                            ? top + rh - 1
×
346
                            : Math.min(top + rh - 1, height - freezeTrailingRowsHeight);
141✔
347
                        const h = bottom - top;
141✔
348

141✔
349
                        // however, not clipping at all is even better. We want to clip if we are the left most col
141✔
350
                        // or overlapping the bottom clip area.
141✔
351
                        if (h !== rh - 1 || cellX + 1 <= clipX) {
141✔
352
                            didDamageClip = true;
2✔
353
                            ctx.save();
2✔
354
                            ctx.beginPath();
2✔
355
                            ctx.rect(cellX + 1, top, cellWidth - 1, h);
2✔
356
                            ctx.clip();
2✔
357
                        }
2✔
358

141✔
359
                        // we also need to make sure to wipe the contents. Since the fill can do that lets repurpose
141✔
360
                        // that call to avoid an extra draw call.
141✔
361
                        fill = fill === undefined ? theme.bgCell : blend(fill, theme.bgCell);
141✔
362
                    }
141✔
363

185,021✔
364
                    const isLastColumn = c.sourceIndex === allColumns.length - 1;
185,021✔
365
                    const isLastRow = row === rows - 1;
185,021✔
366
                    if (fill !== undefined) {
196,120✔
367
                        ctx.fillStyle = fill;
32,382✔
368
                        if (prepResult !== undefined) {
32,382✔
369
                            prepResult.fillStyle = fill;
11,290✔
370
                        }
11,290✔
371
                        if (damage !== undefined) {
32,382✔
372
                            // this accounts for the fill handle outline being drawn inset on these cells. We do this
141✔
373
                            // because technically the bottom right corner of the outline are on other cells.
141✔
374
                            ctx.fillRect(
141✔
375
                                cellX + 1,
141✔
376
                                drawY + 1,
141✔
377
                                cellWidth - (isLastColumn ? 2 : 1),
141!
378
                                rh - (isLastRow ? 2 : 1)
141!
379
                            );
141✔
380
                        } else {
32,382✔
381
                            ctx.fillRect(cellX, drawY, cellWidth, rh);
32,241✔
382
                        }
32,241✔
383
                    }
32,382✔
384

185,021✔
385
                    if (cell.style === "faded") {
196,120!
386
                        ctx.globalAlpha = 0.6;
×
387
                    }
✔
388

185,021✔
389
                    let hoverValue: HoverValues[number] | undefined;
185,021✔
390
                    for (let i = 0; i < hoverValues.length; i++) {
196,120✔
391
                        const hv = hoverValues[i];
2,850✔
392
                        if (hv.item[0] === c.sourceIndex && hv.item[1] === row) {
2,850!
393
                            hoverValue = hv;
×
394
                            break;
×
395
                        }
×
396
                    }
2,850✔
397

185,021✔
398
                    if (cellWidth > minimumCellWidth && !skipContents) {
196,120✔
399
                        const cellFont = theme.baseFontFull;
185,021✔
400
                        if (cellFont !== font) {
185,021!
401
                            ctx.font = cellFont;
×
402
                            font = cellFont;
×
403
                        }
×
404
                        prepResult = drawCell(
185,021✔
405
                            ctx,
185,021✔
406
                            cell,
185,021✔
407
                            c.sourceIndex,
185,021✔
408
                            row,
185,021✔
409
                            isLastColumn,
185,021✔
410
                            isLastRow,
185,021✔
411
                            cellX,
185,021✔
412
                            drawY,
185,021✔
413
                            cellWidth,
185,021✔
414
                            rh,
185,021✔
415
                            accentCount > 0,
185,021✔
416
                            theme,
185,021✔
417
                            fill ?? theme.bgCell,
185,021✔
418
                            imageLoader,
185,021✔
419
                            spriteManager,
185,021✔
420
                            hoverValue?.hoverAmount ?? 0,
185,021!
421
                            hoverInfo,
185,021✔
422
                            hyperWrapping,
185,021✔
423
                            frameTime,
185,021✔
424
                            drawCellCallback,
185,021✔
425
                            prepResult,
185,021✔
426
                            enqueue,
185,021✔
427
                            renderStateProvider,
185,021✔
428
                            getCellRenderer,
185,021✔
429
                            overrideCursor
185,021✔
430
                        );
185,021✔
431
                    }
185,021✔
432

185,021✔
433
                    if (didDamageClip) {
196,120✔
434
                        ctx.restore();
2✔
435
                    }
2✔
436

185,021✔
437
                    if (cell.style === "faded") {
196,120!
438
                        ctx.globalAlpha = 1;
×
439
                    }
✔
440

185,021✔
441
                    toDraw--;
185,021✔
442
                    if (drawingSpan) {
196,120✔
443
                        ctx.restore();
5✔
444
                        prepResult?.deprep?.(deprepArg);
5!
445
                        prepResult = undefined;
5✔
446
                        reclip();
5✔
447
                        font = colFont;
5✔
448
                        ctx.font = colFont;
5✔
449
                    }
5✔
450

185,021✔
451
                    return toDraw <= 0;
185,021✔
452
                }
196,120✔
453
            );
6,385✔
454

6,385✔
455
            ctx.restore();
6,385✔
456
            return toDraw <= 0;
6,385✔
457
        }
6,385✔
458
    );
666✔
459
    return result;
666✔
460
}
666✔
461

1✔
462
const allocatedItem: [number, number] = [0, 0];
1✔
463
const reusableRect = { x: 0, y: 0, width: 0, height: 0 };
1✔
464
const drawState: DrawStateTuple = [undefined, () => undefined];
1✔
465

1✔
466
let animationFrameRequested = false;
1✔
467
function animRequest(): void {
×
468
    animationFrameRequested = true;
×
469
}
×
470

1✔
471
export function drawCell(
1✔
472
    ctx: CanvasRenderingContext2D,
185,022✔
473
    cell: InnerGridCell,
185,022✔
474
    col: number,
185,022✔
475
    row: number,
185,022✔
476
    isLastCol: boolean,
185,022✔
477
    isLastRow: boolean,
185,022✔
478
    x: number,
185,022✔
479
    y: number,
185,022✔
480
    w: number,
185,022✔
481
    h: number,
185,022✔
482
    highlighted: boolean,
185,022✔
483
    theme: FullTheme,
185,022✔
484
    finalCellFillColor: string,
185,022✔
485
    imageLoader: ImageWindowLoader,
185,022✔
486
    spriteManager: SpriteManager,
185,022✔
487
    hoverAmount: number,
185,022✔
488
    hoverInfo: HoverInfo | undefined,
185,022✔
489
    hyperWrapping: boolean,
185,022✔
490
    frameTime: number,
185,022✔
491
    drawCellCallback: DrawCellCallback | undefined,
185,022✔
492
    lastPrep: PrepResult | undefined,
185,022✔
493
    enqueue: EnqueueCallback | undefined,
185,022✔
494
    renderStateProvider: RenderStateProvider,
185,022✔
495
    getCellRenderer: GetCellRendererCallback,
185,022✔
496
    overrideCursor: (cursor: React.CSSProperties["cursor"]) => void
185,022✔
497
): PrepResult | undefined {
185,022✔
498
    let hoverX: number | undefined;
185,022✔
499
    let hoverY: number | undefined;
185,022✔
500
    if (hoverInfo !== undefined && hoverInfo[0][0] === col && hoverInfo[0][1] === row) {
185,022✔
501
        hoverX = hoverInfo[1][0];
33✔
502
        hoverY = hoverInfo[1][1];
33✔
503
    }
33✔
504
    let result: PrepResult | undefined = undefined;
185,022✔
505

185,022✔
506
    allocatedItem[0] = col;
185,022✔
507
    allocatedItem[1] = row;
185,022✔
508

185,022✔
509
    reusableRect.x = x;
185,022✔
510
    reusableRect.y = y;
185,022✔
511
    reusableRect.width = w;
185,022✔
512
    reusableRect.height = h;
185,022✔
513

185,022✔
514
    drawState[0] = renderStateProvider.getValue(allocatedItem);
185,022✔
515
    drawState[1] = (val: any) => renderStateProvider.setValue(allocatedItem, val); //alloc
185,022✔
516

185,022✔
517
    animationFrameRequested = false;
185,022✔
518

185,022✔
519
    const args: DrawArgs<typeof cell> = {
185,022✔
520
        //alloc
185,022✔
521
        ctx,
185,022✔
522
        theme,
185,022✔
523
        col,
185,022✔
524
        row,
185,022✔
525
        cell,
185,022✔
526
        rect: reusableRect,
185,022✔
527
        highlighted,
185,022✔
528
        cellFillColor: finalCellFillColor,
185,022✔
529
        hoverAmount,
185,022✔
530
        frameTime,
185,022✔
531
        hoverX,
185,022✔
532
        drawState,
185,022✔
533
        hoverY,
185,022✔
534
        imageLoader,
185,022✔
535
        spriteManager,
185,022✔
536
        hyperWrapping,
185,022✔
537
        overrideCursor: hoverX !== undefined ? overrideCursor : undefined,
185,022✔
538
        requestAnimationFrame: animRequest,
185,022✔
539
    };
185,022✔
540
    const needsAnim = drawLastUpdateUnderlay(args, cell.lastUpdated, frameTime, lastPrep, isLastCol, isLastRow);
185,022✔
541

185,022✔
542
    const r = getCellRenderer(cell);
185,022✔
543
    if (r !== undefined) {
185,022✔
544
        if (lastPrep?.renderer !== r) {
185,022✔
545
            lastPrep?.deprep?.(args);
81,886✔
546
            lastPrep = undefined;
81,886✔
547
        }
81,886✔
548
        const partialPrepResult = r.drawPrep?.(args, lastPrep);
185,022✔
549
        if (drawCellCallback !== undefined && !isInnerOnlyCell(args.cell)) {
185,022!
550
            drawCellCallback(args as DrawArgs<GridCell>, () => r.draw(args, cell));
×
551
        } else {
185,022✔
552
            r.draw(args, cell);
185,022✔
553
        }
185,022✔
554
        result =
185,022✔
555
            partialPrepResult === undefined
185,022✔
556
                ? undefined
78,254✔
557
                : {
106,768✔
558
                    deprep: partialPrepResult?.deprep,
106,768✔
559
                    fillStyle: partialPrepResult?.fillStyle,
106,768✔
560
                    font: partialPrepResult?.font,
106,768✔
561
                    renderer: r,
106,768✔
562
                };
106,768✔
563
    }
185,022✔
564

185,022✔
565
    if (needsAnim || animationFrameRequested) enqueue?.(allocatedItem);
185,022!
566
    return result;
185,022✔
567
}
185,022✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc