@sheetxl/grid-react 0.7.26 → 0.7.28-beta.1

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 (90) hide show
  1. package/README.md +502 -2
  2. package/browser/cjs/index.cjs +3 -12
  3. package/browser/esm/index.mjs +5797 -1867
  4. package/licenses/dependencies.txt +143 -41
  5. package/node/esm/index.mjs +4951 -1861
  6. package/package.json +7 -6
  7. package/types/index.d.ts +2 -1503
  8. package/types/src/cell/CellLayer.d.ts +70 -0
  9. package/types/src/cell/CellRenderer.d.ts +42 -0
  10. package/types/src/cell/DefaultCellRenderer.d.ts +6 -0
  11. package/types/src/cell/GridlineLayer.d.ts +38 -0
  12. package/types/src/cell/Types.d.ts +12 -0
  13. package/types/src/cell/_CellModel.d.ts +82 -0
  14. package/types/src/cell/index.d.ts +6 -0
  15. package/types/src/editor/DefaultEditable.d.ts +121 -0
  16. package/types/src/editor/EditSession.d.ts +182 -0
  17. package/types/src/editor/EditState.d.ts +61 -0
  18. package/types/src/editor/EditorDiv.d.ts +65 -0
  19. package/types/src/editor/EditorInput.d.ts +88 -0
  20. package/types/src/editor/EditorLayer.d.ts +69 -0
  21. package/types/src/editor/EditorPlacement.d.ts +38 -0
  22. package/types/src/editor/index.d.ts +10 -0
  23. package/types/src/editor/useEditorKeymap.d.ts +74 -0
  24. package/types/src/grid/Grid.d.ts +16 -0
  25. package/types/src/grid/IGrid.d.ts +93 -0
  26. package/types/src/grid/index.d.ts +3 -0
  27. package/types/src/header/HeaderDividerAnchor.d.ts +120 -0
  28. package/types/src/header/index.d.ts +3 -0
  29. package/types/src/header/useHeaderSelectionPointer.d.ts +122 -0
  30. package/types/src/index.d.ts +13 -0
  31. package/types/src/layer/HoverTipLayer.d.ts +74 -0
  32. package/types/src/layer/Layer.d.ts +92 -0
  33. package/types/src/layer/LayerTypes.d.ts +47 -0
  34. package/types/src/layer/RangePrompt.d.ts +42 -0
  35. package/types/src/layer/Stage.d.ts +147 -0
  36. package/types/src/layer/StickyAnchor.d.ts +83 -0
  37. package/types/src/layer/StickyFloat.d.ts +65 -0
  38. package/types/src/layer/VirtualDiv.d.ts +59 -0
  39. package/types/src/layer/_Utils.d.ts +37 -0
  40. package/types/src/layer/index.d.ts +11 -0
  41. package/types/src/layer/useGridAnchor.d.ts +157 -0
  42. package/types/src/layer/useHoverIntent.d.ts +49 -0
  43. package/types/src/layer/useLayerResponder.d.ts +29 -0
  44. package/types/src/layer/usePlacement.d.ts +18 -0
  45. package/types/src/layer/useRangeHover.d.ts +35 -0
  46. package/types/src/list/CheckboxList.d.ts +63 -0
  47. package/types/src/list/List.d.ts +55 -0
  48. package/types/src/list/index.d.ts +3 -0
  49. package/types/src/metrics/Types.d.ts +23 -0
  50. package/types/src/metrics/index.d.ts +3 -0
  51. package/types/src/metrics/useGridMetrics.d.ts +67 -0
  52. package/types/src/range/DragHandle.d.ts +129 -0
  53. package/types/src/range/Draggable.d.ts +86 -0
  54. package/types/src/range/FillHandle.d.ts +54 -0
  55. package/types/src/range/RangeRect.d.ts +36 -0
  56. package/types/src/range/RangeResizer.d.ts +68 -0
  57. package/types/src/range/RangeStyle.d.ts +8 -0
  58. package/types/src/range/index.d.ts +13 -0
  59. package/types/src/selection/DefaultSelection.d.ts +59 -0
  60. package/types/src/selection/ICellAnchor.d.ts +21 -0
  61. package/types/src/selection/ISelection.d.ts +65 -0
  62. package/types/src/selection/RangesPickerLayer.d.ts +128 -0
  63. package/types/src/selection/SelectionLayer.d.ts +179 -0
  64. package/types/src/selection/SelectionStyle.d.ts +28 -0
  65. package/types/src/selection/SelectionTouchGrips.d.ts +29 -0
  66. package/types/src/selection/SelectionUtils.d.ts +2 -0
  67. package/types/src/selection/Types.d.ts +67 -0
  68. package/types/src/selection/_SelectionNavigation.d.ts +6 -0
  69. package/types/src/selection/_SelectionUtils.d.ts +7 -0
  70. package/types/src/selection/index.d.ts +12 -0
  71. package/types/src/selection/useDragCells.d.ts +218 -0
  72. package/types/src/selection/useSelectionPointer.d.ts +70 -0
  73. package/types/src/surface/GridPane.d.ts +85 -0
  74. package/types/src/surface/GridPaneProvider.d.ts +37 -0
  75. package/types/src/surface/GridSurface.d.ts +11 -0
  76. package/types/src/surface/IGridPane.d.ts +288 -0
  77. package/types/src/surface/IGridSurface.d.ts +141 -0
  78. package/types/src/surface/SurfaceStateProvider.d.ts +50 -0
  79. package/types/src/surface/_GridUtils.d.ts +37 -0
  80. package/types/src/surface/index.d.ts +7 -0
  81. package/types/src/surface/useGridPane.d.ts +31 -0
  82. package/types/src/surface/useSurfaceState.d.ts +22 -0
  83. package/types/src/svg/LineSVG.d.ts +13 -0
  84. package/types/src/svg/RectangleSVG.d.ts +114 -0
  85. package/types/src/svg/ShapeBounds.d.ts +15 -0
  86. package/types/src/svg/Types.d.ts +37 -0
  87. package/types/src/svg/index.d.ts +9 -0
  88. package/types/src/theme/GridTheme.d.ts +56 -0
  89. package/types/src/theme/index.d.ts +2 -0
  90. package/node/assets/style.css +0 -1
