@ornery/ui-grid-react 0.1.10 → 1.0.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.
@@ -28,7 +28,7 @@ describe('gridStateMath', () => {
28
28
  it('builds grid template columns deterministically', () => {
29
29
  expect(
30
30
  buildGridTemplateColumns(orderVisibleColumns(columns, ['status', 'revenue', 'customer'])),
31
- ).toBe('2fr 120px minmax(11rem, max-content)');
31
+ ).toBe('2fr 120px minmax(11rem, 1fr)');
32
32
  });
33
33
 
34
34
  it('resolves benchmark iterations with a minimum of one', () => {
@@ -43,9 +43,9 @@ describe('gridStateMath', () => {
43
43
  });
44
44
 
45
45
  it('computes viewport height and viewport rows', () => {
46
- expect(computeViewportHeightPx(undefined, undefined)).toBe('560px');
46
+ expect(computeViewportHeightPx(undefined, undefined)).toBe('440px');
47
47
  expect(computeViewportHeightPx(620, 480)).toBe('620px');
48
- expect(computeViewportRows(undefined, undefined)).toBe(13);
48
+ expect(computeViewportRows(undefined, undefined)).toBe(10);
49
49
  expect(computeViewportRows(220, 44)).toBe(5);
50
50
  });
51
51
  });
@@ -34,12 +34,15 @@ export function formatPaginationSummary(
34
34
  }
35
35
 
36
36
  export function computeViewportHeightPx(
37
- viewportHeight?: number,
38
37
  autoViewportHeight?: number | null,
38
+ minRowsToShow?: number,
39
+ rowHeight?: number,
39
40
  ): string {
40
- return `${viewportHeight ?? autoViewportHeight ?? 560}px`;
41
+ const fallback = (minRowsToShow ?? 10) * (rowHeight ?? 44);
42
+ return `${autoViewportHeight ?? fallback}px`;
41
43
  }
42
44
 
43
- export function computeViewportRows(viewportHeight?: number, rowHeight?: number): number {
44
- return Math.max(1, Math.ceil((viewportHeight ?? 560) / (rowHeight ?? 44)));
45
+ export function computeViewportRows(autoViewportHeight?: number | null, rowHeight?: number, minRowsToShow?: number): number {
46
+ const height = autoViewportHeight ?? (minRowsToShow ?? 10) * (rowHeight ?? 44);
47
+ return Math.max(1, Math.ceil(height / (rowHeight ?? 44)));
45
48
  }
package/src/index.ts CHANGED
@@ -1,27 +1,11 @@
1
1
  export { UiGrid } from './UiGrid';
2
- export type { UiGridProps } from './UiGrid';
3
- export { mountUiGrid, updateUiGrid, styledCell } from './mountUiGrid';
2
+ export type { UiGridProps, UiGridCellRenderers } from './UiGrid';
3
+ export { mountUiGrid, updateUiGrid, styledCell, datePickerCell } from './mountUiGrid';
4
4
  export {
5
5
  mountUiGridCustomElement,
6
6
  type MountUiGridCustomElementOptions,
7
7
  type MountedUiGridCustomElement,
8
8
  } from './vanillaAdapter';
9
- export { useGridState } from './useGridState';
10
- export type { UseGridStateResult } from './useGridState';
11
- export { useVirtualScroll } from './useVirtualScroll';
12
- export type { UseVirtualScrollOptions, UseVirtualScrollResult } from './useVirtualScroll';
13
- export {
14
- orderVisibleColumns,
15
- buildGridTemplateColumns,
16
- resolveBenchmarkIterations,
17
- formatPaginationSummary,
18
- computeViewportHeightPx,
19
- computeViewportRows,
20
- } from './gridStateMath';
21
- export {
22
- enableReactUiGridWasmEngine,
23
- registerReactUiGridWasmEngineFromModule,
24
- } from './rustWasmGridEngine';
25
9
 
26
10
  export type {
27
11
  GridOptions,
@@ -25,4 +25,30 @@ export function styledCell(
25
25
  { style: { color, fontVariantNumeric: 'tabular-nums', ...extraStyle } },
26
26
  text,
27
27
  );
28
+ }
29
+
30
+ /** Create a date <input> React node — usable from non-TSX contexts. */
31
+ export function datePickerCell(
32
+ value: string,
33
+ onChange?: (newValue: string) => void,
34
+ extraStyle?: React.CSSProperties,
35
+ ): React.ReactNode {
36
+ return React.createElement('input', {
37
+ type: 'date',
38
+ value: value || '',
39
+ onChange: onChange
40
+ ? (e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)
41
+ : undefined,
42
+ style: {
43
+ font: 'inherit',
44
+ fontSize: '0.85rem',
45
+ border: '1px solid color-mix(in srgb, currentColor 20%, transparent)',
46
+ borderRadius: '6px',
47
+ padding: '0.2rem 0.4rem',
48
+ background: 'var(--ui-grid-surface, white)',
49
+ color: 'inherit',
50
+ cursor: 'pointer',
51
+ ...extraStyle,
52
+ },
53
+ });
28
54
  }
@@ -0,0 +1,13 @@
1
+ if (typeof ShadowRoot !== 'undefined') {
2
+ const proto = ShadowRoot.prototype as unknown as Record<string, unknown>;
3
+ if (!('adoptedStyleSheets' in proto)) {
4
+ Object.defineProperty(proto, 'adoptedStyleSheets', {
5
+ get() {
6
+ return (this as unknown as Record<string, unknown>)['_adoptedStyleSheets'] ?? [];
7
+ },
8
+ set(sheets: CSSStyleSheet[]) {
9
+ (this as unknown as Record<string, unknown>)['_adoptedStyleSheets'] = sheets;
10
+ },
11
+ });
12
+ }
13
+ }
package/src/ui-grid.css CHANGED
@@ -279,37 +279,70 @@
279
279
  color: var(--ui-grid-muted-color);
280
280
  }
281
281
 
282
+ /*
283
+ * Grid table layout. Header + filter rows live in their own
284
+ * non-user-scrollable strips (real scroll containers so `position: sticky`
285
+ * on pinned cells anchors correctly); the body has its own viewport with
286
+ * visible scrollbars. Horizontal scroll on the body is mirrored onto the
287
+ * strips imperatively via React's onScroll/onWheel handlers so the columns
288
+ * always stay aligned.
289
+ */
282
290
  .grid-table {
283
- display: grid;
291
+ position: relative;
292
+ display: flex;
293
+ flex-direction: column;
284
294
  min-height: 0;
285
295
  width: 100%;
286
296
  min-width: 0;
287
- overflow-x: auto;
288
- overflow-y: hidden;
297
+ overflow-anchor: none;
289
298
  }
290
299
 
291
- .header-grid,
292
- .filter-grid,
293
- .body-grid {
294
- display: grid;
295
- width: max-content;
296
- min-width: 100%;
297
- }
298
-
299
- .header-grid,
300
- .filter-grid {
300
+ .grid-header-strip,
301
+ .grid-filter-strip {
302
+ flex: 0 0 auto;
301
303
  position: sticky;
302
304
  z-index: 3;
305
+ background: var(--ui-grid-surface);
306
+ /* Real scroll container so pinned sticky cells anchor to something,
307
+ * but we don't want the user scrolling it directly. JS drives the
308
+ * scrollLeft, the wheel handler forwards gestures to the body, and
309
+ * the scrollbar is hidden. */
310
+ overflow-x: scroll;
311
+ overflow-y: hidden;
312
+ scrollbar-width: none;
313
+ touch-action: pan-y;
314
+ overscroll-behavior-x: none;
315
+ }
316
+
317
+ .grid-header-strip::-webkit-scrollbar,
318
+ .grid-filter-strip::-webkit-scrollbar {
319
+ display: none;
303
320
  }
304
321
 
305
- .header-grid {
322
+ .grid-header-strip {
306
323
  top: 0;
307
324
  }
308
325
 
309
- .filter-grid {
326
+ .grid-filter-strip {
310
327
  top: var(--ui-grid-header-sticky-top, 0px);
311
328
  }
312
329
 
330
+ .grid-body-viewport {
331
+ flex: 1 1 auto;
332
+ min-height: 0;
333
+ overflow-x: auto;
334
+ overflow-anchor: none;
335
+ overscroll-behavior-x: none;
336
+ }
337
+
338
+ .header-grid,
339
+ .filter-grid,
340
+ .body-grid {
341
+ display: grid;
342
+ width: max-content;
343
+ min-width: 100%;
344
+ }
345
+
313
346
  .header-cell,
314
347
  .filter-cell,
315
348
  .body-cell,
@@ -1183,9 +1183,9 @@ export function useGridState(
1183
1183
 
1184
1184
  // --- Auto resize effect ---
1185
1185
 
1186
- // Auto-resize is on by default so the grid fills its container. Setting an
1187
- // explicit `viewportHeight` opts back into fixed sizing because the observer
1188
- // only writes `autoViewportHeight` when `viewportHeight` is unset.
1186
+ // Auto-resize is on by default so the grid fills its container. The
1187
+ // ResizeObserver measures the host element and writes `autoViewportHeight`
1188
+ // so the grid always fills available space (matching the old ui-grid).
1189
1189
  useEffect(() => {
1190
1190
  if (!FEATURE_AUTO_RESIZE) return;
1191
1191
 
@@ -1206,14 +1206,14 @@ export function useGridState(
1206
1206
  lastGridHeightRef.current = nextHeight;
1207
1207
  lastGridWidthRef.current = nextWidth;
1208
1208
 
1209
- if (!options.viewportHeight && nextHeight > 0) {
1209
+ if (nextHeight > 0) {
1210
1210
  setAutoViewportHeight(nextHeight);
1211
1211
  }
1212
1212
  });
1213
1213
 
1214
1214
  if (!observer) return;
1215
1215
  return () => observer.disconnect();
1216
- }, [options.enableAutoResize, options.viewportHeight, gridApi]);
1216
+ }, [options.enableAutoResize, gridApi]);
1217
1217
 
1218
1218
  // --- Computed values ---
1219
1219
 
@@ -1225,7 +1225,7 @@ export function useGridState(
1225
1225
  const paginationCurrentPage = getCurrentPageValueFn();
1226
1226
  const paginationTotalPages = getTotalPagesValueFn();
1227
1227
  const paginationSelectedPageSize = effectivePageSizeFn(pipeline.totalItems);
1228
- const viewportHeightPx = computeViewportHeightPx(options.viewportHeight, autoViewportHeight);
1228
+ const viewportHeightPx = computeViewportHeightPx(autoViewportHeight, options.minRowsToShow, options.rowHeight);
1229
1229
 
1230
1230
  // --- Display helper functions ---
1231
1231
 
@@ -1770,8 +1770,9 @@ export function useGridState(
1770
1770
  startIndex,
1771
1771
  visibleRows: pipelineRef.current.visibleRows.length,
1772
1772
  viewportRows: computeViewportRows(
1773
- optionsRef.current.viewportHeight,
1773
+ autoViewportHeight,
1774
1774
  optionsRef.current.rowHeight,
1775
+ optionsRef.current.minRowsToShow,
1775
1776
  ),
1776
1777
  threshold: optionsRef.current.infiniteScrollRowsFromEnd ?? 20,
1777
1778
  setState: (state) => setInfiniteScrollState(state),
package/tsconfig.dts.json CHANGED
@@ -7,9 +7,10 @@
7
7
  "outDir": "./dist",
8
8
  "rootDir": "./src",
9
9
  "paths": {
10
- "@ornery/ui-grid-core": ["../ui-grid-core/dist/index.d.ts"]
10
+ "@ornery/ui-grid-core": ["../ui-grid-core/dist/index.d.ts"],
11
+ "@ornery/ui-grid-vanilla": ["../ui-grid-vanilla/dist/index.d.ts"]
11
12
  }
12
13
  },
13
14
  "include": ["src/**/*.ts", "src/**/*.tsx"],
14
- "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"]
15
+ "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/test-setup.ts"]
15
16
  }
package/tsconfig.json CHANGED
@@ -7,7 +7,8 @@
7
7
  "declarationMap": true,
8
8
  "baseUrl": ".",
9
9
  "paths": {
10
- "@ornery/ui-grid-core": ["../ui-grid-core/src/index.ts"]
10
+ "@ornery/ui-grid-core": ["../ui-grid-core/src/index.ts"],
11
+ "@ornery/ui-grid-vanilla": ["../ui-grid-vanilla/src/index.ts"]
11
12
  }
12
13
  },
13
14
  "include": ["src"]
package/vitest.config.ts CHANGED
@@ -5,10 +5,12 @@ export default defineConfig({
5
5
  resolve: {
6
6
  alias: {
7
7
  '@ornery/ui-grid-core': path.resolve(__dirname, '../ui-grid-core/src/index.ts'),
8
+ '@ornery/ui-grid-vanilla': path.resolve(__dirname, '../ui-grid-vanilla/src/index.ts'),
8
9
  },
9
10
  },
10
11
  test: {
11
12
  environment: 'jsdom',
12
13
  globals: true,
14
+ setupFiles: ['./src/test-setup.ts'],
13
15
  },
14
16
  });