@ornery/ui-grid-react 0.1.5 → 0.1.6

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.
@@ -0,0 +1,4 @@
1
+ import { type Root } from 'react-dom/client';
2
+ import { type UiGridProps } from './UiGrid';
3
+ export declare function mountUiGrid(container: Element | DocumentFragment, props: UiGridProps): Root;
4
+ //# sourceMappingURL=mountUiGrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mountUiGrid.d.ts","sourceRoot":"","sources":["../src/mountUiGrid.tsx"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEpD,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3F"}
@@ -0,0 +1,8 @@
1
+ import type { PipelineResult } from '@ornery/ui-grid';
2
+ type UiGridWasmModule = {
3
+ build_pipeline_js(context: unknown): PipelineResult;
4
+ };
5
+ export declare function registerReactUiGridWasmEngineFromModule(module: UiGridWasmModule): void;
6
+ export declare function enableReactUiGridWasmEngine(): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=rustWasmGridEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rustWasmGridEngine.d.ts","sourceRoot":"","sources":["../src/rustWasmGridEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMhF,KAAK,gBAAgB,GAAG;IACtB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAAC;CACrD,CAAC;AAEF,wBAAgB,uCAAuC,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAMtF;AAED,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAIjE"}
@@ -1,18 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import React$1 from 'react';
3
- import { GridOptions, UiGridApi, GridCellTemplateContext, GridExpandableTemplateContext, PipelineResult, GridColumnDef, GridLabels, SortState, GridCellPosition, GridBenchmarkResult, GridInfiniteScrollState, DisplayItem, GroupItem, ExpandableItem, RowItem, GridRow } from '@ornery/ui-grid';
4
- export { DEFAULT_GRID_LABELS, GridBenchmarkResult, GridCellEditableContext, GridCellTemplateContext, GridColumnDef, GridExpandableTemplateContext, GridLabels, GridOptions, GridRecord, GridRow, GridSavedState, SortState, UiGridApi } from '@ornery/ui-grid';
5
-
6
- interface UiGridProps {
7
- options: GridOptions;
8
- onRegisterApi?: (api: UiGridApi) => void;
9
- cellRenderer?: (context: GridCellTemplateContext) => React$1.ReactNode;
10
- expandableRenderer?: (context: GridExpandableTemplateContext) => React$1.ReactNode;
11
- className?: string;
12
- }
13
- declare function UiGrid({ options, onRegisterApi, cellRenderer, expandableRenderer, className, }: UiGridProps): react_jsx_runtime.JSX.Element;
14
-
15
- interface UseGridStateResult {
1
+ import { UiGridApi, GridOptions, GridColumnDef, GridRow, GridBenchmarkResult, GridCellPosition, GridLabels, SortState } from '@ornery/ui-grid';
2
+ import type { DisplayItem, GroupItem, ExpandableItem, RowItem, PipelineResult, GridInfiniteScrollState, GridCellTemplateContext, GridExpandableTemplateContext } from '@ornery/ui-grid';
3
+ export interface UseGridStateResult {
16
4
  pipeline: PipelineResult;
17
5
  visibleColumns: GridColumnDef[];
18
6
  labels: GridLabels;
@@ -118,38 +106,5 @@ interface UseGridStateResult {
118
106
  exportCsv: () => void;
119
107
  onViewportScroll: (startIndex: number) => void;
120
108
  }
121
- declare function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridApi) => void): UseGridStateResult;
122
-
123
- interface UseVirtualScrollOptions {
124
- itemCount: number;
125
- itemSize: number;
126
- viewportHeight: number;
127
- overscan?: number;
128
- }
129
- interface UseVirtualScrollResult {
130
- visibleRange: {
131
- start: number;
132
- end: number;
133
- };
134
- totalHeight: number;
135
- offsetY: number;
136
- onScroll: (event: React.UIEvent<HTMLDivElement>) => void;
137
- viewportRef: React.RefObject<HTMLDivElement | null>;
138
- scrollTop: number;
139
- }
140
- declare function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualScrollResult;
141
-
142
- declare function orderVisibleColumns(columns: readonly GridColumnDef[], order: readonly string[]): GridColumnDef[];
143
- declare function buildGridTemplateColumns(columns: readonly GridColumnDef[]): string;
144
- declare function resolveBenchmarkIterations(iterations?: number, configuredIterations?: number): number;
145
- declare function formatPaginationSummary(totalItems: number, firstRowIndex: number, lastRowIndex: number): string;
146
- declare function computeViewportHeightPx(viewportHeight?: number, autoViewportHeight?: number | null): string;
147
- declare function computeViewportRows(viewportHeight?: number, rowHeight?: number): number;
148
-
149
- type UiGridWasmModule = {
150
- build_pipeline_js(context: unknown): PipelineResult;
151
- };
152
- declare function registerReactUiGridWasmEngineFromModule(module: UiGridWasmModule): void;
153
- declare function enableReactUiGridWasmEngine(): Promise<void>;
154
-
155
- export { UiGrid, type UiGridProps, type UseGridStateResult, type UseVirtualScrollOptions, type UseVirtualScrollResult, buildGridTemplateColumns, computeViewportHeightPx, computeViewportRows, enableReactUiGridWasmEngine, formatPaginationSummary, orderVisibleColumns, registerReactUiGridWasmEngineFromModule, resolveBenchmarkIterations, useGridState, useVirtualScroll };
109
+ export declare function useGridState(options: GridOptions, onRegisterApi?: (api: UiGridApi) => void): UseGridStateResult;
110
+ //# sourceMappingURL=useGridState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGridState.d.ts","sourceRoot":"","sources":["../src/useGridState.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface UseVirtualScrollOptions {
2
+ itemCount: number;
3
+ itemSize: number;
4
+ viewportHeight: number;
5
+ overscan?: number;
6
+ }
7
+ export interface UseVirtualScrollResult {
8
+ visibleRange: {
9
+ start: number;
10
+ end: number;
11
+ };
12
+ totalHeight: number;
13
+ offsetY: number;
14
+ onScroll: (event: React.UIEvent<HTMLDivElement>) => void;
15
+ setScrollTop: (scrollTop: number) => void;
16
+ viewportRef: React.RefObject<HTMLDivElement | null>;
17
+ scrollTop: number;
18
+ }
19
+ export declare function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualScrollResult;
20
+ //# sourceMappingURL=useVirtualScroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVirtualScroll.d.ts","sourceRoot":"","sources":["../src/useVirtualScroll.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACzD,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CA0BzF"}
@@ -0,0 +1,17 @@
1
+ export interface VirtualWindowRequest {
2
+ itemCount: number;
3
+ itemSize: number;
4
+ viewportHeight: number;
5
+ overscan?: number;
6
+ scrollTop: number;
7
+ }
8
+ export interface VirtualWindowResult {
9
+ visibleRange: {
10
+ start: number;
11
+ end: number;
12
+ };
13
+ totalHeight: number;
14
+ offsetY: number;
15
+ }
16
+ export declare function calculateVirtualWindow(request: VirtualWindowRequest): VirtualWindowResult;
17
+ //# sourceMappingURL=virtualScrollMath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"virtualScrollMath.d.ts","sourceRoot":"","sources":["../src/virtualScrollMath.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CAqBzF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ornery/ui-grid-react",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "React wrapper for @ornery/ui-grid",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -16,7 +16,7 @@
16
16
  "peerDependencies": {
17
17
  "react": "^18.0.0 || ^19.0.0",
18
18
  "react-dom": "^18.0.0 || ^19.0.0",
19
- "@ornery/ui-grid": "^0.1.3"
19
+ "@ornery/ui-grid": "^0.1.6"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@ornery/ui-grid": "file:../ui-grid",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "scripts": {
36
36
  "start": "vite serve demo --config demo/vite.config.ts",
37
- "build": "tsup src/index.ts --format esm,cjs --dts --tsconfig tsconfig.json --external react --external react-dom --external @ornery/ui-grid",
37
+ "build": "npm run build:library --prefix ../.. && tsup src/index.ts --format esm,cjs --tsconfig tsconfig.build.json --external react --external react-dom --external @ornery/ui-grid && tsc -p tsconfig.dts.json",
38
38
  "test": "vitest run",
39
39
  "test:watch": "vitest"
40
40
  }