@@ -0,0 +1,65 @@
1
+ import { default as React } from 'react';
2
+ import { ThemeAttrs } from '@sheetxl/utils-react';
3
+ import { IGridPane } from '../surface';
4
+ import { StickyState } from './StickyAnchor';
5
+ import { GridAnchor, GridPlacement, GridBoundary, GridWhenOff } from './useGridAnchor';
6
+ /**
7
+ * Carry a grid float's theme **across the portal boundary**. A grid float portals into the shared,
8
+ * theme-free fullscreen root, where the grid's `--sxl-color-*` tokens do NOT cascade in (the CSS cascade
9
+ * doesn't cross a portal — unlike MUI content, which rides React context and so themes for free). Returns
10
+ * the `className` + `data-sxl-theme-*` attrs to put on the portaled element so the token rules resolve —
11
+ * the same mechanism `FloatPopper` uses for menus.
12
+ *
13
+ * Two parts, because the app themes with `@scope` (per-instance), not just global attribute rules:
14
+ * - **attrs** — the pane's live `mode` / `surface` (grid surface, and the grid's own mode even if it
15
+ * differs from the app). These select WHICH token values apply.
16
+ * - **className** — the theme **scope class(es)** (the float surface's, plus the nearest token provider's
17
+ * `scopeClass` when the instance is isolated). Without a scope class the portaled element is outside
18
+ * every `@scope` root, so the `@scope (.cls){ :scope[mode][surface]{…} }` rules never match it and the
19
+ * attrs alone resolve nothing. The class makes the portal a `:scope` root so those rules apply.
20
+ */
21
+ export declare const useGridFloatTheme: (pane: IGridPane | null | undefined) => {
22
+ className?: string;
23
+ attrs: ThemeAttrs;
24
+ };
25
+ /**
26
+ * Props for {@link StickyFloat}.
27
+ */
28
+ export interface StickyFloatProps {
29
+ /** WHERE to anchor — a grid coordinate (range / cell / bounds / edge / point). */
30
+ anchor: GridAnchor;
31
+ /** Which side + align the float attaches to. @defaultValue `{ side: 't', align: 'center' }` */
32
+ placement?: GridPlacement;
33
+ /** What happens when the anchor leaves the data area. @defaultValue 'hide' */
34
+ whenOff?: GridWhenOff;
35
+ /** Clip / clamp boundary. @defaultValue 'data' */
36
+ boundary?: GridBoundary;
37
+ /** Px overlap below which a geometried anchor reads as clipped. @defaultValue 0 */
38
+ minVisible?: number;
39
+ /** Gap (px) between the anchor edge and the float. @defaultValue 4 */
40
+ offset?: number;
41
+ /**
42
+ * Offset (px) along the anchor EDGE — only applies to `start` / `end` alignment (a centered float has
43
+ * no edge to slide along). Positive insets the float inward from the corner. @defaultValue 0
44
+ */
45
+ alignOffset?: number;
46
+ /** Controlled visibility (e.g. a hover tooltip). @defaultValue true */
47
+ open?: boolean;
48
+ /** Portal container. @defaultValue the fullscreen-aware root ({@link useFullscreenPortal}). */
49
+ container?: Element;
50
+ className?: string;
51
+ style?: React.CSSProperties;
52
+ /** @defaultValue 1250 (a below-modal band; see FLOATING-DESIGN §z-index). */
53
+ zIndex?: number;
54
+ /** The float content — a node, or a render function `(state) => node` (e.g. to react to `slid`). */
55
+ children?: React.ReactNode | ((state: StickyState) => React.ReactNode);
56
+ }
57
+ /**
58
+ * The ergonomic **floating label**: a {@link StickyAnchor} that renders its child in a **portal**
59
+ * (escaping the pane clip, so it can flow past the pane bounds — e.g. overhang a freeze line) anchored
60
+ * at the resolved point. The common case — `<StickyFloat anchor={{ range }}>A1:B4</StickyFloat>` —
61
+ * with no hand-written `createPortal`. For an in-grid (clipping) label, use `<StickyAnchor>` with a plain
62
+ * `<div>` child instead.
63
+ */
64
+ export declare const StickyFloat: React.FC<StickyFloatProps>;
65
+ //# sourceMappingURL=StickyFloat.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { VirtualDivPlacement } from './LayerTypes';
2
+ export interface VirtualDivProps extends React.HTMLAttributes<HTMLDivElement>, VirtualDivPlacement {
3
+ /**
4
+ * Skip device-pixel edge alignment. By default the box edges are snapped to the
5
+ * device grid (so content stays crisp and adjacent boxes sharing an edge resolve to
6
+ * the same pixel — no seam). Turn off for a raw, unaligned box.
7
+ *
8
+ * @defaultValue false
9
+ */
10
+ disableAlign?: boolean;
11
+ /**
12
+ * Extend the right/bottom edges by 1 device px so the box covers all four of its
13
+ * gridline pixels (a gridline paints the boundary PIXEL `[B, B+1]`). Adjacent boxes
14
+ * then overlap by 1px at the shared boundary — intended, like Excel cell fills.
15
+ *
16
+ * @defaultValue false
17
+ */
18
+ coverGridlines?: boolean;
19
+ /**
20
+ * Render an aligned **border** of this many DEVICE px instead of a fill. The edges are placed on
21
+ * the boundary centerline (`edgeCenter`, leading straddles / trailing grows outward) — the same
22
+ * rule as gridlines and {@link RectangleSVG} — so the border lines up with the selection rectangle
23
+ * exactly. The box is `border-box`; set the color via `style.borderColor`.
24
+ *
25
+ * @remarks
26
+ * This is the HTML-`border-box` counterpart of {@link RectangleSVG}'s stroke. Use it for a
27
+ * **content-hosting** box that needs a matching border (the cell editor: the content auto-insets
28
+ * inside the border). For a pure outline/fill overlay, prefer {@link RectangleSVG}/`RangeRect`.
29
+ */
30
+ alignWidth?: number;
31
+ /**
32
+ * Keep the element mounted (positioned off-screen) instead of culling to `null` when off-screen.
33
+ * For always-mounted content such as the cell editor, whose input must never unmount.
34
+ *
35
+ * @defaultValue false
36
+ */
37
+ keepMounted?: boolean;
38
+ }
39
+ /**
40
+ * A **VirtualDiv**: a `<div>` whose bounds are driven by the grid metrics. Place it at a `range`
41
+ * (cell coordinates), `bounds` (grid-space pixels), or `fill` (the whole view); it resolves the
42
+ * placement via the current pane, culls when off-screen, and device-aligns its edges — then hosts
43
+ * any content (CSS background/gradient/pattern for fills, children like images / widgets /
44
+ * movables, or SVG shape leaves like {@link RectangleSVG}).
45
+ *
46
+ * Everything you know about divs applies — pointer events, focus (`tabIndex`), refs, CSS, children —
47
+ * and means what it always means. It is **hittable by default**, exactly like a real div; a
48
+ * decoration opts out with `style.pointerEvents:'none'` (never a hit target; a child shape can
49
+ * re-enable `pointer-events` on its stroke to opt back in). Its handlers are ordinary native props
50
+ * that fire **at its layer's turn** in the ordered bubble (law 3: layers on top hear events first —
51
+ * when the hit is inside this box the native dispatch IS its layer's turn; when the box sits in a
52
+ * lower layer under the pointer, the Stage invokes its positional handlers at that slot). Call
53
+ * `event.stopPropagation()` to consume — there is no auto-consume and no passthrough flag. Render
54
+ * it inside a {@link Layer} (which is what gives it — and everything inside it — its layer
55
+ * membership); with frozen panes each pane instantiates one. For multiple ranges, map it in the
56
+ * consumer (one per range).
57
+ */
58
+ export declare const VirtualDiv: import('react').NamedExoticComponent<VirtualDivProps & import('react').RefAttributes<HTMLDivElement>>;
59
+ //# sourceMappingURL=VirtualDiv.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { RangeCoords } from '@sheetxl/utils';
2
+ import { LayerNode } from './LayerTypes';
3
+ /** Content key for a range — stable across object identity (dedupe / memo / hover-target keys). */
4
+ export declare const rangeKey: (r: RangeCoords) => string;
5
+ /**
6
+ * A layer's **derived stacking order** — its index in the normalized layer list, i.e. the react list
7
+ * order with explicit-`zIndex` overrides already folded in (see {@link normalizeLayers}). This is the
8
+ * "effective z" a dev means by *visual order* — **not** the raw `zIndex` prop. The {@link Layer} at
9
+ * each slot registers its event tap at it, and descendant command scopes read it (via `useLayer`) so
10
+ * keystroke / command dispatch follows the visual stack. Derived purely from the React list — **no
11
+ * DOM, no registration timing**.
12
+ */
13
+ export declare const LayerOrderContext: import('react').Context<number>;
14
+ /**
15
+ * Whether the current subtree is inside a {@link Layer}. Backs the flat-stack law (law 4): a `Layer`
16
+ * dev-errors when it finds itself already inside one, and a {@link VirtualDiv} dev-warns when it is
17
+ * NOT inside one. Purely a dev-time diagnostic — no runtime behavior branches on it.
18
+ */
19
+ export declare const LayerScopeContext: import('react').Context<boolean>;
20
+ /**
21
+ * Normalize the layers (the grid's children) for rendering: sort by `zIndex` and stamp each with its
22
+ * **derived order index** (its position in the sorted list = its effective z). A non-array passes
23
+ * through unchanged.
24
+ *
25
+ * @remarks
26
+ * Ordering matches the CSS model: sort by `zIndex` **ascending** (higher paints later = on top); a layer
27
+ * without a `zIndex` counts as `0`, and `Array.prototype.sort` is stable so same-`zIndex` layers keep
28
+ * **declaration order** — exactly as `z-index: auto` behaves. The `zIndex` is read off the top-level
29
+ * child element (a `<Layer zIndex>` or a composite forwarding one) — this is the ONLY place z is
30
+ * honored; a Layer's dispatch slot always comes from the resulting order index. Each sorted layer is
31
+ * wrapped in a {@link LayerOrderContext} carrying its index, so its `Layer` (and any descendant command
32
+ * scope) reads its effective stacking order — the react-list analog of CSS paint order. That index is
33
+ * stable across remounts / effect re-runs (it is the LIST position, not when a responder registered)
34
+ * and needs **no DOM**. Keyed by the element's own key so a reorder re-slots (not remounts).
35
+ */
36
+ export declare const normalizeLayers: (layers: LayerNode) => React.ReactNode;
37
+ //# sourceMappingURL=_Utils.d.ts.map
@@ -0,0 +1,11 @@
1
+ export * from './LayerTypes';
2
+ export * from './Layer';
3
+ export * from './VirtualDiv';
4
+ export type { GridAnchor, GridPlacement, GridWhenOff, GridBoundary, GridFloatSide, GridAlign, GridSlide } from './useGridAnchor';
5
+ export * from './StickyAnchor';
6
+ export * from './StickyFloat';
7
+ export * from './useHoverIntent';
8
+ export * from './useRangeHover';
9
+ export * from './HoverTipLayer';
10
+ export * from './RangePrompt';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,157 @@
1
+ import { CellCoords, RangeCoords, Bounds, Point, AnchorLocation } from '@sheetxl/utils';
2
+ import { IGridPane } from '../surface';
3
+ /**
4
+ * A viewport edge (used by `edge` anchors and reported by {@link GridAnchorResolved.clampedTo}).
5
+ */
6
+ export type GridFloatSide = 't' | 'l' | 'r' | 'b';
7
+ /** Alignment along the anchored edge (which end the anchor pixel sits at). */
8
+ export type GridAlign = 'start' | 'center' | 'end';
9
+ /**
10
+ * WHERE the float attaches to the range — the `side` it sits on plus the `align` along that edge.
11
+ * This picks a **single anchor pixel** (a corner / edge-midpoint) which decides pane ownership and
12
+ * clip (that one pixel is in exactly one pane), while positioning still uses the whole range rect.
13
+ * Maps to a Floating UI `Placement` at the `FloatPopper` boundary (`t-start` → `top-start`, …).
14
+ */
15
+ export interface GridPlacement {
16
+ side: GridFloatSide;
17
+ /** @defaultValue 'center' */
18
+ align?: GridAlign;
19
+ }
20
+ /**
21
+ * What happens when **no pane owns the anchor pixel** (it scrolled off the data area).
22
+ *
23
+ * - `'hide'` — not rendered (touch handles once their range is gone; ephemeral tooltips). Default.
24
+ * - `'clamp'` — project the pixel onto the nearest data edge and slide it there (sticky / presence);
25
+ * the owner becomes the pane under the clamped pixel and {@link GridAnchorResolved.slid} is set.
26
+ */
27
+ export type GridWhenOff = 'hide' | 'clamp';
28
+ /**
29
+ * How far the anchor pixel was pushed to stay in frame (the `clamp` fallback). Handed to the sticky
30
+ * child so it can render differently when slid — an arrow toward the true (off-frame) anchor, etc.
31
+ */
32
+ export interface GridSlide {
33
+ /** Client-px offset applied to the anchor to clamp it into the boundary. */
34
+ dx: number;
35
+ dy: number;
36
+ /** Magnitude of the push (`hypot(dx, dy)`). */
37
+ distance: number;
38
+ /** The edge it slid to — the direction of the true, off-frame anchor. */
39
+ direction: GridFloatSide;
40
+ }
41
+ /**
42
+ * WHERE a grid float is anchored — a **grid coordinate**, resolved live to a client rect via the
43
+ * pane. The float tracks it across scroll / zoom / resize / freeze.
44
+ *
45
+ * - `range` — a cell range (the primary "virtual range" case).
46
+ * - `cell` — a single cell.
47
+ * - `bounds` — pane **view-relative** bounds (same space `getBoundsFromRange` returns).
48
+ * - `edge` — the midpoint of a viewport edge (touch handles / scrollbars).
49
+ * - `point` — a **client-px** point (drag readouts that follow the pointer).
50
+ */
51
+ export type GridAnchor = {
52
+ range: RangeCoords;
53
+ } | {
54
+ cell: CellCoords;
55
+ } | {
56
+ bounds: Bounds;
57
+ } | {
58
+ edge: GridFloatSide;
59
+ } | {
60
+ point: Point;
61
+ };
62
+ /**
63
+ * The clip / clamp boundary — a **rect fed to the positioning math**, NOT a DOM parent. This is how
64
+ * "don't slide under the headers" is expressed: clamp to the data viewport.
65
+ *
66
+ * - `'data'` — the union of the panes' cell areas (headers excluded). Default.
67
+ * - `'pane'` — just the resolving pane's cell area.
68
+ * - `'grid'` — currently the same as `'data'` (until a surface/header rect is threaded).
69
+ * - a {@link Bounds} — an explicit **client-px** rect.
70
+ */
71
+ export type GridBoundary = 'data' | 'pane' | 'grid' | Bounds;
72
+ /** A `DOMRect`-shaped plain object (works in Floating UI and in Node tests without a DOM). */
73
+ export interface GridAnchorRect {
74
+ x: number;
75
+ y: number;
76
+ width: number;
77
+ height: number;
78
+ top: number;
79
+ left: number;
80
+ right: number;
81
+ bottom: number;
82
+ }
83
+ /** The full resolution of a {@link GridAnchor} at a moment in time. */
84
+ export interface GridAnchorResolved {
85
+ /** The anchor's client rect (what Floating UI positions against). */
86
+ rect: GridAnchorRect;
87
+ /** True when the anchor is fully outside {@link boundaryRect}. */
88
+ clipped: boolean;
89
+ /** The clip / clamp boundary in client px. */
90
+ boundaryRect: GridAnchorRect;
91
+ /** When clipped, which side the anchor is off (for `clamp` / presence); else `null`. */
92
+ clampedTo: GridFloatSide | null;
93
+ /**
94
+ * The single anchor pixel (client px) the `placement` picks out — the corner / edge-midpoint that
95
+ * decides ownership + clip. For a straddling range this pixel lives in exactly one pane.
96
+ */
97
+ anchorPoint: Point;
98
+ /** The pane (by anchor location) that owns the anchor pixel — the single render owner. */
99
+ owner: AnchorLocation | null;
100
+ /** Set (non-null) only under the `clamp` fallback when the anchor slid to stay in frame. */
101
+ slid: GridSlide | null;
102
+ /**
103
+ * The anchor's range has **no visible portion** (fully scrolled off) — independent of `whenOff`. Unlike
104
+ * {@link clipped} (which `clamp` forces false), this stays true under `clamp`, so a presence can tell
105
+ * "partially obstructed / clamped" (still show) from "off-screen" (show a goto affordance).
106
+ */
107
+ outOfView: boolean;
108
+ }
109
+ /** The hook result: stable live accessors + render-time snapshot values. */
110
+ export interface UseGridAnchorResult {
111
+ /**
112
+ * A Floating UI **virtual reference** with **stable identity** — its `getBoundingClientRect` reads
113
+ * the anchor **live** on every call (Floating UI's `autoUpdate` invokes it between React renders).
114
+ */
115
+ reference: {
116
+ getBoundingClientRect: () => DOMRect;
117
+ };
118
+ /** Live accessor for the boundary rect (for `shift`/`flip`/`hide` limiters). */
119
+ getBoundaryRect: () => DOMRect;
120
+ /** Render-time snapshot: anchor outside the boundary. Updates each scroll frame (pane re-render). */
121
+ clipped: boolean;
122
+ /** Render-time snapshot: which side the anchor is off, when clipped. */
123
+ clampedTo: GridFloatSide | null;
124
+ /** Render-time snapshot: the placement's anchor pixel (client px). */
125
+ anchorPoint: Point;
126
+ /** Render-time snapshot: the pane (by anchor location) that owns the anchor pixel. */
127
+ owner: AnchorLocation | null;
128
+ /** Render-time snapshot: the slide applied by the `clamp` fallback (else `null`). */
129
+ slid: GridSlide | null;
130
+ /** Render-time snapshot: the range is fully scrolled off (stays true under `clamp`; see resolved). */
131
+ outOfView: boolean;
132
+ /** The pane the anchor resolves against (null outside a `GridPaneProvider`). */
133
+ pane: IGridPane | null;
134
+ }
135
+ /** @internal The shared placement default (`StickyAnchor` and the resolver must agree on it). */
136
+ export declare const DEFAULT_PLACEMENT: GridPlacement;
137
+ /**
138
+ * Resolve a {@link GridAnchor} to a client rect + clip state against a boundary. **Pure** (given a
139
+ * pane) — no React, no live DOM beyond `getBoundingClientRect`, so it is directly unit-testable with a
140
+ * fake {@link IGridPane}. Shared by {@link useGridAnchor} (the live closures call this per frame).
141
+ */
142
+ export declare const resolveGridAnchor: (pane: IGridPane | null, anchor: GridAnchor, boundary?: GridBoundary, minVisible?: number, placement?: GridPlacement, whenOff?: GridWhenOff) => GridAnchorResolved;
143
+ /**
144
+ * Bridge a {@link GridAnchor} (grid coordinate) to a **live Floating UI virtual reference** plus clip
145
+ * state. The pane already re-renders every layer on scroll (so the snapshot values update per frame),
146
+ * and the returned {@link UseGridAnchorResult.reference} reads live on each `getBoundingClientRect`
147
+ * call so `autoUpdate` keeps the float glued between renders. No hidden measure element, no
148
+ * scroll-parent walk. Read the pane from context, or pass one explicitly.
149
+ */
150
+ export declare const useGridAnchor: (anchor: GridAnchor, options?: {
151
+ pane?: IGridPane;
152
+ boundary?: GridBoundary;
153
+ minVisible?: number;
154
+ placement?: GridPlacement;
155
+ whenOff?: GridWhenOff;
156
+ }) => UseGridAnchorResult;
157
+ //# sourceMappingURL=useGridAnchor.d.ts.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Options for {@link useHoverIntent}.
3
+ */
4
+ export interface UseHoverIntentOptions {
5
+ /** Dwell (ms) over a target before opening — kills pass-through flicker. @defaultValue 300 */
6
+ openDelay?: number;
7
+ /** Grace (ms) after leaving before closing — lets the pointer cross the gap to the float. @defaultValue 150 */
8
+ closeDelay?: number;
9
+ }
10
+ /** Props to spread onto the floated element so hovering IT keeps the float open. */
11
+ export interface HoverTipProps {
12
+ onPointerEnter: () => void;
13
+ onPointerLeave: () => void;
14
+ style: React.CSSProperties;
15
+ }
16
+ /**
17
+ * The result of {@link useHoverIntent}.
18
+ */
19
+ export interface UseHoverIntentResult {
20
+ /** Whether the float should be shown. */
21
+ open: boolean;
22
+ /**
23
+ * Aim the intent at a target, or `null` for "nothing hovered". Idempotent per key: calling with the
24
+ * SAME key (e.g. every `pointermove` within one cell) does NOT restart the open timer. Changing the
25
+ * key while open re-aims without closing.
26
+ */
27
+ setTarget: (key: string | null) => void;
28
+ /**
29
+ * Spread onto the floated element so hovering IT keeps the float open (bridges the anchor→float
30
+ * gap). Sets `pointer-events: auto` and cancels / reschedules the close on enter / leave.
31
+ */
32
+ tipProps: HoverTipProps;
33
+ /**
34
+ * Close the float **immediately**, bypassing the `closeDelay` grace — for a hard dismissal (e.g. the
35
+ * anchored cell entered edit mode) rather than the pointer merely wandering off.
36
+ */
37
+ close: () => void;
38
+ }
39
+ /**
40
+ * The reusable **hover-intent state machine** behind a hover tooltip — delays + keep-open-on-tip-hover,
41
+ * with NO hit-testing of its own. Feed it a target key via {@link UseHoverIntentResult.setTarget}
42
+ * (`null` = nothing) and it debounces open / close; spread {@link UseHoverIntentResult.tipProps} on the
43
+ * float so moving onto the float itself keeps it open.
44
+ *
45
+ * {@link useRangeHover} wraps this with a pointer responder against a fixed range; a dynamic consumer
46
+ * (e.g. an error layer discovering the hovered cell) drives `setTarget` itself.
47
+ */
48
+ export declare const useHoverIntent: (options?: UseHoverIntentOptions) => UseHoverIntentResult;
49
+ //# sourceMappingURL=useHoverIntent.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Options for {@link useLayerResponder}.
3
+ */
4
+ export interface UseLayerResponderOptions {
5
+ /** Live accessor for the box's DOM element (null while culled / unmounted). */
6
+ getElement: () => Element | null;
7
+ /**
8
+ * The box's spread props (`...rest`). Its event handlers (`onPointerDown`, …) are exposed to the
9
+ * Stage so the box can be invoked when it sits BELOW the native target (where its own handlers
10
+ * did not fire natively). The handlers stay native on the element too — for the hit case (the
11
+ * "up your subtree" phase of the one-bubble law).
12
+ *
13
+ * (`any`: a JSX spread bag — heterogeneous by nature; only `function`-valued walked-event props
14
+ * are ever read, guarded by a `typeof` check.)
15
+ */
16
+ handlerProps?: Record<string, any>;
17
+ }
18
+ /**
19
+ * Registers a geometried box ({@link VirtualDiv}) with its pane's Stage so the DOM-driven dispatch
20
+ * can resolve the box a hit element belongs to, interleave it with the layer taps at its **layer's
21
+ * slot** (read from {@link LayerOrderContext}), and invoke boxes below the native target.
22
+ *
23
+ * @remarks
24
+ * Registration lives in a registry SEPARATE from the tap chain (see `Stage.LayerRegistration`); the
25
+ * latest accessors are read through a ref so the registration is stable for the box's life. Call it
26
+ * unconditionally (before any early `return null`).
27
+ */
28
+ export declare const useLayerResponder: ({ getElement, handlerProps }: UseLayerResponderOptions) => void;
29
+ //# sourceMappingURL=useLayerResponder.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { Bounds } from '@sheetxl/utils';
2
+ import { IGridPane } from '../surface';
3
+ import { VirtualDivPlacement } from './LayerTypes';
4
+ /** The resolved placement: the pane, the RAW (pre-snap) bounds, and whether it's fully off-screen. */
5
+ export interface Placement {
6
+ pane: IGridPane;
7
+ raw: Bounds;
8
+ /** True when fully off-screen. The caller decides whether to cull (`return null`) or keep mounted. */
9
+ culled: boolean;
10
+ }
11
+ /**
12
+ * Resolve a {@link VirtualDivPlacement} against the current pane: range → bounds (or explicit
13
+ * bounds, or the pane box for `fill`), plus the off-screen cull flag. Returns `null` when there is
14
+ * no pane or no placement to resolve. Shared by {@link VirtualDiv} / {@link RangeRect} so the
15
+ * resolve-and-cull logic lives once.
16
+ */
17
+ export declare const usePlacement: ({ range, bounds, fill, clipInsets }: VirtualDivPlacement) => Placement | null;
18
+ //# sourceMappingURL=usePlacement.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { RangeCoords } from '@sheetxl/utils';
2
+ import { UseHoverIntentOptions, HoverTipProps } from './useHoverIntent';
3
+ /**
4
+ * The result of {@link useRangeHover}.
5
+ */
6
+ export interface UseRangeHoverResult {
7
+ /** Whether a hover tooltip for the range should be shown (after the intent delays). */
8
+ open: boolean;
9
+ /** Spread onto the floated tip so hovering it keeps the tooltip open (see {@link HoverTipProps}). */
10
+ tipProps: HoverTipProps;
11
+ }
12
+ /**
13
+ * Tracks the pointer over a **fixed grid range** and drives hover-tooltip intent: returns `open` (with
14
+ * open / close delays) plus `tipProps` to keep the tip open while the pointer is on it. Pair with
15
+ * {@link StickyFloat} for the label:
16
+ *
17
+ * ```tsx
18
+ * const { open, tipProps } = useRangeHover(range);
19
+ * <StickyFloat anchor={{ range }} placement={{ side: 't' }} offset={6} open={open}>
20
+ * <div {...tipProps}>Value must be a positive number</div>
21
+ * </StickyFloat>
22
+ * ```
23
+ *
24
+ * Must be called inside a grid (needs pane context). For a **dynamic** target (an error layer that
25
+ * discovers whichever hovered cell has errors), drive {@link useHoverIntent} directly instead.
26
+ *
27
+ * @remarks
28
+ * Registers a passive `pointermove` / `pointerleave` responder on the pane's Stage (mirrors
29
+ * a {@link Layer} tap); it never consumes the event. Because a grid child renders per pane, the tip
30
+ * resolves on the pane owning the anchor — for a range that straddles panes the open state follows the
31
+ * pointer's pane, which can differ from the owner (a known v1 limitation; small / single-cell ranges,
32
+ * the common error-tooltip case, are unaffected).
33
+ */
34
+ export declare const useRangeHover: (range: RangeCoords, options?: UseHoverIntentOptions) => UseRangeHoverResult;
35
+ //# sourceMappingURL=useRangeHover.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { default as React } from 'react';
2
+ import { ISelection } from '../selection';
3
+ export interface CheckboxListAttributes {
4
+ isCheckboxList: () => true;
5
+ }
6
+ export interface ICheckboxListElement extends HTMLDivElement, CheckboxListAttributes {
7
+ }
8
+ /**
9
+ * Props for {@link CheckboxList}.
10
+ */
11
+ export interface CheckboxListProps extends React.HTMLAttributes<HTMLDivElement> {
12
+ /** Number of rows. */
13
+ itemCount: number;
14
+ /** Whether the item at `index` is checked. */
15
+ isChecked: (index: number) => boolean;
16
+ /**
17
+ * Set `indices` to `checked`. Called with a single index on a click, every selected row on a keyboard
18
+ * toggle (Space/Enter), and every row from `(Select All)`. The caller maps `index` to its own store.
19
+ */
20
+ onCheckedChange: (indices: number[], checked: boolean) => void;
21
+ /** The value/label content for `index`. */
22
+ renderValue: (index: number) => React.ReactNode;
23
+ /**
24
+ * Render the checkbox **control** (visual only — the row owns the toggle, so it's forced
25
+ * `pointerEvents:none`). `indeterminate` is set for the `(Select All)` tri-state. Override to use a
26
+ * styled control (MUI etc.). @defaultValue a native checkbox.
27
+ */
28
+ renderCheckbox?: (checked: boolean, indeterminate?: boolean) => React.ReactNode;
29
+ /**
30
+ * Render a divider between the `(Select All)` header and the list, or elsewhere as needed.
31
+ */
32
+ renderDivider?: () => React.ReactNode;
33
+ /**
34
+ * Show a tri-state **(Select All)** header with this label. It operates over *all* rows — which in a
35
+ * filtered list are the filtered ones, so it's "Select all (filtered)" automatically.
36
+ */
37
+ selectAllLabel?: React.ReactNode;
38
+ /** Uniform row height in px. @defaultValue 24 */
39
+ itemHeight?: number;
40
+ /** Selection model (highlight + Arrow nav). Created internally if omitted. */
41
+ selection?: ISelection;
42
+ /** Content cache-bust; bump when checked state changes without `isChecked`'s identity changing. */
43
+ cacheToken?: unknown;
44
+ /** Ref for the Element. */
45
+ ref?: React.Ref<ICheckboxListElement>;
46
+ }
47
+ /**
48
+ * A **checkbox list** on top of {@link List}: a virtualized multi-select with a checkbox per row and an
49
+ * optional tri-state `(Select All)` header.
50
+ *
51
+ * @remarks
52
+ * Owns the toggle behavior — the part worth not re-deriving:
53
+ * - **Click** (anywhere on a row) toggles that one row, and selects it.
54
+ * - **Space / Enter** toggles every *selected* row using the **anchor** (active cell) as the template:
55
+ * if the anchor is checked the whole selection clears, otherwise it sets — so a mixed multi-selection
56
+ * goes uniform rather than checkerboarding.
57
+ * - **(Select All)** sets every row to the opposite of the all-checked state.
58
+ *
59
+ * Checked state is the caller's store (`isChecked` / `onCheckedChange`), orthogonal to the selection.
60
+ * Styling is the caller's via {@link CheckboxListProps.renderValue} / {@link CheckboxListProps.renderCheckbox}.
61
+ */
62
+ export declare const CheckboxList: React.ForwardRefExoticComponent<Omit<CheckboxListProps, "ref"> & React.RefAttributes<ICheckboxListElement>>;
63
+ //# sourceMappingURL=CheckboxList.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { default as React } from 'react';
2
+ import { ISelection } from '../selection';
3
+ export interface ListAttributes {
4
+ isList: () => true;
5
+ }
6
+ export interface IListElement extends HTMLDivElement, ListAttributes {
7
+ }
8
+ /**
9
+ * Props for {@link List}.
10
+ */
11
+ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
12
+ /** Number of rows. */
13
+ itemCount: number;
14
+ /**
15
+ * Content for the item at `index`. Called only for the *visible* rows (virtualized), so real
16
+ * interactive controls — checkboxes, buttons — are fine here. `index` is the row index (for a
17
+ * filtered list this is the *view* index — map it to your source store yourself).
18
+ */
19
+ renderItem: (index: number) => React.ReactNode;
20
+ /** Uniform row height in px. @defaultValue 24 */
21
+ itemHeight?: number;
22
+ /**
23
+ * The selection model — the active-row highlight + Arrow-key navigation. The List's *state*. Owned
24
+ * by the caller (so it can read what's selected); a private one is created when omitted.
25
+ */
26
+ selection?: ISelection;
27
+ /**
28
+ * Key events over the list. Forwarded into the grid's keyboard layer (so it fires even for keys the
29
+ * grid would otherwise consume, like Enter) — runs before the built-in Arrow navigation, so handle
30
+ * Space/Enter here and `stopPropagation` to override. Read {@link ListProps.selection} to act on the
31
+ * selected rows; Arrows you don't handle fall through to navigation.
32
+ */
33
+ onKeyDown?: (e: React.KeyboardEvent) => void;
34
+ /**
35
+ * Opaque content cache-bust forwarded to the `CellLayer`. Bump (new identity) when an item's
36
+ * *rendered* content changed without `renderItem`'s identity changing.
37
+ */
38
+ cacheToken?: unknown;
39
+ /** Ref for the Element. */
40
+ ref?: React.Ref<IListElement>;
41
+ }
42
+ /**
43
+ * A virtualized **list** built on `GridSurface` with the grid's own layers — a `CellLayer` for the
44
+ * items and a `SelectionLayer` for the active-row highlight + Arrow keys (the same shape as the editor
45
+ * demo: a model passed to layers as props).
46
+ *
47
+ * @remarks
48
+ * The List's whole state is its {@link ListProps.selection}; it has no per-item callbacks and no notion
49
+ * of "checked" or "toggle". Pointer selection is the `SelectionLayer`'s job (it hit-tests the pointer),
50
+ * so the cells are inert, transparent content; the keyboard is the caller's via {@link ListProps.onKeyDown}.
51
+ * The active row fills (no Excel anchor hole) and carries an inset ring, since the anchor is the live
52
+ * Space/scroll target. See {@link CheckboxList} for the checkbox-list specialization.
53
+ */
54
+ export declare const List: React.ForwardRefExoticComponent<Omit<ListProps, "ref"> & React.RefAttributes<IListElement>>;
55
+ //# sourceMappingURL=List.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from './List';
2
+ export * from './CheckboxList';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * A function that returns the size of an item given its index.
3
+ */
4
+ export type ItemSizer = (index: number) => number;
5
+ export interface IGridMetrics {
6
+ /**
7
+ * Returns the offset given an index.
8
+ */
9
+ getColOffset: ItemSizer;
10
+ /**
11
+ * Returns the index given an offset.
12
+ */
13
+ getColIndex: ItemSizer;
14
+ /**
15
+ * Returns the offset given an index.
16
+ */
17
+ getRowOffset: ItemSizer;
18
+ /**
19
+ * Returns the index given an offset.
20
+ */
21
+ getRowIndex: ItemSizer;
22
+ }
23
+ //# sourceMappingURL=Types.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from './Types';
2
+ export * from './useGridMetrics';
3
+ //# sourceMappingURL=index.d.ts.map