@ornery/ui-grid-react 0.1.5 → 0.1.7

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 (41) hide show
  1. package/demo/main.tsx +1 -1
  2. package/demo/vite.config.ts +1 -1
  3. package/dist/UiGrid.d.ts +11 -0
  4. package/dist/UiGrid.d.ts.map +1 -0
  5. package/dist/gridStateMath.d.ts +8 -0
  6. package/dist/gridStateMath.d.ts.map +1 -0
  7. package/dist/index.d.ts +14 -155
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +699 -1063
  10. package/dist/index.mjs +594 -930
  11. package/dist/mountUiGrid.d.ts +4 -0
  12. package/dist/mountUiGrid.d.ts.map +1 -0
  13. package/dist/rustWasmGridEngine.d.ts +8 -0
  14. package/dist/rustWasmGridEngine.d.ts.map +1 -0
  15. package/dist/{index.d.mts → useGridState.d.ts} +6 -50
  16. package/dist/useGridState.d.ts.map +1 -0
  17. package/dist/useVirtualScroll.d.ts +20 -0
  18. package/dist/useVirtualScroll.d.ts.map +1 -0
  19. package/dist/vanillaAdapter.d.ts +14 -0
  20. package/dist/vanillaAdapter.d.ts.map +1 -0
  21. package/dist/virtualScrollMath.d.ts +17 -0
  22. package/dist/virtualScrollMath.d.ts.map +1 -0
  23. package/package.json +11 -11
  24. package/src/UiGrid.test.tsx +63 -25
  25. package/src/UiGrid.tsx +438 -187
  26. package/src/gridStateMath.test.ts +10 -8
  27. package/src/gridStateMath.ts +20 -7
  28. package/src/index.ts +21 -5
  29. package/src/mountUiGrid.tsx +10 -0
  30. package/src/rustWasmGridEngine.test.ts +4 -4
  31. package/src/rustWasmGridEngine.ts +7 -5
  32. package/src/ui-grid.css +200 -1
  33. package/src/useGridState.ts +56 -30
  34. package/src/useVirtualScroll.ts +2 -0
  35. package/src/vanillaAdapter.test.ts +33 -0
  36. package/src/vanillaAdapter.ts +36 -0
  37. package/tsconfig.build.json +6 -0
  38. package/tsconfig.dts.json +15 -0
  39. package/tsconfig.json +1 -1
  40. package/vitest.config.ts +1 -1
  41. package/CLAUDE.md +0 -283
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import type { GridColumnDef } from '@ornery/ui-grid';
2
+ import type { GridColumnDef } from '@ornery/ui-grid-core';
3
3
  import {
4
4
  buildGridTemplateColumns,
5
5
  computeViewportHeightPx,
@@ -18,15 +18,17 @@ describe('gridStateMath', () => {
18
18
  ];
19
19
 
20
20
  it('orders only visible columns by the provided column order', () => {
21
- expect(orderVisibleColumns(columns, ['customer', 'revenue', 'status', 'owner']).map((column) => column.name)).toEqual([
22
- 'customer',
23
- 'revenue',
24
- 'status',
25
- ]);
21
+ expect(
22
+ orderVisibleColumns(columns, ['customer', 'revenue', 'status', 'owner']).map(
23
+ (column) => column.name,
24
+ ),
25
+ ).toEqual(['customer', 'revenue', 'status']);
26
26
  });
27
27
 
28
28
  it('builds grid template columns deterministically', () => {
29
- expect(buildGridTemplateColumns(orderVisibleColumns(columns, ['status', 'revenue', 'customer']))).toBe('2fr 120px minmax(11rem, max-content)');
29
+ expect(
30
+ buildGridTemplateColumns(orderVisibleColumns(columns, ['status', 'revenue', 'customer'])),
31
+ ).toBe('2fr 120px minmax(11rem, max-content)');
30
32
  });
31
33
 
32
34
  it('resolves benchmark iterations with a minimum of one', () => {
@@ -46,4 +48,4 @@ describe('gridStateMath', () => {
46
48
  expect(computeViewportRows(undefined, undefined)).toBe(13);
47
49
  expect(computeViewportRows(220, 44)).toBe(5);
48
50
  });
49
- });
51
+ });
@@ -1,7 +1,10 @@
1
- import type { GridColumnDef } from '@ornery/ui-grid';
2
- import { gridColumnWidth } from '@ornery/ui-grid';
1
+ import type { GridColumnDef } from '@ornery/ui-grid-core';
2
+ import { gridColumnWidth } from '@ornery/ui-grid-core';
3
3
 