@@ -235,7 +235,8 @@ describe('UiGrid React component', () => {
235
235
  });
236
236
 
237
237
  expect(gridApi.core.getVisibleRows()).toHaveLength(5);
238
- expect(container.querySelector('.grid-viewport')).not.toBeNull();
238
+ expect(container.querySelector('.grid-virtual-spacer')).not.toBeNull();
239
+ expect(container.querySelector('.grid-virtual-body')).not.toBeNull();
239
240
  });
240
241
 
241
242
  it('paginates rows', () => {
package/src/UiGrid.tsx CHANGED
@@ -63,13 +63,103 @@ export function UiGrid({
63
63
  overscan: 3,
64
64
  });
65
65
 
66
+ const headerGridRef = React.useRef<HTMLDivElement | null>(null);
67
+ const filterGridRef = React.useRef<HTMLDivElement | null>(null);
68
+ const [openPinMenuColumn, setOpenPinMenuColumn] = React.useState<string | null>(null);
69
+ const [headerStickyHeight, setHeaderStickyHeight] = React.useState(0);
70
+ const [filterStickyHeight, setFilterStickyHeight] = React.useState(0);
71
+ const stickyChromeHeight = headerStickyHeight + filterStickyHeight;
72
+ const scrollContainerHeight = `${(options.viewportHeight ?? 560) + stickyChromeHeight}px`;
73
+
74
+ const eventPathIncludesClass = React.useCallback((event: Event, className: string): boolean => {
75
+ const eventPath = typeof event.composedPath === 'function'
76
+ ? event.composedPath()
77
+ : (event.target ? [event.target] : []);
78
+
79
+ return eventPath.some((target) => {
80
+ if (!target || typeof target !== 'object' || !('classList' in target)) {
81
+ return false;
82
+ }
83
+
84
+ const classList = (target as { classList?: DOMTokenList }).classList;
85
+ return classList?.contains(className) ?? false;
86
+ });
87
+ }, []);
88
+
89
+ const isPinMenuOpen = React.useCallback(
90
+ (column: GridColumnDef) => openPinMenuColumn === column.name,
91
+ [openPinMenuColumn],
92
+ );
93
+
94
+ const pinButtonLabel = React.useCallback(
95
+ (column: GridColumnDef) => (state.isPinned(column) ? labels.unpin : labels.pinColumn),
96
+ [labels, state],
97
+ );
98
+
99
+ const onPinTrigger = React.useCallback(
100
+ (column: GridColumnDef, event?: React.MouseEvent) => {
101
+ event?.stopPropagation();
102
+ if (state.isPinned(column)) {
103
+ setOpenPinMenuColumn(null);
104
+ state.gridApi.pinning.pinColumn(column.name, 'none');
105
+ return;
106
+ }
107
+
108
+ setOpenPinMenuColumn((current) => (current === column.name ? null : column.name));
109
+ },
110
+ [state],
111
+ );
112
+
113
+ const choosePinDirection = React.useCallback(
114
+ (column: GridColumnDef, direction: 'left' | 'right', event?: React.MouseEvent) => {
115
+ event?.stopPropagation();
116
+ setOpenPinMenuColumn(null);
117
+ state.gridApi.pinning.pinColumn(column.name, direction);
118
+ },
119
+ [state],
120
+ );
121
+
122
+ React.useLayoutEffect(() => {
123
+ setHeaderStickyHeight(headerGridRef.current?.offsetHeight ?? 0);
124
+ setFilterStickyHeight(filterGridRef.current?.offsetHeight ?? 0);
125
+ }, [visibleColumns, filteringFeature, options.enableFiltering]);
126
+
127
+ React.useEffect(() => {
128
+ if (!openPinMenuColumn) {
129
+ return;
130
+ }
131
+
132
+ const handleDocumentClick = (event: MouseEvent) => {
133
+ if (eventPathIncludesClass(event, 'pin-control')) {
134
+ return;
135
+ }
136
+
137
+ setOpenPinMenuColumn(null);
138
+ };
139
+
140
+ const handleDocumentEscape = (event: KeyboardEvent) => {
141
+ if (event.key === 'Escape') {
142
+ setOpenPinMenuColumn(null);
143
+ }
144
+ };
145
+
146
+ document.addEventListener('click', handleDocumentClick);
147
+ document.addEventListener('keydown', handleDocumentEscape);
148
+
149
+ return () => {
150
+ document.removeEventListener('click', handleDocumentClick);
151
+ document.removeEventListener('keydown', handleDocumentEscape);
152
+ };
153
+ }, [eventPathIncludesClass, openPinMenuColumn]);
154
+
66
155
  const itemsToRender = virtualizationEnabled
67
156
  ? displayItems.slice(virtualScroll.visibleRange.start, virtualScroll.visibleRange.end)
68
157
  : displayItems;
69
158
 
70
- const onViewportScroll = (event: React.UIEvent<HTMLDivElement>) => {
71
- virtualScroll.onScroll(event);
72
- const startIndex = Math.floor(event.currentTarget.scrollTop / rowSize);
159
+ const onGridTableScroll = (event: React.UIEvent<HTMLDivElement>) => {
160
+ const bodyScrollTop = Math.max(0, event.currentTarget.scrollTop - stickyChromeHeight);
161
+ virtualScroll.setScrollTop(bodyScrollTop);
162
+ const startIndex = Math.floor(bodyScrollTop / rowSize);
73
163
  state.onViewportScroll(startIndex);
74
164
  };
75
165
 
@@ -320,21 +410,28 @@ export function UiGrid({
320
410
  </p>
321
411
  </div>
322
412
 
323
- <div className="grid-table ui-grid-contents-wrapper" data-part="grid-table">
413
+ <div
414
+ className="grid-table ui-grid-contents-wrapper"
415
+ data-part="grid-table"
416
+ style={virtualizationEnabled ? { height: scrollContainerHeight, overflowY: 'auto' } : undefined}
417
+ onScroll={virtualizationEnabled ? onGridTableScroll : undefined}
418
+ >
324
419
  {/* Header row */}
325
420
  <div
326
421
  className="header-grid ui-grid-header ui-grid-header-canvas"
327
422
  data-part="header"
328
423
  role="row"
424
+ ref={headerGridRef}
329
425
  style={{ gridTemplateColumns }}
330
426
  >
331
427
  {visibleColumns.map((column) => {
332
428
  const pinned = state.isPinned(column);
333
429
  const pinOffset = pinned ? state.pinnedOffset(column) : null;
430
+ const pinMenuOpen = isPinMenuOpen(column);
334
431
  return (
335
432
  <div
336
433
  key={column.name}
337
- className={`header-cell ui-grid-header-cell${sortingFeature && state.sortDirection(column) !== 'none' ? ' is-active' : ''}${pinned ? ' is-pinned' : ''}`}
434
+ className={`header-cell ui-grid-header-cell${sortingFeature && state.sortDirection(column) !== 'none' ? ' is-active' : ''}${pinned ? ' is-pinned' : ''}${pinMenuOpen ? ' is-pin-menu-open' : ''}`}
338
435
  data-part="header-cell"
339
436
  role="columnheader"
340
437
  aria-sort={sortingFeature ? (state.sortAriaSort(column) as any) : undefined}
@@ -342,7 +439,7 @@ export function UiGrid({
342
439
  position: pinned ? 'sticky' : undefined,
343
440
  left: pinOffset?.side === 'left' ? pinOffset.offset : undefined,
344
441
  right: pinOffset?.side === 'right' ? pinOffset.offset : undefined,
345
- zIndex: pinned ? 2 : undefined,
442
+ zIndex: pinMenuOpen ? 8 : pinned ? 2 : undefined,
346
443
  }}
347
444
  >
348
445
  <span className="header-label">{state.headerLabel(column)}</span>
@@ -386,21 +483,62 @@ export function UiGrid({
386
483
  {state.pinningFeature &&
387
484
  state.isPinningEnabled() &&
388
485
  state.isColumnPinnable(column) && (
389
- <button
390
- type="button"
391
- className={`chip-action${pinned ? ' chip-action-active' : ''}`}
392
- data-part="pin-toggle"
393
- aria-label={pinned ? labels.unpin : labels.pinLeft}
394
- title={pinned ? labels.unpin : labels.pinLeft}
395
- onClick={() => state.togglePin(column)}
396
- >
397
- <svg viewBox="0 0 24 24" aria-hidden="true" focusable={false}>
398
- <path d="M16 12V4h1V2H7v2h1v8l-2 2v2h5v6l1 1 1-1v-6h5v-2l-2-2z" />
399
- </svg>
400
- <span className="sr-only ui-grid-sr-only">
401
- {pinned ? labels.unpin : labels.pinLeft}
402
- </span>
403
- </button>
486
+ <div className={`pin-control${pinMenuOpen ? ' pin-control-open' : ''}`} onClick={(event) => event.stopPropagation()}>
487
+ <button
488
+ type="button"
489
+ className={`chip-action pin-trigger${pinned || pinMenuOpen ? ' chip-action-active' : ''}`}
490
+ data-part="pin-toggle"
491
+ aria-label={pinButtonLabel(column)}
492
+ title={pinButtonLabel(column)}
493
+ aria-haspopup={pinned ? undefined : 'menu'}
494
+ aria-expanded={pinned ? undefined : pinMenuOpen}
495
+ onClick={(event) => onPinTrigger(column, event)}
496
+ >
497
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable={false}>
498
+ <path d="M16 12V4h1V2H7v2h1v8l-2 2v2h5v6l1 1 1-1v-6h5v-2l-2-2z" />
499
+ </svg>
500
+ <span className="sr-only ui-grid-sr-only">{pinButtonLabel(column)}</span>
501
+ </button>
502
+
503
+ <div
504
+ className="pin-menu"
505
+ data-part="pin-menu"
506
+ role="menu"
507
+ aria-label="Pin options"
508
+ aria-hidden={!pinMenuOpen}
509
+ >
510
+ <button
511
+ type="button"
512
+ className="pin-menu-action"
513
+ data-part="pin-left-action"
514
+ role="menuitem"
515
+ aria-label={labels.pinLeft}
516
+ title={labels.pinLeft}
517
+ tabIndex={pinMenuOpen ? 0 : -1}
518
+ onClick={(event) => choosePinDirection(column, 'left', event)}
519
+ >
520
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable={false}>
521
+ <path d="M10 6 4 12l6 6v-4h10v-4H10V6z" />
522
+ </svg>
523
+ <span className="sr-only ui-grid-sr-only">{labels.pinLeft}</span>
524
+ </button>
525
+ <button
526
+ type="button"
527
+ className="pin-menu-action"
528
+ data-part="pin-right-action"
529
+ role="menuitem"
530
+ aria-label={labels.pinRight}
531
+ title={labels.pinRight}
532
+ tabIndex={pinMenuOpen ? 0 : -1}
533
+ onClick={(event) => choosePinDirection(column, 'right', event)}
534
+ >
535
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable={false}>
536
+ <path d="M14 6v4H4v4h10v4l6-6-6-6z" />
537
+ </svg>
538
+ <span className="sr-only ui-grid-sr-only">{labels.pinRight}</span>
539
+ </button>
540
+ </div>
541
+ </div>
404
542
  )}
405
543
  </div>
406
544
  </div>
@@ -413,7 +551,8 @@ export function UiGrid({
413
551
  <div
414
552
  className="filter-grid ui-grid-header"
415
553
  data-part="filters"
416
- style={{ gridTemplateColumns }}
554
+ ref={filterGridRef}
555
+ style={{ gridTemplateColumns, ['--ui-grid-header-sticky-top' as string]: `${headerStickyHeight}px` }}
417
556
  >
418
557
  {visibleColumns.map((column) => {
419
558
  const pinned = state.isPinned(column);
@@ -450,29 +589,19 @@ export function UiGrid({
450
589
  {/* Body */}
451
590
  {displayItems.length > 0 ? (
452
591
  virtualizationEnabled ? (
453
- <div
454
- className="grid-viewport ui-grid-viewport"
455
- data-part="viewport"
456
- ref={virtualScroll.viewportRef}
457
- style={{ height: viewportHeightPx, overflow: 'auto', position: 'relative' }}
458
- onScroll={onViewportScroll}
459
- >
460
- <div style={{ height: `${virtualScroll.totalHeight}px`, position: 'relative' }}>
461
- <div
462
- className="body-grid ui-grid-canvas"
463
- data-part="body"
464
- role="rowgroup"
465
- style={{
466
- gridTemplateColumns,
467
- position: 'absolute',
468
- top: 0,
469
- left: 0,
470
- right: 0,
471
- transform: `translateY(${virtualScroll.offsetY}px)`,
472
- }}
473
- >
474
- {itemsToRender.map(renderDisplayItem)}
475
- </div>
592
+ <div className="grid-virtual-spacer" style={{ height: `${virtualScroll.totalHeight}px` }}>
593
+ <div
594
+ className="body-grid ui-grid-canvas grid-virtual-body"
595
+ data-part="body"
596
+ role="rowgroup"
597
+ style={{
598
+ gridTemplateColumns,
599
+ position: 'absolute',
600
+ top: `${virtualScroll.offsetY}px`,
601
+ left: 0,
602
+ }}
603
+ >
604
+ {itemsToRender.map(renderDisplayItem)}
476
605
  </div>
477
606
  </div>
478
607
  ) : (
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { UiGrid } from './UiGrid';
2
2
  export type { UiGridProps } from './UiGrid';
3
+ export { mountUiGrid } from './mountUiGrid';
3
4
  export { useGridState } from './useGridState';
4
5
  export type { UseGridStateResult } from './useGridState';
5
6
  export { useVirtualScroll } from './useVirtualScroll';
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { createRoot, type Root } from 'react-dom/client';
3
+
4
+ import { UiGrid, type UiGridProps } from './UiGrid';
5
+
6
+ export function mountUiGrid(container: Element | DocumentFragment, props: UiGridProps): Root {
7
+ const root = createRoot(container);
8
+ root.render(React.createElement(UiGrid, props));
9
+ return root;
10
+ }
@@ -1,7 +1,8 @@
1
1
  import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid';
2
2
  import { registerRustWasmGridEngine } from '@ornery/ui-grid';
3
3
 
4
- const uiGridWasmModulePath = '../../../dist/ui-grid-wasm/ui_grid_wasm.js';
4
+ const uiGridWasmModulePath = '../../../dist/ui-grid-wasm-web/ui_grid_wasm.js';
5
+ const uiGridWasmBinaryPath = '/dist/ui-grid-wasm-web/ui_grid_wasm_bg.wasm';
5
6
 
6
7
  type UiGridWasmModule = {
7
8
  build_pipeline_js(context: unknown): PipelineResult;
@@ -17,5 +18,6 @@ export function registerReactUiGridWasmEngineFromModule(module: UiGridWasmModule
17
18
 
18
19
  export async function enableReactUiGridWasmEngine(): Promise<void> {
19
20
  const module = await import(/* @vite-ignore */ uiGridWasmModulePath);
21
+ await module.default(uiGridWasmBinaryPath);
20
22
  registerReactUiGridWasmEngineFromModule(module);
21
23
  }