ng-hub-ui-utils 22.17.0 → 22.18.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.
package/README.md CHANGED
@@ -62,6 +62,7 @@ This library is part of the **ng-hub-ui** ecosystem:
62
62
  - [**ng-hub-ui-portal**](https://www.npmjs.com/package/ng-hub-ui-portal)
63
63
  - [**ng-hub-ui-skeleton**](https://www.npmjs.com/package/ng-hub-ui-skeleton)
64
64
  - [**ng-hub-ui-sortable**](https://www.npmjs.com/package/ng-hub-ui-sortable)
65
+ - [**ng-hub-ui-spreadsheet**](https://www.npmjs.com/package/ng-hub-ui-spreadsheet)
65
66
  - [**ng-hub-ui-stepper**](https://www.npmjs.com/package/ng-hub-ui-stepper)
66
67
  - [**ng-hub-ui-utils**](https://www.npmjs.com/package/ng-hub-ui-utils) ← You are here
67
68
 
@@ -489,6 +490,55 @@ Also available: the imperative `HubTooltipController` (engine) and the
489
490
  types. See the ecosystem-wide
490
491
  [Synergies & agnosticism](../../README.md#synergies--agnosticism) section.
491
492
 
493
+ ### 🔢 Grid Primitives
494
+
495
+ The arithmetic a sheet of cells is made of, with no DOM and no framework in it. A grid has a
496
+ handful of problems every implementation solves again and gets wrong in the same places: where the
497
+ cursor goes at an edge, what rectangle two corners describe, which cell a merged block swallowed,
498
+ and which tracks are worth drawing at all. `ng-hub-ui-spreadsheet` is built on these, which is how
499
+ it installs without `@angular/cdk`.
500
+
501
+ ```ts
502
+ import {
503
+ resolveGridIntent,
504
+ moveGridFocus,
505
+ gridRangeBetween,
506
+ buildSpanMap,
507
+ anchorOf,
508
+ gridWindow,
509
+ growWindowToSpans
510
+ } from 'ng-hub-ui-utils';
511
+
512
+ // A key read as an intent, so the grid does not grow a switch statement per browser. The
513
+ // roving tab stop is `gridCellTabIndex(cell, active, 'roving')`.
514
+ const intent = resolveGridIntent(event, { pageSize: 20 });
515
+
516
+ // The cursor, with the three different answers at the end of a row told apart.
517
+ const next = moveGridFocus(cursor, { row: 0, col: 1 }, bounds, { horizontal: 'continuous' });
518
+
519
+ // A selection kept as two corners, so ten thousand cells cost nothing.
520
+ const range = gridRangeBetween(anchor, cursor);
521
+
522
+ // Merged blocks: the cursor lands on the block and never inside it.
523
+ const spans = buildSpanMap([{ row: 4, col: 0, rowSpan: 3, colSpan: 2 }]);
524
+ const landing = anchorOf(spans, cursor);
525
+
526
+ // Only what the viewport covers, with the pixels the rest would have taken, so the scrollbar
527
+ // still tells the truth. Then widened until every block it touches is drawn whole.
528
+ const window = growWindowToSpans(
529
+ gridWindow({ offset: scrollTop, viewport: height, sizes: 32, count: rows.length, pinned: 1 }),
530
+ blocks,
531
+ 'row',
532
+ 32,
533
+ { count: rows.length, pinned: 1 }
534
+ );
535
+ ```
536
+
537
+ Also exported: `resolveGridEdge`, `isWithinGridRange`, `gridRangeCells`, `gridRangeSize`,
538
+ `clampGridRange`, `spanAt`, `isCovered`, `coversRange`, `gridCellTabIndex`, and the types
539
+ `HubGridCoords`, `HubGridBounds`, `HubGridRange`, `HubGridSpan`, `HubGridSpanMap`, `HubGridWrap`,
540
+ `HubGridEdge`, `HubGridIntent`, `HubGridWindow` and `HubGridTrackSizes`.
541
+
492
542
  ## 🚀 Installation
493
543
 
494
544
  ```bash