4
- export function orderVisibleColumns(columns: readonly GridColumnDef[], order: readonly string[]): GridColumnDef[] {
4
+ export function orderVisibleColumns(
5
+ columns: readonly GridColumnDef[],
6
+ order: readonly string[],
7
+ ): GridColumnDef[] {
5
8
  return [...columns]
6
9
  .filter((column) => column.visible !== false)
7
10
  .sort((left, right) => order.indexOf(left.name) - order.indexOf(right.name));
@@ -11,11 +14,18 @@ export function buildGridTemplateColumns(columns: readonly GridColumnDef[]): str
11
14
  return columns.map((column) => gridColumnWidth(column)).join(' ');
12
15
  }
13
16
 
14
- export function resolveBenchmarkIterations(iterations?: number, configuredIterations?: number): number {
17
+ export function resolveBenchmarkIterations(
18
+ iterations?: number,
19
+ configuredIterations?: number,
20
+ ): number {
15
21
  return Math.max(1, iterations ?? configuredIterations ?? 25);
16
22
  }
17
23
 
18
- export function formatPaginationSummary(totalItems: number, firstRowIndex: number, lastRowIndex: number): string {
24
+ export function formatPaginationSummary(
25
+ totalItems: number,
26
+ firstRowIndex: number,
27
+ lastRowIndex: number,
28
+ ): string {
19
29
  if (totalItems === 0) {
20
30
  return '0-0 of 0';
21
31
  }
@@ -23,10 +33,13 @@ export function formatPaginationSummary(totalItems: number, firstRowIndex: numbe
23
33
  return `${firstRowIndex + 1}-${lastRowIndex + 1} of ${totalItems}`;
24
34
  }
25
35
 
26
- export function computeViewportHeightPx(viewportHeight?: number, autoViewportHeight?: number | null): string {
36
+ export function computeViewportHeightPx(
37
+ viewportHeight?: number,
38
+ autoViewportHeight?: number | null,
39
+ ): string {
27
40
  return `${viewportHeight ?? autoViewportHeight ?? 560}px`;
28
41
  }
29
42
 
30
43
  export function computeViewportRows(viewportHeight?: number, rowHeight?: number): number {
31
44
  return Math.max(1, Math.ceil((viewportHeight ?? 560) / (rowHeight ?? 44)));
32
- }
45
+ }
package/src/index.ts CHANGED
@@ -1,11 +1,27 @@
1
1
  export { UiGrid } from './UiGrid';
2
2
  export type { UiGridProps } from './UiGrid';
3
+ export { mountUiGrid } from './mountUiGrid';
4
+ export {
5
+ mountUiGridCustomElement,
6
+ type MountUiGridCustomElementOptions,
7
+ type MountedUiGridCustomElement,
8
+ } from './vanillaAdapter';
3
9
  export { useGridState } from './useGridState';
4
10
  export type { UseGridStateResult } from './useGridState';
5
11
  export { useVirtualScroll } from './useVirtualScroll';
6
12
  export type { UseVirtualScrollOptions, UseVirtualScrollResult } from './useVirtualScroll';
7
- export { orderVisibleColumns, buildGridTemplateColumns, resolveBenchmarkIterations, formatPaginationSummary, computeViewportHeightPx, computeViewportRows } from './gridStateMath';
8
- export { enableReactUiGridWasmEngine, registerReactUiGridWasmEngineFromModule } from './rustWasmGridEngine';
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';
9
25
 
10
26
  export type {
11
27
  GridOptions,
@@ -19,7 +35,7 @@ export type {
19
35
  GridBenchmarkResult,
20
36
  GridSavedState,
21
37
  SortState,
22
- } from '@ornery/ui-grid';
38
+ } from '@ornery/ui-grid-core';
23
39
 
24
- export type { UiGridApi } from '@ornery/ui-grid';
25
- export { DEFAULT_GRID_LABELS } from '@ornery/ui-grid';
40
+ export type { UiGridApi } from '@ornery/ui-grid-core';
41
+ export { DEFAULT_GRID_LABELS } from '@ornery/ui-grid-core';
@@ -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
+ }
@@ -3,10 +3,10 @@ import {
3
3
  activeGridEngineBackend,
4
4
  clearRustWasmGridEngine,
5
5
  defaultGridEngine,
6
- } from '@ornery/ui-grid';
6
+ } from '@ornery/ui-grid-core';
7
7
  import { registerReactUiGridWasmEngineFromModule } from './rustWasmGridEngine';
8
- import { SORT_DIRECTIONS } from '@ornery/ui-grid';
9
- import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid';
8
+ import { SORT_DIRECTIONS } from '@ornery/ui-grid-core';
9
+ import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid-core';
10
10
 
11
11
  function createContext(): BuildGridPipelineContext {
12
12
  return {
@@ -53,4 +53,4 @@ describe('rustWasmGridEngine', () => {
53
53
  expect(defaultGridEngine.buildPipeline(createContext())).toBe(sentinel);
54
54
  expect(activeGridEngineBackend()).toBe('rust-wasm');
55
55
  });
56
- });
56
+ });
@@ -1,7 +1,8 @@
1
- import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid';
2
- import { registerRustWasmGridEngine } from '@ornery/ui-grid';
1
+ import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid-core';
2
+ import { registerRustWasmGridEngine } from '@ornery/ui-grid-core';
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;
@@ -11,11 +12,12 @@ export function registerReactUiGridWasmEngineFromModule(module: UiGridWasmModule
11
12
  registerRustWasmGridEngine({
12
13
  buildPipeline(context: BuildGridPipelineContext): PipelineResult {
13
14
  return module.build_pipeline_js(context);
14
- }
15
+ },
15
16
  });
