@ornery/ui-grid-react 0.1.4 → 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,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
+ }
@@ -0,0 +1,56 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import {
3
+ activeGridEngineBackend,
4
+ clearRustWasmGridEngine,
5
+ defaultGridEngine,
6
+ } from '@ornery/ui-grid';
7
+ import { registerReactUiGridWasmEngineFromModule } from './rustWasmGridEngine';
8
+ import { SORT_DIRECTIONS } from '@ornery/ui-grid';
9
+ import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid';
10
+
11
+ function createContext(): BuildGridPipelineContext {
12
+ return {
13
+ options: {
14
+ id: 'react-engine-spec',
15
+ data: [
16
+ { id: 'row-1', owner: 'Alice' },
17
+ { id: 'row-2', owner: 'Bob' },
18
+ ],
19
+ columnDefs: [{ name: 'owner' }],
20
+ },
21
+ columns: [{ name: 'owner' }],
22
+ activeFilters: {},
23
+ sortState: { columnName: null, direction: SORT_DIRECTIONS.none },
24
+ groupByColumns: [],
25
+ collapsedGroups: {},
26
+ hiddenRowReasons: {},
27
+ expandedRows: {},
28
+ expandedTreeRows: {},
29
+ currentPage: 1,
30
+ pageSize: 0,
31
+ rowSize: 44,
32
+ };
33
+ }
34
+
35
+ describe('rustWasmGridEngine', () => {
36
+ beforeEach(() => {
37
+ clearRustWasmGridEngine();
38
+ });
39
+
40
+ it('registers the real module shape into the shared engine seam', () => {
41
+ const sentinel: PipelineResult = {
42
+ visibleRows: [],
43
+ displayItems: [],
44
+ virtualizationEnabled: true,
45
+ pipelineMs: 0,
46
+ totalItems: 77,
47
+ };
48
+
49
+ registerReactUiGridWasmEngineFromModule({
50
+ build_pipeline_js: () => sentinel,
51
+ });
52
+
53
+ expect(defaultGridEngine.buildPipeline(createContext())).toBe(sentinel);
54
+ expect(activeGridEngineBackend()).toBe('rust-wasm');
55
+ });
56
+ });
@@ -0,0 +1,23 @@
1
+ import type { BuildGridPipelineContext, PipelineResult } from '@ornery/ui-grid';
2
+ import { registerRustWasmGridEngine } from '@ornery/ui-grid';
3
+
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';
6
+
7
+ type UiGridWasmModule = {
8
+ build_pipeline_js(context: unknown): PipelineResult;
9
+ };
10
+
11
+ export function registerReactUiGridWasmEngineFromModule(module: UiGridWasmModule): void {
12
+ registerRustWasmGridEngine({
13
+ buildPipeline(context: BuildGridPipelineContext): PipelineResult {
14
+ return module.build_pipeline_js(context);
15
+ }
16
+ });
17
+ }
18
+
19
+ export async function enableReactUiGridWasmEngine(): Promise<void> {
20
+ const module = await import(/* @vite-ignore */ uiGridWasmModulePath);
21
+ await module.default(uiGridWasmBinaryPath);
22
+ registerReactUiGridWasmEngineFromModule(module);
23
+ }
package/src/ui-grid.css CHANGED
@@ -20,6 +20,17 @@
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(--app-ui-grid-pin-menu-shadow, 0 10px 24px color-mix(in srgb, var(--ui-grid-cell-color) 10%, transparent));
29
+ --ui-grid-pin-menu-action-size: var(--app-ui-grid-pin-menu-action-size, 1.75rem);
30
+ --ui-grid-pin-control-collapsed-size: var(--app-ui-grid-pin-control-collapsed-size, 1px);
31
+ --ui-grid-pin-control-transition-duration: var(--app-ui-grid-pin-control-transition-duration, 160ms);
32
+ --ui-grid-pin-control-transition-easing: var(--app-ui-grid-pin-control-transition-easing, cubic-bezier(0.22, 1, 0.36, 1));
33
+ --ui-grid-pin-menu-scale-closed: var(--app-ui-grid-pin-menu-scale-closed, 0.72);
23
34
  display: block;
24
35
  color: var(--ui-grid-cell-color);
25
36
  }
@@ -39,12 +50,18 @@
39
50
  .grid-hero {
40
51
  display: flex;
41
52
  justify-content: space-between;
53
+ flex-wrap: wrap;
42
54
  gap: 1.5rem;
43
- align-items: end;
55
+ align-items: flex-start;
44
56
  padding: 1rem 0;
45
57
  color: var(--ui-grid-cell-color);
46
58
  }
47
59
 
60
+ .grid-hero > :first-child {
61
+ flex: 1 1 28rem;
62
+ min-width: min(100%, 20rem);
63
+ }
64
+
48
65
  .ui-grid-host .eyebrow {
49
66
  margin: 0 0 0.5rem;
50
67
  text-transform: uppercase;
@@ -70,6 +87,9 @@
70
87
  gap: 1rem;
71
88
  align-items: center;
72
89
  flex-wrap: wrap;
90
+ flex: 0 1 auto;
91
+ margin-left: auto;
92
+ justify-content: flex-end;
73
93
  }
74
94
 
75
95
  .action {
@@ -168,6 +188,22 @@
168
188
  .filter-grid,
169
189
  .body-grid {
170
190
  display: grid;
191
+ width: max-content;
192
+ min-width: 100%;
193
+ }
194
+
195
+ .header-grid,
196
+ .filter-grid {
197
+ position: sticky;
198
+ z-index: 3;
199
+ }
200
+
201
+ .header-grid {
202
+ top: 0;
203
+ }
204
+
205
+ .filter-grid {
206
+ top: var(--ui-grid-header-sticky-top, 0px);
171
207
  }
172
208
 
173
209
  .header-cell,
@@ -192,8 +228,18 @@
192
228
  background: color-mix(in srgb, var(--ui-grid-accent) 8%, var(--ui-grid-header-background));
193
229
  }
