@octanejs/window 0.0.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 (43) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +89 -0
  3. package/UPSTREAM.md +116 -0
  4. package/package.json +68 -0
  5. package/src/components/grid/Grid.tsx +329 -0
  6. package/src/components/grid/types.ts +334 -0
  7. package/src/components/grid/useGridCallbackRef.ts +16 -0
  8. package/src/components/grid/useGridRef.ts +12 -0
  9. package/src/components/list/List.tsx +216 -0
  10. package/src/components/list/isDynamicRowHeight.ts +10 -0
  11. package/src/components/list/types.ts +214 -0
  12. package/src/components/list/useDynamicRowHeight.ts +170 -0
  13. package/src/components/list/useListCallbackRef.ts +16 -0
  14. package/src/components/list/useListRef.ts +12 -0
  15. package/src/constants.ts +3 -0
  16. package/src/core/createCachedBounds.ts +65 -0
  17. package/src/core/getEstimatedSize.ts +25 -0
  18. package/src/core/getOffsetForIndex.ts +75 -0
  19. package/src/core/getStartStopIndices.ts +68 -0
  20. package/src/core/types.ts +14 -0
  21. package/src/core/useCachedBounds.ts +29 -0
  22. package/src/core/useIsRtl.ts +27 -0
  23. package/src/core/useItemSize.ts +33 -0
  24. package/src/core/useVirtualizer.ts +269 -0
  25. package/src/hooks/useIsomorphicLayoutEffect.ts +10 -0
  26. package/src/hooks/useMemoizedObject.ts +14 -0
  27. package/src/hooks/useResizeObserver.ts +98 -0
  28. package/src/hooks/useStableCallback.ts +39 -0
  29. package/src/index.ts +20 -0
  30. package/src/internal.ts +37 -0
  31. package/src/types.ts +5 -0
  32. package/src/utils/adjustScrollOffsetForRtl.ts +35 -0
  33. package/src/utils/areArraysEqual.ts +13 -0
  34. package/src/utils/arePropsEqual.ts +19 -0
  35. package/src/utils/assert.ts +10 -0
  36. package/src/utils/colors/getContrastColor.ts +47 -0
  37. package/src/utils/colors/stringToColor.ts +13 -0
  38. package/src/utils/debug.ts +13 -0
  39. package/src/utils/getRTLOffsetType.ts +46 -0
  40. package/src/utils/getScrollbarSize.ts +23 -0
  41. package/src/utils/isRtl.ts +12 -0
  42. package/src/utils/parseNumericStyleValue.ts +17 -0
  43. package/src/utils/shallowCompare.ts +26 -0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Brian Vaughn
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,89 @@
1
+ # @octanejs/window
2
+
3
+ The exact [`react-window@2.3.0`](https://github.com/bvaughn/react-window) API for
4
+ the [Octane](https://github.com/octanejs/octane) renderer. Change the package
5
+ import and keep the current v2 `List`, `Grid`, sizing, callback, ref, and
6
+ imperative-scrolling contracts.
7
+
8
+ ```diff
9
+ -import { List } from 'react-window';
10
+ +import { List } from '@octanejs/window';
11
+ ```
12
+
13
+ ```tsx
14
+ import { Grid, List, type CellComponentProps, type RowComponentProps } from '@octanejs/window';
15
+
16
+ function Row({ index, style, ariaAttributes }: RowComponentProps) {
17
+ return <div {...ariaAttributes} style={style}>{'Row ' + index}</div>;
18
+ }
19
+
20
+ function Cell({ columnIndex, rowIndex, style, ariaAttributes }: CellComponentProps) {
21
+ return <div {...ariaAttributes} style={style}>{`${rowIndex}:${columnIndex}`}</div>;
22
+ }
23
+
24
+ export function VirtualizedData() @{
25
+ <section>
26
+ <List
27
+ defaultHeight={300}
28
+ rowComponent={Row}
29
+ rowCount={10_000}
30
+ rowHeight={32}
31
+ rowProps={{}}
32
+ style={{ height: 300 }}
33
+ />
34
+ <Grid
35
+ cellComponent={Cell}
36
+ cellProps={{}}
37
+ columnCount={100}
38
+ columnWidth={100}
39
+ defaultHeight={300}
40
+ defaultWidth={600}
41
+ rowCount={10_000}
42
+ rowHeight={32}
43
+ style={{ height: 300, width: 600 }}
44
+ />
45
+ </section>
46
+ }
47
+ ```
48
+
49
+ The package includes all eight v2 runtime exports and all eight public type
50
+ exports from the pinned release. Fixed, percentage, function, and dynamic row
51
+ sizes; overscan; RTL grids; ARIA attributes; custom root tags; imperative refs;
52
+ SSR; and hydration are covered by executable parity evidence.
53
+
54
+ ## Version boundary
55
+
56
+ This package targets the current `react-window` v2 API. The older v1
57
+ `FixedSizeList`, `VariableSizeList`, `FixedSizeGrid`, and `VariableSizeGrid`
58
+ names are not exports of `react-window@2.3.0` and are intentionally not added
59
+ here. Applications still using those names need a v1-specific migration before
60
+ changing imports.
61
+
62
+ ## Renderer differences
63
+
64
+ Consumer-visible rendering, keyed state, scrolling, measurement, and final DOM
65
+ match the React package. Two renderer-internal observations differ:
66
+
67
+ - Octane reserves the second raw function-component invocation argument for its
68
+ internal block ABI. Tests and instrumentation should assert component props,
69
+ not React's undocumented `undefined` second argument.
70
+ - After keyed reordering, sibling effect order and equal-prop rerender counts
71
+ can differ. Do not depend on sibling effect ordering; keyed state and DOM
72
+ identity are preserved.
73
+
74
+ The pinned source, npm declaration bundle, license, test inventory, allowed
75
+ adaptations, and executable pristine/adapted/type/differential/SSR/hydration
76
+ lanes are recorded in [`UPSTREAM.md`](./UPSTREAM.md) and
77
+ [`audit/react-parity.json`](./audit/react-parity.json).
78
+
79
+ ## License
80
+
81
+ MIT — contains source derived from
82
+ [react-window](https://github.com/bvaughn/react-window) (MIT, © Brian Vaughn),
83
+ adapted for Octane.
84
+
85
+ ## Status
86
+
87
+ Current scope and verification status are tracked in the generated
88
+ [bindings status table](../../docs/bindings-status.md), sourced from
89
+ [`status.json`](./status.json).
package/UPSTREAM.md ADDED
@@ -0,0 +1,116 @@
1
+ # react-window upstream contract
2
+
3
+ ## Immutable pin
4
+
5
+ | Field | Value |
6
+ | --- | --- |
7
+ | Package | `react-window` |
8
+ | Version | `2.3.0` |
9
+ | Repository | `https://github.com/bvaughn/react-window.git` |
10
+ | Annotated tag | `2.3.0` (`c8f17487…`) |
11
+ | Dereferenced tag commit | `4d9eebbb510262b3b7e95463cf49a10de53ea77d` |
12
+ | npm integrity | `sha512-FW6TIpaOH646k51X7yE+LSCWGkt5Pfsnc1fVyq/sCI9h0pTqmMiBXM04pzFKg3Bt7NGkeV6kqbU8d/QjmFS7Ug==` |
13
+ | npm shasum | `92fefee75b7de56a31204dfffc492b84136e4783` |
14
+ | npm tarball SHA-256 | `c62b0568794a8cf5f523fa6fd68f83261cfdc9bb7578e918ca2ae1181fc44623` |
15
+ | Canonical tag archive SHA-256 | `d0b66c0138c6355051a75086ce0681aa5880249c0d14f1e9759185daee16e452` |
16
+ | License | MIT, copyright Brian Vaughn |
17
+
18
+ The byte-exact tagged `lib/` tree, published npm declaration bundle, repository
19
+ package metadata, and license are vendored under `upstream/` for provenance and
20
+ parity evidence. They are audit inputs only and must remain excluded from the
21
+ published package.
22
+
23
+ Run `pnpm --dir packages/window upstream:verify` to verify all 58 vendored
24
+ artifacts, the exact file set, the published declaration bundle, the 14 upstream
25
+ test artifacts and their 117 test registrations, package metadata, and the
26
+ complete root export inventory.
27
+
28
+ ## Public v2.3.0 surface
29
+
30
+ Runtime exports: `Grid`, `List`, `getScrollbarSize`, `useDynamicRowHeight`,
31
+ `useGridCallbackRef`, `useGridRef`, `useListCallbackRef`, and `useListRef`.
32
+
33
+ Type exports: `Align`, `CellComponentProps`, `DynamicRowHeight`,
34
+ `GridImperativeAPI`, `GridProps`, `ListImperativeAPI`, `ListProps`, and
35
+ `RowComponentProps`.
36
+
37
+ The v1 `FixedSizeList`, `VariableSizeList`, `FixedSizeGrid`, and
38
+ `VariableSizeGrid` names are not part of this pin and are intentionally outside
39
+ the binding contract.
40
+
41
+ ## Source boundary and module disposition
42
+
43
+ The immutable boundary is every file under upstream `lib/`, plus `src/constants.ts`,
44
+ `vitest.setup.js`, `package.json`, and the MIT license. Production modules are
45
+ ported source-correspondently under `src/`; test modules are either executed
46
+ byte-exact in the pristine lane or generated into `tests/upstream/` using the
47
+ audited transformations in `audit/adapted-transformations.json`. Upstream test
48
+ utilities remain byte-locked inputs and are mapped to the adapted utilities by
49
+ the generator. No vendored file is published.
50
+
51
+ | Upstream module class | Octane disposition | Evidence |
52
+ | --- | --- | --- |
53
+ | `lib/components/**`, `lib/core/**`, `lib/hooks/**`, `lib/utils/**` production files | Source-correspondent port in `src/**`; React imports and renderer syntax are the only framework adaptations | `audit/adapted-transformations.json`, `tests/audit/adapted.test.mjs` |
54
+ | `lib/**/*.test.{ts,tsx}` | Unchanged pristine execution and generated adapted execution | `audit/pristine-runtime.json`, `audit/adapted-runtime.json` |
55
+ | `lib/utils/test/**`, `vitest.setup.js` | Unchanged pristine support; source-correspondent adapted support | `upstream/SHA256SUMS`, `tests/generate-adapted-tests.mjs` |
56
+ | Published declaration bundle | Unchanged pristine oracle; identical shared assertion program targets Octane source | `audit/type-contract.json`, `typetests/**` |
57
+ | Repository metadata and license | Vendored provenance only | `tests/audit/upstream.test.mjs` |
58
+
59
+ ## Export crosswalk
60
+
61
+ | Public export | Kind | Classification | Octane evidence |
62
+ | --- | --- | --- | --- |
63
+ | `Grid` | runtime | Exact component port | upstream Grid suites, runtime, differential, SSR, hydration |
64
+ | `List` | runtime | Exact component port | upstream List suites, runtime, differential, SSR, hydration |
65
+ | `getScrollbarSize` | runtime | Source-identical utility | upstream inventory and shared type contract |
66
+ | `useDynamicRowHeight` | runtime | Exact hook port with compiler-slot plumbing | upstream hook suite, runtime dynamic-height suite |
67
+ | `useGridCallbackRef` | runtime | Exact public React hook signature; Octane state slot internally | shared types and grid initializer runtime case |
68
+ | `useGridRef` | runtime | Exact public React hook signature; Octane ref slot internally | shared types and grid initializer runtime case |
69
+ | `useListCallbackRef` | runtime | Exact public React hook signature; Octane state slot internally | shared types and list initializer runtime case |
70
+ | `useListRef` | runtime | Exact public React hook signature; Octane ref slot internally | shared types and list initializer runtime case |
71
+ | `Align` | type | Exact published declaration | shared type contract group `utilities` |
72
+ | `CellComponentProps` | type | Exact published declaration | shared type contract group `component-props` |
73
+ | `DynamicRowHeight` | type | Exact published declaration | shared type contract group `dynamic-height` |
74
+ | `GridImperativeAPI` | type | Exact published declaration | shared type contract groups `ref-hooks`, `imperative-api` |
75
+ | `GridProps` | type | Exact published declaration | shared type contract groups `list-grid-props`, `forbidden-generated-props` |
76
+ | `ListImperativeAPI` | type | Exact published declaration | shared type contract groups `ref-hooks`, `imperative-api` |
77
+ | `ListProps` | type | Exact published declaration | shared type contract groups `list-grid-props`, `forbidden-generated-props` |
78
+ | `RowComponentProps` | type | Exact published declaration | shared type contract group `component-props` |
79
+
80
+ ## Upstream test crosswalk
81
+
82
+ Every row runs unchanged in `react-window-pristine` and as a generator-produced,
83
+ audited adaptation in `react-window-adapted`; the inventories prove all 117
84
+ registered cases are unique and executed.
85
+
86
+ | Upstream test file | Pristine disposition | Adapted disposition |
87
+ | --- | --- | --- |
88
+ | `components/grid/Grid.test.tsx` | byte-exact | generated framework adaptation |
89
+ | `components/list/List.test.tsx` | byte-exact | generated framework adaptation |
90
+ | `components/list/useDynamicRowHeight.test.ts` | byte-exact | generated framework adaptation |
91
+ | `core/createCachedBounds.test.ts` | byte-exact | generated import adaptation |
92
+ | `core/getEstimatedSize.test.ts` | byte-exact | generated import adaptation |
93
+ | `core/getOffsetForIndex.test.ts` | byte-exact | generated import adaptation |
94
+ | `core/getStartStopIndices.test.ts` | byte-exact | generated import adaptation |
95
+ | `core/useCachedBounds.test.ts` | byte-exact | generated hook adaptation |
96
+ | `core/useVirtualizer.test.ts` | byte-exact | generated hook adaptation |
97
+ | `hooks/useMemoizedObject.test.ts` | byte-exact | generated hook adaptation |
98
+ | `hooks/useResizeObserver.test.ts` | byte-exact | generated hook adaptation |
99
+ | `hooks/useStableCallback.test.tsx` | byte-exact | generated hook adaptation |
100
+ | `utils/parseNumericStyleValue.test.ts` | byte-exact | generated import adaptation |
101
+ | `utils/shallowCompare.test.ts` | byte-exact | generated import adaptation |
102
+
103
+ ## Port-authored evidence crosswalk
104
+
105
+ | Authored evidence | Classification | React-to-Octane pairing / citation |
106
+ | --- | --- | --- |
107
+ | `tests/runtime/{grid,list,dynamic-height}.test.ts` | Adapted consumer behavior | Supplements the exact upstream suites with Octane-native DOM, imperative API, initializer, and measurement coverage |
108
+ | `tests/differential/parity.test.ts` | Differential | One shared TSX fixture is compiled for React and Octane; the rig compares serialized DOM after native scrolling |
109
+ | `tests/ssr/ssr.test.ts` | Adapted SSR | Exercises the same public `List`/`Grid` defaults pinned by the declaration and upstream component suites |
110
+ | `tests/hydration.test.ts` | Adapted hydration | Adopts nodes emitted by the SSR fixture, then proves live ResizeObserver and scrolling behavior |
111
+ | `typetests/parity.test-d.ts` | Paired type oracle | Identical assertion groups compile against the npm declaration and Octane source; `audit/type-contract.json` forbids source transforms |
112
+ | `typetests/negative/missing-coordinate.test-d.ts` | Paired negative type control | Both compilers reject the same incomplete Grid coordinate |
113
+ | `tests/audit/upstream.test.mjs` | Provenance audit | Pins the exact file/export/test inventory in both directions |
114
+ | `tests/audit/adapted.test.mjs` | Adaptation audit | Regenerates and byte-compares every adapted upstream test/source mapping with mutation controls |
115
+ | `tests/audit/types.test.mjs` | Type-evidence audit | Accounts for every assertion group and proves skipped/deleted/unauthorized mutations fail |
116
+ | `tests/feasibility/renderer-boundary.test.ts` | Framework-boundary characterization | Documents the two reviewed renderer ABI/scheduling divergences recorded in the manifest |
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "@octanejs/window",
3
+ "version": "0.0.1",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.22.2"
8
+ },
9
+ "octane": {
10
+ "hookSlots": {
11
+ "manual": [
12
+ "src"
13
+ ]
14
+ }
15
+ },
16
+ "description": "Exact react-window 2.3.0 binding for Octane: virtualized List and Grid components, dynamic row measurement, imperative scrolling, SSR, and hydration.",
17
+ "author": {
18
+ "name": "Dominic Gannaway",
19
+ "email": "dg@domgan.com"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/octanejs/octane.git",
27
+ "directory": "packages/window"
28
+ },
29
+ "main": "src/index.ts",
30
+ "module": "src/index.ts",
31
+ "types": "src/index.ts",
32
+ "files": [
33
+ "src",
34
+ "README.md",
35
+ "UPSTREAM.md",
36
+ "LICENSE.md"
37
+ ],
38
+ "exports": {
39
+ ".": "./src/index.ts"
40
+ },
41
+ "dependencies": {
42
+ "@types/react": "^19.2.17"
43
+ },
44
+ "peerDependencies": {
45
+ "octane": "0.1.35"
46
+ },
47
+ "devDependencies": {
48
+ "@testing-library/jest-dom": "^6.9.1",
49
+ "@testing-library/react": "16.3.2",
50
+ "@tsrx/react": "^0.2.56",
51
+ "esbuild": "^0.28.1",
52
+ "react": "^19.2.7",
53
+ "react-dom": "^19.2.7",
54
+ "react-window": "2.3.0",
55
+ "vitest": "^4.1.10",
56
+ "@octanejs/testing-library": "0.1.32",
57
+ "octane": "0.1.35"
58
+ },
59
+ "scripts": {
60
+ "adapted:generate": "node tests/generate-adapted-tests.mjs",
61
+ "test": "pnpm upstream:verify && pnpm test:audit && node ../../scripts/react-parity/harness.mjs run-required --manifest packages/window/audit/react-parity.json",
62
+ "upstream:verify": "node tests/audit/verify-upstream.mjs",
63
+ "test:audit": "node --test tests/audit/*.test.mjs",
64
+ "test:types:pristine": "tsc --noEmit -p typetests/tsconfig.pristine.json",
65
+ "test:types:adapted": "tsrx-tsc --noEmit -p typetests/tsconfig.adapted.json",
66
+ "typecheck": "tsrx-tsc --noEmit -p tsconfig.json"
67
+ }
68
+ }
@@ -0,0 +1,329 @@
1
+ /** @jsxImportSource octane */
2
+ 'use client';
3
+
4
+ import { createElement, memo, useEffect, useImperativeHandle, useMemo, useState } from 'octane';
5
+ import { useIsRtl } from '../../core/useIsRtl.js';
6
+ import { useVirtualizer } from '../../core/useVirtualizer.js';
7
+ import { useMemoizedObject } from '../../hooks/useMemoizedObject.js';
8
+ import type { Align, TagNames } from '../../types.js';
9
+ import { arePropsEqual } from '../../utils/arePropsEqual.js';
10
+ import type { GridProps } from './types.js';
11
+
12
+ /**
13
+ * Renders data with many rows and columns.
14
+ *
15
+ * ℹ️ Unlike `List` rows, `Grid` cell sizes must be known ahead of time.
16
+ * Either static sizes or something that can be derived (from the data in `CellProps`) without rendering.
17
+ */
18
+ export function Grid<CellProps extends object, TagName extends TagNames = 'div'>({
19
+ cellComponent: CellComponentProp,
20
+ cellProps: cellPropsUnstable,
21
+ children,
22
+ className,
23
+ columnCount,
24
+ columnKey,
25
+ columnWidth,
26
+ defaultHeight = 0,
27
+ defaultWidth = 0,
28
+ dir,
29
+ gridRef,
30
+ onCellsRendered,
31
+ onResize,
32
+ overscanCount = 3,
33
+ rowCount,
34
+ rowHeight,
35
+ rowKey,
36
+ style,
37
+ tagName = 'div' as TagName,
38
+ ...rest
39
+ }: GridProps<CellProps, TagName>) {
40
+ const cellProps = useMemoizedObject(cellPropsUnstable);
41
+ const CellComponent = useMemo(
42
+ () => memo(CellComponentProp as any, arePropsEqual as any) as typeof CellComponentProp,
43
+ [CellComponentProp],
44
+ );
45
+
46
+ const [element, setElement] = useState<HTMLDivElement | null>(null);
47
+
48
+ const isRtl = useIsRtl(element, dir);
49
+
50
+ const {
51
+ getCellBounds: getColumnBounds,
52
+ getEstimatedSize: getEstimatedWidth,
53
+ startIndexOverscan: columnStartIndexOverscan,
54
+ startIndexVisible: columnStartIndexVisible,
55
+ scrollToIndex: scrollToColumnIndex,
56
+ stopIndexOverscan: columnStopIndexOverscan,
57
+ stopIndexVisible: columnStopIndexVisible,
58
+ } = useVirtualizer({
59
+ containerElement: element,
60
+ containerStyle: style,
61
+ defaultContainerSize: defaultWidth,
62
+ direction: 'horizontal',
63
+ isRtl,
64
+ itemCount: columnCount,
65
+ itemProps: cellProps,
66
+ itemSize: columnWidth,
67
+ onResize,
68
+ overscanCount,
69
+ });
70
+
71
+ const {
72
+ getCellBounds: getRowBounds,
73
+ getEstimatedSize: getEstimatedHeight,
74
+ startIndexOverscan: rowStartIndexOverscan,
75
+ startIndexVisible: rowStartIndexVisible,
76
+ scrollToIndex: scrollToRowIndex,
77
+ stopIndexOverscan: rowStopIndexOverscan,
78
+ stopIndexVisible: rowStopIndexVisible,
79
+ } = useVirtualizer({
80
+ containerElement: element,
81
+ containerStyle: style,
82
+ defaultContainerSize: defaultHeight,
83
+ direction: 'vertical',
84
+ itemCount: rowCount,
85
+ itemProps: cellProps,
86
+ itemSize: rowHeight,
87
+ onResize,
88
+ overscanCount,
89
+ });
90
+
91
+ useImperativeHandle(
92
+ gridRef,
93
+ () => ({
94
+ get element() {
95
+ return element;
96
+ },
97
+
98
+ scrollToCell({
99
+ behavior = 'auto',
100
+ columnAlign = 'auto',
101
+ columnIndex,
102
+ rowAlign = 'auto',
103
+ rowIndex,
104
+ }: {
105
+ behavior?: ScrollBehavior;
106
+ columnAlign?: Align;
107
+ columnIndex: number;
108
+ rowAlign?: Align;
109
+ rowIndex: number;
110
+ }) {
111
+ const left = scrollToColumnIndex({
112
+ align: columnAlign,
113
+ containerScrollOffset: element?.scrollLeft ?? 0,
114
+ index: columnIndex,
115
+ });
116
+ const top = scrollToRowIndex({
117
+ align: rowAlign,
118
+ containerScrollOffset: element?.scrollTop ?? 0,
119
+ index: rowIndex,
120
+ });
121
+
122
+ if (typeof element?.scrollTo === 'function') {
123
+ element.scrollTo({
124
+ behavior,
125
+ left,
126
+ top,
127
+ });
128
+ }
129
+ },
130
+
131
+ scrollToColumn({
132
+ align = 'auto',
133
+ behavior = 'auto',
134
+ index,
135
+ }: {
136
+ align?: Align;
137
+ behavior?: ScrollBehavior;
138
+ index: number;
139
+ }) {
140
+ const left = scrollToColumnIndex({
141
+ align,
142
+ containerScrollOffset: element?.scrollLeft ?? 0,
143
+ index,
144
+ });
145
+
146
+ if (typeof element?.scrollTo === 'function') {
147
+ element.scrollTo({
148
+ behavior,
149
+ left,
150
+ });
151
+ }
152
+ },
153
+
154
+ scrollToRow({
155
+ align = 'auto',
156
+ behavior = 'auto',
157
+ index,
158
+ }: {
159
+ align?: Align;
160
+ behavior?: ScrollBehavior;
161
+ index: number;
162
+ }) {
163
+ const top = scrollToRowIndex({
164
+ align,
165
+ containerScrollOffset: element?.scrollTop ?? 0,
166
+ index,
167
+ });
168
+
169
+ if (typeof element?.scrollTo === 'function') {
170
+ element.scrollTo({
171
+ behavior,
172
+ top,
173
+ });
174
+ }
175
+ },
176
+ }),
177
+ [element, scrollToColumnIndex, scrollToRowIndex],
178
+ );
179
+
180
+ useEffect(() => {
181
+ if (
182
+ columnStartIndexOverscan >= 0 &&
183
+ columnStopIndexOverscan >= 0 &&
184
+ rowStartIndexOverscan >= 0 &&
185
+ rowStopIndexOverscan >= 0 &&
186
+ onCellsRendered
187
+ ) {
188
+ onCellsRendered(
189
+ {
190
+ columnStartIndex: columnStartIndexVisible,
191
+ columnStopIndex: columnStopIndexVisible,
192
+ rowStartIndex: rowStartIndexVisible,
193
+ rowStopIndex: rowStopIndexVisible,
194
+ },
195
+ {
196
+ columnStartIndex: columnStartIndexOverscan,
197
+ columnStopIndex: columnStopIndexOverscan,
198
+ rowStartIndex: rowStartIndexOverscan,
199
+ rowStopIndex: rowStopIndexOverscan,
200
+ },
201
+ );
202
+ }
203
+ }, [
204
+ onCellsRendered,
205
+ columnStartIndexOverscan,
206
+ columnStartIndexVisible,
207
+ columnStopIndexOverscan,
208
+ columnStopIndexVisible,
209
+ rowStartIndexOverscan,
210
+ rowStartIndexVisible,
211
+ rowStopIndexOverscan,
212
+ rowStopIndexVisible,
213
+ ]);
214
+
215
+ const cells = useMemo(() => {
216
+ const children: unknown[] = [];
217
+ if (columnCount > 0 && rowCount > 0) {
218
+ for (let rowIndex = rowStartIndexOverscan; rowIndex <= rowStopIndexOverscan; rowIndex++) {
219
+ const rowBounds = getRowBounds(rowIndex);
220
+
221
+ const columns: unknown[] = [];
222
+
223
+ for (
224
+ let columnIndex = columnStartIndexOverscan;
225
+ columnIndex <= columnStopIndexOverscan;
226
+ columnIndex++
227
+ ) {
228
+ const columnBounds = getColumnBounds(columnIndex);
229
+
230
+ columns.push(
231
+ <CellComponent
232
+ {...(cellProps as CellProps)}
233
+ ariaAttributes={{
234
+ 'aria-colindex': columnIndex + 1,
235
+ role: 'gridcell',
236
+ }}
237
+ columnIndex={columnIndex}
238
+ key={
239
+ columnKey
240
+ ? columnKey({
241
+ columnIndex,
242
+ data: cellProps,
243
+ rowIndex,
244
+ })
245
+ : columnIndex
246
+ }
247
+ rowIndex={rowIndex}
248
+ style={{
249
+ position: 'absolute',
250
+ left: isRtl ? undefined : 0,
251
+ right: isRtl ? 0 : undefined,
252
+ transform: `translate(${isRtl ? -columnBounds.scrollOffset : columnBounds.scrollOffset}px, ${rowBounds.scrollOffset}px)`,
253
+ height: rowBounds.size,
254
+ width: columnBounds.size,
255
+ }}
256
+ />,
257
+ );
258
+ }
259
+
260
+ children.push(
261
+ <div
262
+ key={
263
+ rowKey
264
+ ? rowKey({
265
+ data: cellProps,
266
+ rowIndex,
267
+ })
268
+ : rowIndex
269
+ }
270
+ role="row"
271
+ aria-rowindex={rowIndex + 1}
272
+ >
273
+ {columns}
274
+ </div>,
275
+ );
276
+ }
277
+ }
278
+ return children;
279
+ }, [
280
+ CellComponent,
281
+ cellProps,
282
+ columnCount,
283
+ columnKey,
284
+ columnStartIndexOverscan,
285
+ columnStopIndexOverscan,
286
+ getColumnBounds,
287
+ getRowBounds,
288
+ isRtl,
289
+ rowCount,
290
+ rowKey,
291
+ rowStartIndexOverscan,
292
+ rowStopIndexOverscan,
293
+ ]);
294
+
295
+ const sizingElement = (
296
+ <div
297
+ aria-hidden
298
+ style={{
299
+ height: getEstimatedHeight(),
300
+ width: getEstimatedWidth(),
301
+ zIndex: -1,
302
+ }}
303
+ ></div>
304
+ );
305
+
306
+ return createElement(
307
+ tagName,
308
+ {
309
+ 'aria-colcount': columnCount,
310
+ 'aria-rowcount': rowCount,
311
+ role: 'grid',
312
+ ...rest,
313
+ className,
314
+ dir,
315
+ ref: setElement,
316
+ style: {
317
+ position: 'relative',
318
+ maxHeight: '100%',
319
+ maxWidth: '100%',
320
+ flexGrow: 1,
321
+ overflow: 'auto',
322
+ ...style,
323
+ },
324
+ },
325
+ cells,
326
+ children,
327
+ sizingElement,
328
+ );
329
+ }