16
17
  }
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
+ }
package/src/ui-grid.css CHANGED
@@ -20,6 +20,26 @@
20
20
  --ui-grid-status-enterprise-color: var(--app-ui-grid-status-enterprise-color, #115e59);
21
21
  --ui-grid-status-pilot-bg: var(--app-ui-grid-status-pilot-bg, rgba(234, 88, 12, 0.14));
22
22
  --ui-grid-status-pilot-color: var(--app-ui-grid-status-pilot-color, #c2410c);
23
+ --ui-grid-pin-menu-open-z-index: var(--app-ui-grid-pin-menu-open-z-index, 8);
24
+ --ui-grid-pin-menu-z-index: var(--app-ui-grid-pin-menu-z-index, 20);
25
+ --ui-grid-pin-menu-gap: var(--app-ui-grid-pin-menu-gap, 0.25rem);
26
+ --ui-grid-pin-menu-padding: var(--app-ui-grid-pin-menu-padding, 0.25rem);
27
+ --ui-grid-pin-menu-radius: var(--app-ui-grid-pin-menu-radius, 999px);
28
+ --ui-grid-pin-menu-shadow: var(
29
+ --app-ui-grid-pin-menu-shadow,
30
+ 0 10px 24px color-mix(in srgb, var(--ui-grid-cell-color) 10%, transparent)
31
+ );
32
+ --ui-grid-pin-menu-action-size: var(--app-ui-grid-pin-menu-action-size, 1.75rem);
33
+ --ui-grid-pin-control-collapsed-size: var(--app-ui-grid-pin-control-collapsed-size, 1px);
34
+ --ui-grid-pin-control-transition-duration: var(
35
+ --app-ui-grid-pin-control-transition-duration,
36
+ 160ms
37
+ );
38
+ --ui-grid-pin-control-transition-easing: var(
39
+ --app-ui-grid-pin-control-transition-easing,
40
+ cubic-bezier(0.22, 1, 0.36, 1)
41
+ );
42
+ --ui-grid-pin-menu-scale-closed: var(--app-ui-grid-pin-menu-scale-closed, 0.72);
23
43
  display: block;
24
44
  color: var(--ui-grid-cell-color);
25
45
  }
@@ -39,12 +59,18 @@
39
59
  .grid-hero {
40
60
  display: flex;
41
61
  justify-content: space-between;
62
+ flex-wrap: wrap;
42
63
  gap: 1.5rem;
43
- align-items: end;
64
+ align-items: flex-start;
44
65
  padding: 1rem 0;
45
66
  color: var(--ui-grid-cell-color);
46
67
  }
47
68
 
69
+ .grid-hero > :first-child {
70
+ flex: 1 1 28rem;
71
+ min-width: min(100%, 20rem);
72
+ }
73
+
48
74
  .ui-grid-host .eyebrow {
49
75
  margin: 0 0 0.5rem;
50
76
  text-transform: uppercase;
@@ -70,6 +96,9 @@
70
96
  gap: 1rem;
71
97
  align-items: center;
72
98
  flex-wrap: wrap;
99
+ flex: 0 1 auto;
100
+ margin-left: auto;
101
+ justify-content: flex-end;
73
102
  }
74
103
 
75
104
  .action {
@@ -168,6 +197,22 @@
168
197
  .filter-grid,
169
198
  .body-grid {
170
199
  display: grid;
200
+ width: max-content;
201
+ min-width: 100%;
202
+ }
203
+
204
+ .header-grid,
205
+ .filter-grid {
206
+ position: sticky;
207
+ z-index: 3;
208
+ }
209
+
210
+ .header-grid {
211
+ top: 0;
212
+ }
213
+
214
+ .filter-grid {
215
+ top: var(--ui-grid-header-sticky-top, 0px);
171
216
  }
172
217
 
173
218
  .header-cell,
@@ -188,12 +233,36 @@
188
233
  align-items: center;
189
234
  }
190
235
 
236
+ .header-cell[draggable='true'] {
237
+ cursor: grab;
238
+ }
239
+
240
+ .header-cell.is-dragging {
241
+ opacity: 0.6;
242
+ }
243
+
244
+ .header-cell.is-drag-target {
245
+ background: color-mix(in srgb, var(--ui-grid-accent) 9%, var(--ui-grid-header-background));
246
+ border: 1px dashed color-mix(in srgb, var(--ui-grid-accent) 45%, var(--ui-grid-border-color));
247
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-grid-accent) 18%, transparent);
248
+ }
249
+
191
250
  .header-cell.is-active {
192
251
  background: color-mix(in srgb, var(--ui-grid-accent) 8%, var(--ui-grid-header-background));
193
252
  }
