@sheetxl/grid-react 0.7.27 → 0.7.28-beta.2

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,67 @@
1
+ import { ItemSizer, IGridMetrics } from './Types';
2
+ /**
3
+ * One axis (rows OR columns) of the grid: maps an item index ⇄ its pixel offset.
4
+ *
5
+ * @remarks
6
+ * The public {@link IGridMetrics} is exactly four of these (`get(Col|Row)(Offset|Index)`); an axis is
7
+ * the per-direction half. `getOffset` returns the LEADING edge of an item; `getIndex` is its inverse
8
+ * (the item whose span contains a pixel offset). Both clamp gracefully outside the grid: a negative
9
+ * index/offset → `0`, an offset at/past the total extent → `count` (one past the last) — on BOTH the
10
+ * uniform and variable paths. On a run of zero-size (hidden) items — an equal-offset plateau —
11
+ * `getIndex` deterministically resolves to the LAST index of the run: the following sized item, the
12
+ * one whose span actually contains the pixel (a zero-size span contains nothing).
13
+ */
14
+ export interface Axis {
15
+ /** Leading-edge pixel offset of `index` (0 for `index <= 0`). */
16
+ getOffset: ItemSizer;
17
+ /** Item index whose span contains pixel `offset` (0 at/under the origin, clamped to `count`). */
18
+ getIndex: ItemSizer;
19
+ }
20
+ /**
21
+ * Build an {@link Axis} from a fixed size or a per-index sizer.
22
+ *
23
+ * - **Uniform** (a `number`): closed-form arithmetic — no allocation, no cache, O(1) both ways. This
24
+ * is the overwhelmingly common case (every default grid, every fixed row height) and the supported
25
+ * large-grid mode.
26
+ * - **Variable** (an {@link ItemSizer}): offsets are MEASURED lazily and memoized in dense arrays
27
+ * (indices fill sequentially from 0). The *search* over the already-measured prefix is O(log n)
28
+ * (binary search, with an exponential probe to bracket a target past it) — but **measurement is
29
+ * linear and cumulative**: a jump to index N measures and permanently caches every offset `0..N`
30
+ * (O(N) time & memory on first touch; a far jump into a 1M-item axis walks ~1M sizer calls). This
31
+ * path is the convenience tier for human-scale variable axes. Grids that need large *and* variable
32
+ * don't use it — they pass a prebuilt {@link IGridMetrics} (e.g. the SDK's run-length metrics)
33
+ * instead of a sizer. (GRID-CLEAN F1: documented as-is by design; no sublinear tree here.)
34
+ *
35
+ * Exported for tests (uniform/variable edge parity — GRID-CLEAN B4/B5); not part of the package API.
36
+ */
37
+ export declare const createAxis: (count: number, size: ItemSizer | number) => Axis;
38
+ export interface UseGridMetricOptions {
39
+ /**
40
+ * Number of columns in the grid.
41
+ */
42
+ columnCount: number;
43
+ /**
44
+ * Number of rows in the grid.
45
+ */
46
+ rowCount: number;
47
+ /**
48
+ * Row height: a fixed number (uniform) or a `(index) => number` sizer. Defaults to `20`.
49
+ */
50
+ getRowHeight?: ItemSizer | number;
51
+ /**
52
+ * Column width: a fixed number (uniform) or a `(index) => number` sizer. Defaults to `75`.
53
+ */
54
+ getColumnWidth?: ItemSizer | number;
55
+ }
56
+ /**
57
+ * Build an {@link IGridMetrics} (the index ⇄ offset mapping the surface needs) from a row/column
58
+ * count plus fixed sizes or per-index sizers.
59
+ *
60
+ * @remarks
61
+ * The returned object's IDENTITY changes only when the geometry inputs do (count or sizer) — never on
62
+ * scroll — which is exactly the signal the surface keys its pane recreation on. A `null` `props`
63
+ * yields `null` (the engine then falls back to its own default metrics). `IGridMetrics` is the entire
64
+ * consumed contract; nothing else is exposed.
65
+ */
66
+ export declare const useGridMetrics: (props: UseGridMetricOptions | null) => IGridMetrics | null;
67
+ //# sourceMappingURL=useGridMetrics.d.ts.map
@@ -0,0 +1,129 @@
1
+ import { CellCoords, RangeCoords } from '@sheetxl/utils';
2
+ /** Which corner (`tl`/`tr`/`bl`/`br`) or edge-midpoint (`t`/`b`/`l`/`r`) of the range to sit on. */
3
+ export type HandleAnchor = 'tl' | 'tr' | 'bl' | 'br' | 't' | 'b' | 'l' | 'r';
4
+ /**
5
+ * Resolved info passed to {@link DragHandleProps.renderHandle}.
6
+ */
7
+ export interface DragHandleRenderProps {
8
+ /** The corner/edge this handle sits on. */
9
+ anchor: HandleAnchor;
10
+ /** Resolved size (px). */
11
+ size: number;
12
+ /** Resolved cursor (anchor-derived unless overridden). */
13
+ cursor: string;
14
+ /** Resolved background color — handy as a theme color for custom marks. */
15
+ background: string;
16
+ /** Whether the pointer is currently over the handle (for a hover affordance). */
17
+ hovered: boolean;
18
+ /**
19
+ * **Spread onto your handle element** — this wires placement, pointer-capture, cursor, and the
20
+ * drag events. Without it the handle won't drag. Only pointer-DOWN is on the element; the drag's
21
+ * move/up are attached to `window` for the gesture's duration (so it survives the handle
22
+ * repositioning under an edge auto-scroll).
23
+ */
24
+ handleProps: {
25
+ onPointerDown: (e: React.PointerEvent) => void;
26
+ onPointerEnter?: (e: React.PointerEvent) => void;
27
+ onPointerLeave?: (e: React.PointerEvent) => void;
28
+ style: React.CSSProperties;
29
+ };
30
+ }
31
+ /**
32
+ * Props for {@link DragHandle}.
33
+ */
34
+ export interface DragHandleProps {
35
+ /** The range the handle is anchored to; it sits on the {@link DragHandleProps.anchor} corner. */
36
+ range: RangeCoords;
37
+ /** Which corner/edge of `range` to position on. */
38
+ anchor: HandleAnchor;
39
+ /** Visual size (px) of the handle. @defaultValue 8 */
40
+ size?: number;
41
+ /** Cursor while hovering/dragging. @defaultValue an anchor-appropriate resize cursor. */
42
+ cursor?: string;
43
+ /**
44
+ * Cursor to show **only while a drag is in flight** (the pointer-capture target's cursor holds for
45
+ * the whole drag, even off the handle / off-window). Use it to swap the idle affordance for a neutral
46
+ * "manipulating" cursor — e.g. {@link RangeResizer} flips its directional resize cursors to
47
+ * `grabbing` so a corner drag doesn't keep implying you can still resize that way. Omit to keep
48
+ * {@link DragHandleProps.cursor} for the whole gesture (the fill / table-resize handles, which want
49
+ * their crosshair/resize cursor throughout).
50
+ */
51
+ dragCursor?: string;
52
+ /** Default-dot background, and the theme color handed to {@link DragHandleProps.renderHandle}. */
53
+ background?: string;
54
+ /** Drag started; `cell` is under the pointer at press. */
55
+ onDragStart?: (cell: CellCoords, e: React.PointerEvent) => void;
56
+ /** The pointer moved to a new cell during the drag (`from` = press cell, `to` = current). */
57
+ onDragMove?: (from: CellCoords, to: CellCoords, e: React.PointerEvent) => void;
58
+ /** Drag ended (`from` = press cell, `to` = last cell). */
59
+ onDragEnd?: (from: CellCoords, to: CellCoords, e: React.PointerEvent) => void;
60
+ /**
61
+ * The drag was **aborted** — the user pressed Escape, or the OS cancelled the pointer (`pointercancel`).
62
+ * Fires INSTEAD of {@link DragHandleProps.onDragEnd} (nothing is committed). Use it to tear down any
63
+ * in-flight preview a flavor set in `onDragStart`/`onDragMove` (e.g. the fill's dashed preview) without
64
+ * applying it. Event-less — an Escape abort has no pointer event.
65
+ */
66
+ onDragCancel?: () => void;
67
+ /** The pointer entered (`true`) / left (`false`) the handle — for a hover affordance on the host. */
68
+ onHoverChange?: (hovered: boolean) => void;
69
+ /**
70
+ * Style merged onto the default dot **only while hovered** — e.g. {@link RangeResizer} recolors
71
+ * the thumb's border from the window color to the stroke color, so it appears to "grow" on hover
72
+ * (Excel-style). Use the same shorthand property as the base `style` (e.g. `border`) to avoid a
73
+ * React shorthand/longhand warning. Ignored for headless `renderHandle` consumers (they get
74
+ * {@link DragHandleRenderProps.hovered} to wire their own).
75
+ */
76
+ hoverStyle?: React.CSSProperties;
77
+ /**
78
+ * Glide the handle to its new position (transition `left`/`top`) when `range` changes — pair with
79
+ * {@link RangeRect}'s `animatedTransition` so the corners track a move-drag's gliding outline
80
+ * instead of jumping. @defaultValue false
81
+ */
82
+ animatedTransition?: boolean;
83
+ /**
84
+ * Pin the handle centre INSIDE the pane's viewport instead of letting it sit at the range's true
85
+ * anchored corner. When that corner is far off-grid — e.g. a whole-row / whole-column fill selection
86
+ * whose corner is at the last column / row — this keeps the handle visible and reachable. When the
87
+ * range no longer intersects this pane at all, the handle is not rendered.
88
+ *
89
+ * Clamp a **single axis** to track the range along the other: `'y'` pins the vertical position but
90
+ * lets `x` follow the range (a whole-column `tr` handle — pinned to the top, scrolls with the column);
91
+ * `'x'` pins the horizontal but lets `y` follow (a whole-row `bl` handle — pinned to the left, scrolls
92
+ * with the row). `true` clamps both. @defaultValue false
93
+ */
94
+ clampToView?: boolean | 'x' | 'y';
95
+ /**
96
+ * Render the handle **element** yourself (headless): return your element with `handleProps` spread
97
+ * onto it — that wires placement, pointer-capture, cursor, and events. Replaces the default dot and
98
+ * `children`. Use it for a configured look — e.g. an Excel table-resize tick.
99
+ */
100
+ renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
101
+ /** Static content inside the default dot. Ignored when `renderHandle` is set. */
102
+ children?: React.ReactNode;
103
+ className?: string;
104
+ style?: React.CSSProperties;
105
+ /**
106
+ * Keep the currently-focused element focused across the drag (`preventDefault` on pointer-down, which
107
+ * suppresses the compat `mousedown` that would otherwise move focus to the grid's `tabIndex` Stage).
108
+ * Used by the reference-range picker so dragging a handle doesn't blur the formula editor's textarea —
109
+ * it preserves whatever had focus rather than stealing it. @defaultValue false
110
+ */
111
+ preserveFocus?: boolean;
112
+ }
113
+ /**
114
+ * A generic, draggable handle anchored to a corner/edge of a grid range. It owns only the **gesture
115
+ * mechanics** — placement, pointer capture, and cell hit-testing with edge auto-scroll — and reports
116
+ * the drag in **cell terms** (`from` → `to`). It holds no range math: flavors (fill, table-resize,
117
+ * touch-extend) own the cell→model conversion and any preview.
118
+ *
119
+ * @remarks
120
+ * The handle centre is placed with the shared {@link ReactUtils.edgeCenter} rule — the same one
121
+ * gridlines and cell borders use — so it lands on the boundary device-crisply and flush with the
122
+ * selection outline. It's a real DOM element (not a Stage responder): it captures the pointer on its
123
+ * own element so move/up survive leaving the handle, and it `stopPropagation`s pointer-down so the
124
+ * selection layer underneath does not also begin a gesture. Provide
125
+ * {@link DragHandleProps.renderHandle} to render the element yourself (spread the supplied
126
+ * `handleProps`).
127
+ */
128
+ export declare const DragHandle: React.FC<DragHandleProps>;
129
+ //# sourceMappingURL=DragHandle.d.ts.map
@@ -0,0 +1,86 @@
1
+ import { RangeCoords } from '@sheetxl/utils';
2
+ /**
3
+ * Props for {@link Draggable}.
4
+ */
5
+ export interface DraggableProps {
6
+ /**
7
+ * The range the wrapped content occupies (controlled). The drag's "from"; the result range comes out
8
+ * via `onDrag*`.
9
+ */
10
+ range?: RangeCoords;
11
+ /**
12
+ * The content to make movable — any visual ({@link RangeRect}, `RangeResizer`, …), rendered at
13
+ * `range` and grabbable. `Draggable` draws nothing itself; the visuals are all composed children.
14
+ */
15
+ children?: React.ReactNode;
16
+ /**
17
+ * Where you can grab to move: the whole rectangle (`entire`, the default) or only its **border ring**
18
+ * (`edges` — interior clicks fall through, for a selection whose interior is for selecting/editing).
19
+ *
20
+ * @defaultValue 'entire'
21
+ */
22
+ grip?: 'entire' | 'edges';
23
+ /**
24
+ * Grab slack in device px. For `grip: 'edges'` it's the border-ring hit thickness (centered on the
25
+ * border, so half reaches outside the range). For `grip: 'entire'` the whole rectangle is grabbable
26
+ * and this pads the hit area OUTWARD by the same amount, so you can grab just past the edge too.
27
+ *
28
+ * @defaultValue 10
29
+ */
30
+ hitSize?: number;
31
+ /**
32
+ * The pointer entered (`true`) or left (`false`) the grip's hit area — the border ring for
33
+ * `grip: 'edges'`, the whole (outward-padded) rectangle for `grip: 'entire'`. Use it to render a
34
+ * hover affordance on the composed visual, e.g. Excel thickening the move border on hover. Fires only
35
+ * on a real enter/leave, not on every move.
36
+ */
37
+ onHoverChange?: (hovered: boolean) => void;
38
+ /**
39
+ * The drag began (`true`) / ended (`false`) — fires on every press/release, even a press with no
40
+ * movement (unlike `onDragEnd`, which only fires on an actual translation). Separate from
41
+ * {@link DraggableProps.onHoverChange} because pointer-capture suppresses the grip's hover events
42
+ * mid-drag; use this to keep a drag affordance (e.g. the thickened move border) lit for the whole drag.
43
+ */
44
+ onDragStateChange?: (dragging: boolean) => void;
45
+ /** Drag started; `range` is the original. */
46
+ onDragStart?: (range: RangeCoords, e: React.PointerEvent) => void;
47
+ /**
48
+ * The translated range during the drag. Wire it to update `range` (the content follows the pointer —
49
+ * "live"), or to position a composed ghost preview. `Draggable` itself stays silent.
50
+ */
51
+ onDragMove?: (range: RangeCoords, e: React.PointerEvent) => void;
52
+ /** Drag released: commit the translated range. */
53
+ onDragEnd?: (range: RangeCoords, e: React.PointerEvent) => void;
54
+ /**
55
+ * Render a **ghost** at the in-flight translated range while a drag is in progress (nothing is rendered
56
+ * when idle). `Draggable` owns the preview state internally, so the consumer doesn't have to stash it
57
+ * — return e.g. a faded {@link RangeRect} to trail the pointer while the original stays put (the
58
+ * cut-move pattern). For a "live" feel (content follows the pointer) wire `onDragMove → range` instead.
59
+ */
60
+ renderMove?: (range: RangeCoords) => React.ReactNode;
61
+ /**
62
+ * Keep the current focus across a move drag (`preventDefault` on pointer-down, suppressing the compat
63
+ * `mousedown` that would move focus to the grid's `tabIndex` Stage). Used by the reference-range picker
64
+ * so dragging a range doesn't blur the formula editor's textarea. @defaultValue false
65
+ */
66
+ preserveFocus?: boolean;
67
+ }
68
+ /**
69
+ * A **"make draggable" wrapper**: it makes its `children` movable (translate — same size, new location)
70
+ * and reports the translated range via `onDrag*`. It owns ONLY the gesture (pointer-capture +
71
+ * cell-delta translate + edge auto-scroll); it draws nothing. The consumer composes the visuals and
72
+ * decides what a drag means:
73
+ *
74
+ * - **Live** — `onDragMove` → update `range`; the wrapped child follows the pointer (e.g. a picker, or
75
+ * any draggable `RangeRect`). This is the default feel.
76
+ * - **Ghost** — pass `renderMove`; `Draggable` tracks the in-flight range internally and hands it to
77
+ * your callback to render a second, faded layer, while the original stays put and commits on
78
+ * `onDragEnd` (e.g. a selection's cut-move). No preview state to manage in the consumer.
79
+ *
80
+ * @remarks
81
+ * The grip is the whole rectangle ({@link DraggableProps.grip} = `entire`) by default, or just its
82
+ * border ring (`edges`). Drawn first so child handles (resize corners) layered above stay grabbable;
83
+ * `stopPropagation` keeps a layer beneath from also reacting.
84
+ */
85
+ export declare const Draggable: React.FC<DraggableProps>;
86
+ //# sourceMappingURL=Draggable.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { default as React } from 'react';
2
+ import { RangeCoords } from '@sheetxl/utils';
3
+ import { VisualSVGProps } from '../svg';
4
+ import { DragHandleRenderProps } from './DragHandle';
5
+ /**
6
+ * Props for {@link FillHandle}. Follows the shared **RangeHandle** contract: a controlled `range`
7
+ * (the fill template/source) in, the fill **destination** range out via `onDrag*`. The destination
8
+ * includes the template; the fill direction is `compare(range, destination)` if a consumer needs it.
9
+ * Extends {@link VisualSVGProps} (like {@link RangeRectProps} / {@link RangeResizerProps}) so the
10
+ * dashed preview's paint props — `stroke`, `strokeWidth`, … — are passed directly; the handle inherits
11
+ * {@link DEFAULT_HANDLE_STYLE} for whatever you don't set, and the handle color follows `stroke`.
12
+ */
13
+ export interface FillHandleProps extends VisualSVGProps {
14
+ /**
15
+ * The template (source) range being filled from. Optional because `SelectionLayer` injects it when
16
+ * `enableFill` is set (or via a `singleLayer` slot); required in practice for standalone use.
17
+ */
18
+ range?: RangeCoords;
19
+ /** Drag started; `range` is the template. */
20
+ onDragStart?: (range: RangeCoords, e: React.PointerEvent) => void;
21
+ /**
22
+ * Adjust the raw destination before it drives the preview + `onDrag*` — e.g. snap the fill edge to a
23
+ * merged-cell boundary so the dashed preview and the committed fill agree (and never land on an
24
+ * illegal interval). Given the template and the pointer-derived destination, return the destination to
25
+ * use. Applied on every move; omit for no snapping.
26
+ */
27
+ snapDestination?: (template: RangeCoords, destination: RangeCoords) => RangeCoords;
28
+ /** Live during the drag: the in-progress destination range (incl. the template). */
29
+ onDragMove?: (range: RangeCoords, e: React.PointerEvent) => void;
30
+ /** Drag released: commit the fill to this destination range. Fires only when there IS a fill. */
31
+ onDragEnd?: (range: RangeCoords, e: React.PointerEvent) => void;
32
+ /**
33
+ * The drag finished — fires on **every** release after a press (unlike {@link FillHandleProps.onDragEnd},
34
+ * which fires only when a fill was drawn) AND on an abort (Escape / OS pointercancel). Use it for
35
+ * teardown that must run regardless — closing a preview tooltip, clearing a drag mode. Event-less on an
36
+ * Escape abort, hence the optional event.
37
+ */
38
+ onDragDone?: (e?: React.PointerEvent) => void;
39
+ /**
40
+ * Render the fill handle **element** yourself (headless) — forwarded to {@link DragHandle.renderHandle}.
41
+ * Replaces the default dot. Use it for a custom fill mark (e.g. the Excel fill square).
42
+ */
43
+ renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
44
+ }
45
+ /**
46
+ * The fill layer: a {@link DragHandle} at a corner of the `range` (bottom-right by default). It owns
47
+ * the handle placement — including the whole-row/column special-casing (pin to bottom-left / top-right
48
+ * and clamp into view) and freeze-pane de-duplication — plus the single-axis fill geometry and the
49
+ * dashed preview, reporting the destination range via the RangeHandle `onDrag*` callbacks. Selection
50
+ * extension + the actual data fill are the consumer's job (via `onDragEnd`) — this layer is
51
+ * selection-agnostic.
52
+ */
53
+ export declare const FillHandle: React.FC<FillHandleProps>;
54
+ //# sourceMappingURL=FillHandle.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { RangeCoords } from '@sheetxl/utils';
2
+ import { VirtualDivProps } from '../layer';
3
+ import { VisualSVGProps } from '../svg';
4
+ /**
5
+ * Props for {@link RangeRect}: a thin range→bounds wrapper that places the {@link RectangleSVG}
6
+ * **shape** at a grid range — it resolves the range to bounds via the pane, culls off-screen,
7
+ * optionally cuts a `hole`, and forwards every visual prop straight through. The SVG sibling of
8
+ * {@link VirtualDiv} (which hosts HTML content at a range).
9
+ */
10
+ export interface RangeRectProps extends Omit<VirtualDivProps, 'fill'>, Omit<VisualSVGProps, 'style'> {
11
+ /**
12
+ * The range to outline / fill. Resolved to absolute bounds via the current pane.
13
+ */
14
+ range: RangeCoords;
15
+ /**
16
+ * A range cut out of the FILL (evenodd hole) — e.g. the active cell in a selection. The range
17
+ * counterpart of {@link RectangleSVG}'s `hole` (which takes resolved bounds); `RangeRect` resolves
18
+ * it against the pane. The hole belongs to THIS range (the consumer decides which range gets it).
19
+ */
20
+ hole?: RangeCoords;
21
+ /**
22
+ * View-pixel slack for the off-screen cull. Defaults to half the stroke width. This is the one
23
+ * concern {@link RectangleSVG} can't express (it has no pane and does no culling).
24
+ */
25
+ clipInsets?: number;
26
+ }
27
+ /**
28
+ * Places one device-aligned {@link RectangleSVG} at a grid range. Resolves the range to bounds via
29
+ * the current pane, culls when off-screen, cuts an optional `hole`, and hands the raw bounds + all
30
+ * visual props to {@link RectangleSVG} (which snaps + paints). It knows about ranges and the pane;
31
+ * the painting/snapping is RectangleSVG's job. A pure decoration — composed from the two primitives
32
+ * (a view-sized, pointer-transparent {@link VirtualDiv} hosting the shape), shipped because it
33
+ * encodes non-obvious geometry.
34
+ */
35
+ export declare const RangeRect: import('react').MemoExoticComponent<(props: RangeRectProps) => import("react").JSX.Element | null>;
36
+ //# sourceMappingURL=RangeRect.d.ts.map
@@ -0,0 +1,68 @@
1
+ import { RangeCoords } from '@sheetxl/utils';
2
+ import { VisualSVGProps } from '../svg';
3
+ import { HandleAnchor, DragHandleRenderProps } from './DragHandle';
4
+ /** The four corner handles (chart series / formula precedent rectangles use these). */
5
+ export declare const RESIZE_ANCHORS_4: readonly HandleAnchor[];
6
+ /** Corners + edge midpoints. */
7
+ export declare const RESIZE_ANCHORS_8: readonly HandleAnchor[];
8
+ /**
9
+ * Props for {@link RangeResizer}. Follows the shared **RangeHandle** contract: a controlled
10
+ * `range` in, the resized range out via `onDrag*`. Extends {@link VisualSVGProps} (like
11
+ * {@link RangeRectProps}) so the outline's paint props — `stroke`, `fill`, `strokeWidth`,
12
+ * `animatedStroke`, `animatedTransition`, … — are passed directly; the handles inherit
13
+ * {@link DEFAULT_HANDLE_STYLE} for whatever you don't set. The grip color follows `stroke`.
14
+ */
15
+ export interface RangeResizerProps extends VisualSVGProps {
16
+ /**
17
+ * The range being resized (controlled). Optional because `SelectionLayer` injects it when used as a
18
+ * `singleLayer` slot; required in practice for standalone use.
19
+ */
20
+ range?: RangeCoords;
21
+ /**
22
+ * Which handles to show. @defaultValue {@link RESIZE_ANCHORS_8}. Pass {@link RESIZE_ANCHORS_4} for
23
+ * corners-only (chart series / formula precedents).
24
+ */
25
+ anchors?: readonly HandleAnchor[];
26
+ /** Drag started; `range` is the original. */
27
+ onDragStart?: (range: RangeCoords, e: React.PointerEvent) => void;
28
+ /** Live during the drag: the in-progress resized range. */
29
+ onDragMove?: (range: RangeCoords, e: React.PointerEvent) => void;
30
+ /** Drag released: commit the resized range. */
31
+ onDragEnd?: (range: RangeCoords, e: React.PointerEvent) => void;
32
+ /**
33
+ * The resize was **aborted** — the user pressed Escape, or the OS cancelled the pointer. Fires INSTEAD
34
+ * of {@link RangeResizerProps.onDragEnd}. The local preview is cleared automatically; because a resize
35
+ * commits live via `onDragMove`, a consumer that mutates its model each move should restore the
36
+ * pre-drag range here (Windows-style abort). Event-less — an Escape abort has no pointer event.
37
+ */
38
+ onDragCancel?: () => void;
39
+ /**
40
+ * The pointer entered (`true`) or left (`false`) a resize grip — aggregated across all handles (only
41
+ * one is ever hovered at a time). Lets a consumer emphasize the outline while a corner is hovered (e.g.
42
+ * Excel thickening the border). Ignored for headless `renderHandle` consumers, which wire their own.
43
+ */
44
+ onHandleHoverChange?: (hovered: boolean) => void;
45
+ /** Handle size (px). @defaultValue 8 */
46
+ size?: number;
47
+ /**
48
+ * Render each corner/edge grip yourself (headless) — forwarded to {@link DragHandle}'s `renderHandle`
49
+ * per anchor (use `DragHandleRenderProps.anchor` to differentiate). Replaces the default grips.
50
+ */
51
+ renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
52
+ /** Keep the current focus across a handle drag (forwarded to {@link DragHandle.preserveFocus}). */
53
+ preserveFocus?: boolean;
54
+ }
55
+ /**
56
+ * A draggable, resizable rectangle: an outlined range with {@link DragHandle} grips on the requested
57
+ * corners/edges — the interaction used to re-scope a **chart series** or a **formula precedent**.
58
+ * Each grip is the same `DragHandle` primitive; this layer supplies the per-anchor edge-move
59
+ * semantics and the live preview, delegating the committed value to
60
+ * {@link RangeResizerProps.onDragEnd}.
61
+ *
62
+ * @remarks
63
+ * Controlled: render with the current `range` and update it from `onDragEnd`. Dragging an edge/corner
64
+ * past its opposite flips the rectangle (Excel-style), bottoming out at a 1x1 — at which point the
65
+ * grip you're holding has effectively become the opposite grip.
66
+ */
67
+ export declare const RangeResizer: React.FC<RangeResizerProps>;
68
+ //# sourceMappingURL=RangeResizer.d.ts.map
@@ -0,0 +1,8 @@
1
+ import { VisualSVGProps } from '../svg';
2
+ /**
3
+ * The flat outline (fill + border, no inner ring) for the resize/fill handle layers
4
+ * (`RangeResizer` / {@link FillHandle}), which draw ONE rectangle. Colors compose from the grid
5
+ * `accent` (same knob as selection); the literals are the fallbacks so a bare grid renders unchanged.
6
+ */
7
+ export declare const DEFAULT_HANDLE_STYLE: VisualSVGProps;
8
+ //# sourceMappingURL=RangeStyle.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * **Range** primitives: the pane-aware {@link RangeRect} (an SVG rectangle at a range) plus the
3
+ * controlled drag handles (move / resize / fill) and the generic {@link DragHandle}. The selection
4
+ * layer composes these; the drag handles in turn share `selection/`'s window-driven cell-drag engine
5
+ * (`useWindowDragCells`) for their gesture mechanics — capture, de-dup, edge auto-scroll, touch, cancel.
6
+ */
7
+ export * from './RangeStyle';
8
+ export * from './RangeRect';
9
+ export * from './DragHandle';
10
+ export * from './Draggable';
11
+ export * from './RangeResizer';
12
+ export * from './FillHandle';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { Direction, RangeAxis, CellCoords, RangeCoords, SelectionCoords } from '@sheetxl/utils';
2
+ import { ISelection } from './ISelection';
3
+ /**
4
+ * Options for {@link SelectionConstructionOptions}.
5
+ */
6
+ export interface SelectionConstructionOptions {
7
+ /** Initial selection. Defaults to a single active cell at `0,0`. */
8
+ initial?: SelectionCoords;
9
+ /**
10
+ * Bounds used to clamp {@link DefaultSelection.navigate}. The dumb tier's stand-in for the sheet's
11
+ * entire range. Defaults to non-negative / unbounded.
12
+ */
13
+ getBounds?: () => RangeCoords;
14
+ }
15
+ /**
16
+ * The default in-memory {@link ISelection} for examples and the basic grid. Its {@link ICellAnchor}
17
+ * anchor is `selection.cell`, so a single instance drives both the selection layer (as `ISelection`)
18
+ * and the editor (as the read-only anchor). The real app adapts an sdk `IRangeSelection` to
19
+ * {@link ISelection} instead.
20
+ *
21
+ * @remarks
22
+ * Dumb on purpose: it has no merge / hidden awareness; navigation just steps one cell, clamped to
23
+ * the {@link SelectionConstructionOptions.getBounds} port. Smart navigation lives in the sdk model.
24
+ * Methods are plain (unbound) — consumers read it via `useBoundSyncExternalStore`, which calls
25
+ * `subscribe`/the snapshot selector on the instance, so no per-method binding is needed.
26
+ */
27
+ export declare class DefaultSelection implements ISelection {
28
+ private _coords;
29
+ private readonly _getBounds;
30
+ private readonly listeners;
31
+ constructor(options?: SelectionConstructionOptions);
32
+ /** @inheritdoc ISelection.getSelection */
33
+ getSelection(): SelectionCoords;
34
+ /** @inheritdoc ISelection.setSelection */
35
+ setSelection(next: SelectionCoords): void;
36
+ /** @inheritdoc ISelection.navigate */
37
+ navigate(direction: Direction, select?: boolean, contentful?: boolean, increment?: number): void;
38
+ /** @inheritdoc ISelection.traverse */
39
+ traverse(axis?: RangeAxis, reverse?: boolean, region?: RangeCoords): void;
40
+ /**
41
+ * The index of the active range, or `-1` when there are no ranges. A `rangeIndex < 0` (or out of
42
+ * bounds) WITH ranges resolves to the LAST range — the same convention `SelectionLayer` renders with,
43
+ * so `modify` and the visuals agree on which range is "active" in that (anomalous) state.
44
+ */
45
+ private _activeIndex;
46
+ /** The active range (see {@link _activeIndex}), or the anchor as a 1x1 range when nothing is selected. */
47
+ private _activeRange;
48
+ /** @inheritdoc ISelection.modify */
49
+ modify(cell: Partial<CellCoords>, pivot?: Partial<CellCoords>): void;
50
+ /** @inheritdoc ISelection.append */
51
+ append(start: CellCoords, end?: CellCoords): void;
52
+ /** @inheritdoc ISelection.deselect */
53
+ deselect(rangeToRemove: RangeCoords): void;
54
+ /** @inheritdoc ICellAnchor.getCoords */
55
+ getCoords(): CellCoords;
56
+ /** @inheritdoc ICellAnchor.subscribe */
57
+ subscribe(listener: () => void): (() => void);
58
+ }
59
+ //# sourceMappingURL=DefaultSelection.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { CellCoords } from '@sheetxl/utils';
2
+ /**
3
+ * An **observable thing located at a cell** — the read+observe anchor the editor follows.
4
+ *
5
+ * @remarks
6
+ * The base of the anchor hierarchy and the decoupling seam between selection and the editor. A
7
+ * consumer given an `ICellAnchor` can read the cell (`getCoords`) and be notified when it changes
8
+ * (`subscribe`), but **cannot move it** — there is no setter, so navigation stays with the selection.
9
+ *
10
+ * Two kinds of thing satisfy it: the selection's **moving** active cell ({@link ISelection} extends
11
+ * this, so a single instance drives both the selection layer and the editor), and a **fixed-cell**
12
+ * thing whose `coords` never move (an `EditSession`, observable on its edit state). The editor reads
13
+ * its anchor through this view (`getCoords`/`subscribe`) and never navigates.
14
+ */
15
+ export interface ICellAnchor {
16
+ /** The cell this thing is located at. Safe in event handlers (always current, never stale). */
17
+ getCoords(): CellCoords;
18
+ /** Subscribe to changes. Returns an unsubscribe. */
19
+ subscribe(listener: () => void): () => void;
20
+ }
21
+ //# sourceMappingURL=ICellAnchor.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { CellCoords, Direction, RangeAxis, RangeCoords, SelectionCoords } from '@sheetxl/utils';
2
+ import { ICellAnchor } from './ICellAnchor';
3
+ /**
4
+ * An **observable store** of a full {@link SelectionCoords} — the active cell plus the selected
5
+ * ranges and the active range index. The SelectionCoords-level extension of {@link ICellAnchor},
6
+ * whose anchor is the active cell.
7
+ *
8
+ * @remarks
9
+ * This is the single source of truth the selection layer renders and the pointer controller writes.
10
+ * It exposes the minimal mutation surface the layer needs — a raw {@link ISelection.setSelection}
11
+ * write plus {@link ISelection.navigate} (the one logical-move intent).
12
+ *
13
+ * Extends {@link ICellAnchor}: the active cell ({@link SelectionCoords.cell}) is the anchor and
14
+ * `subscribe` notifies on any change, so a single instance drives both the selection layer (as
15
+ * `ISelection`) and the editor (as the read-only {@link ICellAnchor}).
16
+ *
17
+ * `getSelection` MUST return a stable reference while unchanged.
18
+ */
19
+ export interface ISelection extends ICellAnchor {
20
+ /** The current selection. Stable ref while unchanged. Safe in event handlers (never stale). */
21
+ getSelection(): SelectionCoords;
22
+ /** Replace the selection. No-op if logically equal to the current value. */
23
+ setSelection(next: SelectionCoords): void;
24
+ /**
25
+ * Move the active cell in `direction`. With `select`, extend the active range instead of moving
26
+ * (the anchor stays fixed). `contentful` jumps to the next data edge (Ctrl+Arrow); `increment`
27
+ * moves that many cells at once (the page step — the caller passes a view-derived amount).
28
+ * Implementation-defined intelligence (bounds, merges, hidden, contentful) — dumb in
29
+ * {@link DefaultSelection}, sheet-backed in the sdk adapter (mirrors the sdk
30
+ * `IRangeSelection.navigate(direction, select?, contentful?, incrementAmount?)`).
31
+ */
32
+ navigate(direction: Direction, select?: boolean, contentful?: boolean, increment?: number): void;
33
+ /**
34
+ * Cycle the **active cell** to the next cell *within the current selection* — the Tab/Enter
35
+ * traversal. Unlike {@link ISelection.navigate} it never changes which ranges are selected (it only
36
+ * moves the active cell, and the active range index): it wraps at a range edge, visits each cell
37
+ * once, and jumps to the next range when stepping off the last corner. On a single cell (no range)
38
+ * it falls back to a one-cell move — unless a `region` is supplied, in which case a bare cursor
39
+ * **wraps within that region** (a table's data range) instead of moving off it. `Row` axis ⇒ Tab
40
+ * (→, reverse = ←); `Column` axis ⇒ Enter (↓, reverse = ↑). Implementation-defined intelligence
41
+ * (merges, hidden) — dumb in {@link DefaultSelection}, sheet-backed in the sdk adapter.
42
+ */
43
+ traverse(axis?: RangeAxis, reverse?: boolean, region?: RangeCoords): void;
44
+ /**
45
+ * Replace the active range with the box spanning `pivot`..`cell`. `pivot` defaults to the current
46
+ * anchor — so a drag or Shift+click passes only `cell`. Partial coords fall back to the active
47
+ * range's bounds (modifying only a row extends rows while keeping columns). The anchor is kept,
48
+ * unless the new range no longer contains it (an explicit off-anchor `pivot`, e.g. a touch grip on
49
+ * the opposite corner, can drag the range off the anchor) — then it snaps to the nearest corner so
50
+ * the anchor always lies within the active range.
51
+ */
52
+ modify(cell: Partial<CellCoords>, pivot?: Partial<CellCoords>): void;
53
+ /**
54
+ * Add a new range spanning `start`..`end` to the selection (the Ctrl/Cmd-click gesture). If there
55
+ * were no ranges, one is first seeded from the current anchor. The appended range becomes active
56
+ * and its start becomes the anchor.
57
+ */
58
+ append(start: CellCoords, end?: CellCoords): void;
59
+ /**
60
+ * Remove `range` from the selection, splitting any intersecting range around it (the
61
+ * Ctrl/Cmd-click-on-selected gesture). The active cell/range is preserved where possible.
62
+ */
63
+ deselect(range: RangeCoords): void;
64
+ }
65
+ //# sourceMappingURL=ISelection.d.ts.map