194
230
 
231
+ .header-cell.is-pin-menu-open {
232
+ z-index: var(--ui-grid-pin-menu-open-z-index);
233
+ }
234
+
195
235
  .header-label {
196
236
  min-width: 0;
237
+ display: block;
238
+ line-height: 1.25;
239
+ white-space: nowrap;
240
+ overflow: hidden;
241
+ text-overflow: clip;
242
+ hyphens: none;
197
243
  }
198
244
 
199
245
  .header-actions {
@@ -201,6 +247,8 @@
201
247
  align-items: center;
202
248
  gap: 0.4rem;
203
249
  justify-self: end;
250
+ flex-wrap: nowrap;
251
+ flex-shrink: 0;
204
252
  }
205
253
 
206
254
  .header-action,
@@ -250,14 +298,115 @@
250
298
  color: var(--ui-grid-accent);
251
299
  }
252
300
 
301
+ .pin-control {
302
+ position: relative;
303
+ display: inline-grid;
304
+ place-items: center;
305
+ justify-items: center;
306
+ min-width: 2rem;
307
+ overflow: hidden;
308
+ }
309
+
310
+ .pin-trigger {
311
+ grid-area: 1 / 1;
312
+ transform-origin: center;
313
+ transition:
314
+ width var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
315
+ height var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
316
+ opacity var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
317
+ transform var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
318
+ border-color var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
319
+ background-color var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
320
+ color var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing);
321
+ }
322
+
323
+ .pin-menu {
324
+ grid-area: 1 / 1;
325
+ z-index: var(--ui-grid-pin-menu-z-index);
326
+ display: grid;
327
+ grid-auto-rows: var(--ui-grid-pin-menu-action-size);
328
+ gap: var(--ui-grid-pin-menu-gap);
329
+ padding: var(--ui-grid-pin-menu-padding);
330
+ border: 1px solid var(--ui-grid-border-color);
331
+ border-radius: var(--ui-grid-pin-menu-radius);
332
+ background: var(--ui-grid-surface);
333
+ box-shadow: var(--ui-grid-pin-menu-shadow);
334
+ justify-items: center;
335
+ align-items: center;
336
+ width: 2rem;
337
+ max-height: var(--ui-grid-pin-control-collapsed-size);
338
+ opacity: 0;
339
+ pointer-events: none;
340
+ transform: scale(var(--ui-grid-pin-menu-scale-closed));
341
+ transform-origin: center;
342
+ transition:
343
+ max-height var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
344
+ opacity var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
345
+ transform var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
346
+ padding var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
347
+ border-color var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing),
348
+ box-shadow var(--ui-grid-pin-control-transition-duration) var(--ui-grid-pin-control-transition-easing);
349
+ }
350
+
351
+ .pin-control-open {
352
+ align-items: start;
353
+ }
354
+
355
+ .pin-control-open .pin-trigger {
356
+ width: var(--ui-grid-pin-control-collapsed-size);
357
+ height: var(--ui-grid-pin-control-collapsed-size);
358
+ opacity: 0;
359
+ transform: scale(0.72);
360
+ pointer-events: none;
361
+ }
362
+
363
+ .pin-control-open .pin-menu {
364
+ max-height: calc((var(--ui-grid-pin-menu-action-size) * 2) + var(--ui-grid-pin-menu-gap) + (var(--ui-grid-pin-menu-padding) * 2));
365
+ opacity: 1;
366
+ pointer-events: auto;
367
+ transform: scale(1);
368
+ }
369
+
370
+ .pin-menu-action {
371
+ display: inline-flex;
372
+ align-items: center;
373
+ justify-content: center;
374
+ width: var(--ui-grid-pin-menu-action-size);
375
+ height: var(--ui-grid-pin-menu-action-size);
376
+ border: 0;
377
+ border-radius: 999px;
378
+ background: transparent;
379
+ color: var(--ui-grid-cell-color);
380
+ cursor: pointer;
381
+ }
382
+
383
+ .pin-menu-action:hover,
384
+ .pin-menu-action:focus-visible {
385
+ outline: 0;
386
+ background: color-mix(in srgb, var(--ui-grid-accent) 12%, var(--ui-grid-surface));
387
+ color: var(--ui-grid-accent);
388
+ }
389
+
390
+ .pin-menu-action svg {
391
+ width: 1rem;
392
+ height: 1rem;
393
+ fill: currentColor;
394
+ }
395
+
253
396
  .filter-cell {
254
397
  padding: 0.75rem 1rem 1rem;
255
398
  border-bottom: 1px solid var(--ui-grid-border-color);
256
399
  background: var(--ui-grid-header-background);
400
+ overflow: hidden;
257
401
  }
258
402
 
259
403
  .filter-cell input {
404
+ display: block;
260
405
  width: 100%;
406
+ min-width: 0;
407
+ max-width: 100%;
408
+ min-inline-size: 0;
409
+ max-inline-size: 100%;
261
410
  border: 1px solid var(--ui-grid-border-color);
262
411
  border-radius: var(--ui-grid-radius);
263
412
  background: var(--ui-grid-surface);
@@ -390,6 +539,17 @@
390
539
  width: 100%;
391
540
  }
392
541
 
542
+ .grid-virtual-spacer {
543
+ position: relative;
544
+ width: max-content;
545
+ min-width: 100%;
546
+ }
547
+
548
+ .grid-virtual-body {
549
+ position: absolute;
550
+ left: 0;
551
+ }
552
+
393
553
  .body-cell.align-center {
394
554
  text-align: center;
395
555
  }