194
253
 
254
+ .header-cell.is-pin-menu-open {
255
+ z-index: var(--ui-grid-pin-menu-open-z-index);
256
+ }
257
+
195
258
  .header-label {
196
259
  min-width: 0;
260
+ display: block;
261
+ line-height: 1.25;
262
+ white-space: nowrap;
263
+ overflow: hidden;
264
+ text-overflow: clip;
265
+ hyphens: none;
197
266
  }
198
267
 
199
268
  .header-actions {
@@ -201,6 +270,8 @@
201
270
  align-items: center;
202
271
  gap: 0.4rem;
203
272
  justify-self: end;
273
+ flex-wrap: nowrap;
274
+ flex-shrink: 0;
204
275
  }
205
276
 
206
277
  .header-action,
@@ -250,14 +321,131 @@
250
321
  color: var(--ui-grid-accent);
251
322
  }
252
323
 
324
+ .pin-control {
325
+ position: relative;
326
+ display: inline-grid;
327
+ place-items: center;
328
+ justify-items: center;
329
+ min-width: 2rem;
330
+ overflow: hidden;
331
+ }
332
+
333
+ .pin-trigger {
334
+ grid-area: 1 / 1;
335
+ transform-origin: center;
336
+ transition:
337
+ width var(--ui-grid-pin-control-transition-duration)
338
+ var(--ui-grid-pin-control-transition-easing),
339
+ height var(--ui-grid-pin-control-transition-duration)
340
+ var(--ui-grid-pin-control-transition-easing),
341
+ opacity var(--ui-grid-pin-control-transition-duration)
342
+ var(--ui-grid-pin-control-transition-easing),
343
+ transform var(--ui-grid-pin-control-transition-duration)
344
+ var(--ui-grid-pin-control-transition-easing),
345
+ border-color var(--ui-grid-pin-control-transition-duration)
346
+ var(--ui-grid-pin-control-transition-easing),
347
+ background-color var(--ui-grid-pin-control-transition-duration)
348
+ var(--ui-grid-pin-control-transition-easing),
349
+ color var(--ui-grid-pin-control-transition-duration)
350
+ var(--ui-grid-pin-control-transition-easing);
351
+ }
352
+
353
+ .pin-menu {
354
+ grid-area: 1 / 1;
355
+ z-index: var(--ui-grid-pin-menu-z-index);
356
+ display: grid;
357
+ grid-auto-rows: var(--ui-grid-pin-menu-action-size);
358
+ gap: var(--ui-grid-pin-menu-gap);
359
+ padding: var(--ui-grid-pin-menu-padding);
360
+ border: 1px solid var(--ui-grid-border-color);
361
+ border-radius: var(--ui-grid-pin-menu-radius);
362
+ background: var(--ui-grid-surface);
363
+ box-shadow: var(--ui-grid-pin-menu-shadow);
364
+ justify-items: center;
365
+ align-items: center;
366
+ width: 2rem;
367
+ max-height: var(--ui-grid-pin-control-collapsed-size);
368
+ opacity: 0;
369
+ pointer-events: none;
370
+ transform: scale(var(--ui-grid-pin-menu-scale-closed));
371
+ transform-origin: center;
372
+ transition:
373
+ max-height var(--ui-grid-pin-control-transition-duration)
374
+ var(--ui-grid-pin-control-transition-easing),
375
+ opacity var(--ui-grid-pin-control-transition-duration)
376
+ var(--ui-grid-pin-control-transition-easing),
377
+ transform var(--ui-grid-pin-control-transition-duration)
378
+ var(--ui-grid-pin-control-transition-easing),
379
+ padding var(--ui-grid-pin-control-transition-duration)
380
+ var(--ui-grid-pin-control-transition-easing),
381
+ border-color var(--ui-grid-pin-control-transition-duration)
382
+ var(--ui-grid-pin-control-transition-easing),
383
+ box-shadow var(--ui-grid-pin-control-transition-duration)
384
+ var(--ui-grid-pin-control-transition-easing);
385
+ }
386
+
387
+ .pin-control-open {
388
+ align-items: start;
389
+ }
390
+
391
+ .pin-control-open .pin-trigger {
392
+ width: var(--ui-grid-pin-control-collapsed-size);
393
+ height: var(--ui-grid-pin-control-collapsed-size);
394
+ opacity: 0;
395
+ transform: scale(0.72);
396
+ pointer-events: none;
397
+ }
398
+
399
+ .pin-control-open .pin-menu {
400
+ max-height: calc(
401
+ (var(--ui-grid-pin-menu-action-size) * 2) + var(--ui-grid-pin-menu-gap) +
402
+ (var(--ui-grid-pin-menu-padding) * 2)
403
+ );
404
+ opacity: 1;
405
+ pointer-events: auto;
406
+ transform: scale(1);
407
+ }
408
+
409
+ .pin-menu-action {
410
+ display: inline-flex;
411
+ align-items: center;
412
+ justify-content: center;
413
+ width: var(--ui-grid-pin-menu-action-size);
414
+ height: var(--ui-grid-pin-menu-action-size);
415
+ border: 0;
416
+ border-radius: 999px;
417
+ background: transparent;
418
+ color: var(--ui-grid-cell-color);
419
+ cursor: pointer;
420
+ }
421
+
422
+ .pin-menu-action:hover,
423
+ .pin-menu-action:focus-visible {
424
+ outline: 0;
425
+ background: color-mix(in srgb, var(--ui-grid-accent) 12%, var(--ui-grid-surface));
426
+ color: var(--ui-grid-accent);
427
+ }
428
+
429
+ .pin-menu-action svg {
430
+ width: 1rem;
431
+ height: 1rem;
432
+ fill: currentColor;
433
+ }
434
+
253
435
  .filter-cell {
254
436
  padding: 0.75rem 1rem 1rem;
255
437
  border-bottom: 1px solid var(--ui-grid-border-color);
256
438
  background: var(--ui-grid-header-background);
439
+ overflow: hidden;
257
440
  }
258
441
 
259
442
  .filter-cell input {
443
+ display: block;
260
444
  width: 100%;
445
+ min-width: 0;
446
+ max-width: 100%;
447
+ min-inline-size: 0;
448
+ max-inline-size: 100%;
261
449
  border: 1px solid var(--ui-grid-border-color);
262
450
  border-radius: var(--ui-grid-radius);
263
451
  background: var(--ui-grid-surface);
@@ -390,6 +578,17 @@
390
578
  width: 100%;
391
579
  }
392
580
 
581
+ .grid-virtual-spacer {
582
+ position: relative;
583
+ width: max-content;
584
+ min-width: 100%;
585
+ }
586
+
587
+ .grid-virtual-body {
588
+ position: absolute;
589
+ left: 0;
590
+ }
591
+
393
592
  .body-cell.align-center {
394
593
  text-align: center;
395
594
  }
@@ -88,19 +88,10 @@ import {
88
88
  FEATURE_AUTO_RESIZE,
89
89
  FEATURE_SAVE_STATE,
90
90
  FEATURE_PINNING,
91
- } from '@ornery/ui-grid';
92
- import type {
93
- DisplayItem,
94
- GroupItem,
95
- ExpandableItem,
96
- RowItem,
97
- PipelineResult,
98
- GridInfiniteScrollState,
99
- GridMoveDirection,
100
- GridCellTemplateContext,
101
- GridExpandableTemplateContext,
102
- } from '@ornery/ui-grid';
103
- import {
91
+ buildInitialPinnedState,
92
+ computePinnedOffset,
93
+ isColumnPinnable,
94
+ isPinningEnabled,
104
95
  applyGridSortStateCommand,
105
96
  updateGridFilterCommand,
106
97
  clearGridFiltersCommand,
@@ -127,16 +118,6 @@ import {
127
118
  setGridInfiniteScrollDirectionsCommand,
128
119
  restoreGridStateCommand,
129
120
  pinGridColumnCommand,
130
- } from '../../ui-grid/src/lib/grid/ui-grid.commands';
131
- import {
132
- buildInitialPinnedState,
133
- computePinnedOffset,
134
- isColumnPinnable,
135
- isPinningEnabled,
136
- PinDirection,
137
- PinnedColumnState,
138
- } from '../../ui-grid/src/lib/grid/grid.core';
139
- import {
140
121
  raiseGridRenderingComplete,
141
122
  raiseGridRowsRendered,
142
123
  raiseGridRowsVisibleChanged,
@@ -145,8 +126,22 @@ import {
145
126
  raiseGridScrollBegin,
146
127
  raiseGridScrollEnd,
147
128
  raiseGridBenchmarkComplete,
148
- } from '../../ui-grid/src/lib/grid/ui-grid.events';
149
- import { downloadGridCsvFile, observeGridHostSize } from '../../ui-grid/src/lib/grid/ui-grid.host';
129
+ downloadGridCsvFile,
130
+ observeGridHostSize,
131
+ } from '@ornery/ui-grid-core';
132
+ import type {
133
+ DisplayItem,
134
+ GroupItem,
135
+ ExpandableItem,
136
+ RowItem,
137
+ PipelineResult,
138
+ GridInfiniteScrollState,
139
+ GridMoveDirection,
140
+ GridCellTemplateContext,
141
+ GridExpandableTemplateContext,
142
+ PinDirection,
143
+ PinnedColumnState,
144
+ } from '@ornery/ui-grid-core';
150
145
 
151
146
  function escapeCssSelectorValue(value: string): string {
152
147
  const nativeEscape = globalThis.CSS?.escape;
@@ -167,9 +162,7 @@ function escapeCssSelectorValue(value: string): string {
167
162
  const isControlCharacter = (codePoint >= 0x0001 && codePoint <= 0x001f) || codePoint === 0x007f;
168
163
  const startsWithDigit = index === 0 && codePoint >= 0x0030 && codePoint <= 0x0039;
169
164
  const secondCharDigitAfterHyphen =
170
- index === 1 &&
171
- codePoint >= 0x0030 && codePoint <= 0x0039 &&
172
- value.charCodeAt(0) === 0x002d;
165
+ index === 1 && codePoint >= 0x0030 && codePoint <= 0x0039 && value.charCodeAt(0) === 0x002d;
173
166
 
174
167
  if (isControlCharacter || startsWithDigit || secondCharDigitAfterHyphen) {
175
168
  output += `\\${codePoint.toString(16)} `;
@@ -313,6 +306,7 @@ export interface UseGridStateResult {
313
306
  toggleRowExpansion: (row: GridRow, event?: React.MouseEvent) => void;
314
307
  toggleTreeRow: (row: GridRow, event?: React.MouseEvent) => void;
315
308
  moveColumn: (fromIndex: number, toIndex: number) => void;
309
+ moveVisibleColumn: (columnName: string, targetColumnName: string) => void;
316
310
  nextPage: () => void;
317
311
  previousPage: () => void;
318
312
  onPageSizeChange: (value: string) => void;
@@ -398,8 +392,27 @@ export function useGridState(
398
392
  const rowSize = options.rowHeight ?? 44;
399
393
 
400
394
  const visibleColumns = useMemo(() => {
401
- return orderVisibleColumns(options.columnDefs, columnOrder);
402
- }, [options.columnDefs, columnOrder]);
395
+ const orderedColumns = orderVisibleColumns(options.columnDefs, columnOrder);
396
+ const pinnedEntries = Object.entries(pinnedColumns);
397
+ if (pinnedEntries.length === 0) {
398
+ return orderedColumns;
399
+ }
400
+
401
+ const columnByName = new Map(orderedColumns.map((column) => [column.name, column]));
402
+ const pinnedLeft = pinnedEntries
403
+ .filter(([, direction]) => direction === 'left')
404
+ .map(([columnName]) => columnByName.get(columnName))
405
+ .filter((column): column is GridColumnDef => column !== undefined);
406
+ const pinnedRight = pinnedEntries
407
+ .filter(([, direction]) => direction === 'right')
408
+ .map(([columnName]) => columnByName.get(columnName))
409
+ .filter((column): column is GridColumnDef => column !== undefined);
410
+ const centerColumns = orderedColumns.filter(
411
+ (column) => pinnedColumns[column.name] === undefined,
412
+ );
413
+
414
+ return [...pinnedLeft, ...centerColumns, ...pinnedRight];
415
+ }, [options.columnDefs, columnOrder, pinnedColumns]);
403
416
 
404
417
  const visibleColumnsRef = useRef(visibleColumns);
405
418
  visibleColumnsRef.current = visibleColumns;
@@ -1559,6 +1572,18 @@ export function useGridState(
1559
1572
  );
1560
1573
  }, []);
1561
1574
 
1575
+ const moveVisibleColumnFn = useCallback((columnName: string, targetColumnName: string): void => {
1576
+ moveGridVisibleColumnCommand(
1577
+ gridApiRef.current!,
1578
+ FEATURE_COLUMN_MOVING && optionsRef.current.enableColumnMoving === true,
1579
+ columnOrderRef.current,
1580
+ visibleColumnsRef.current.map((column) => column.name),
1581
+ columnName,
1582
+ targetColumnName,
1583
+ (order) => setColumnOrder(order),
1584
+ );
1585
+ }, []);
1586
+
1562
1587
  const nextPageFn = useCallback((): void => {
1563
1588
  seekPageFn(getCurrentPageValueFn() + 1);
1564
1589
  }, [seekPageFn, getCurrentPageValueFn]);
@@ -1706,6 +1731,7 @@ export function useGridState(
1706
1731
  toggleRowExpansion: toggleRowExpansionFn,
1707
1732
  toggleTreeRow: toggleTreeRowFn,
1708
1733
  moveColumn: moveColumnFn,
1734
+ moveVisibleColumn: moveVisibleColumnFn,
1709
1735
  nextPage: nextPageFn,
1710
1736
  previousPage: previousPageFn,
1711
1737
  onPageSizeChange: onPageSizeChangeFn,
@@ -13,6 +13,7 @@ export interface UseVirtualScrollResult {
13
13
  totalHeight: number;
14
14
  offsetY: number;
15
15
  onScroll: (event: React.UIEvent<HTMLDivElement>) => void;
16
+ setScrollTop: (scrollTop: number) => void;
16
17
  viewportRef: React.RefObject<HTMLDivElement | null>;
17
18
  scrollTop: number;
18
19
  }
@@ -39,6 +40,7 @@ export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualSc
39
40
  totalHeight: virtualWindow.totalHeight,
40
41
  offsetY: virtualWindow.offsetY,
41
42
  onScroll,
43
+ setScrollTop,
42
44
  viewportRef,
43
45
  scrollTop,
44
46
  };
@@ -0,0 +1,33 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import { mountUiGridCustomElement } from './vanillaAdapter';
4
+
5
+ describe('mountUiGridCustomElement', () => {
6
+ it('mounts a custom element and applies grid options', async () => {
7
+ const host = document.createElement('div');
8
+ const ensureDefined = vi.fn(async (tagName: string) => {
9
+ if (!customElements.get(tagName)) {
10
+ customElements.define(tagName, class extends HTMLElement {});
11
+ }
12
+ });
13
+
14
+ const options = {
15
+ id: 'react-adapter-grid',
16
+ data: [{ name: 'Alice' }],
17
+ columnDefs: [{ name: 'name' }],
18
+ };
19
+
20
+ const mounted = await mountUiGridCustomElement(host, {
21
+ options,
22
+ tagName: 'ui-grid-react-adapter-test',
23
+ ensureDefined,
24
+ });
25
+
26
+ expect(ensureDefined).toHaveBeenCalledWith('ui-grid-react-adapter-test');
27
+ expect(host.firstElementChild).toBe(mounted.element);
28
+ expect(mounted.element.options).toBe(options);
29
+
30
+ mounted.unmount();
31
+ expect(host.childElementCount).toBe(0);
32
+ });
33
+ });
@@ -0,0 +1,36 @@
1
+ import type { GridOptions } from '@ornery/ui-grid-core';
2
+
3
+ export interface MountUiGridCustomElementOptions {
4
+ options: GridOptions;
5
+ tagName?: string;
6
+ ensureDefined?: (tagName: string) => Promise<void> | void;
7
+ }
8
+
9
+ export interface MountedUiGridCustomElement {
10
+ element: HTMLElement & { options: GridOptions };
11
+ unmount: () => void;
12
+ }
13
+
14
+ export async function mountUiGridCustomElement(
15
+ container: Element,
16
+ mountOptions: MountUiGridCustomElementOptions,
17
+ ): Promise<MountedUiGridCustomElement> {
18
+ const tagName = mountOptions.tagName ?? 'ui-grid-element';
19
+
20
+ if (mountOptions.ensureDefined) {
21
+ await mountOptions.ensureDefined(tagName);
22
+ }
23
+
24
+ const element = document.createElement(tagName) as HTMLElement & { options: GridOptions };
25
+ element.options = mountOptions.options;
26
+ container.replaceChildren(element);
27
+
28
+ return {
29
+ element,
30
+ unmount: () => {
31
+ if (container.firstElementChild === element) {
32
+ container.replaceChildren();
33
+ }
34
+ },
35
+ };
36
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "paths": {}
5
+ }
6
+ }