@sheetxl/grid-react 0.7.28-beta.4 → 0.8.0-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 (35) hide show
  1. package/browser/cjs/index.cjs +3 -3
  2. package/browser/esm/index.mjs +2052 -1487
  3. package/licenses/dependencies.txt +35 -1
  4. package/node/esm/index.mjs +1662 -1108
  5. package/package.json +3 -3
  6. package/types/src/cell/CellRenderer.d.ts +14 -3
  7. package/types/src/editor/DefaultEditable.d.ts +9 -0
  8. package/types/src/editor/EditSession.d.ts +6 -0
  9. package/types/src/editor/EditState.d.ts +6 -0
  10. package/types/src/editor/EditorInput.d.ts +2 -1
  11. package/types/src/editor/useEditorKeymap.d.ts +11 -0
  12. package/types/src/header/useHeaderSelectionPointer.d.ts +6 -2
  13. package/types/src/layer/GridFloatPanel.d.ts +18 -0
  14. package/types/src/layer/HoverTipLayer.d.ts +20 -47
  15. package/types/src/layer/RangeFloatLayer.d.ts +146 -0
  16. package/types/src/layer/StickyAnchor.d.ts +2 -4
  17. package/types/src/layer/StickyFloat.d.ts +4 -1
  18. package/types/src/layer/index.d.ts +3 -1
  19. package/types/src/layer/useGridAnchor.d.ts +23 -6
  20. package/types/src/layer/useRangeHover.d.ts +5 -4
  21. package/types/src/list/CheckboxList.d.ts +11 -0
  22. package/types/src/list/List.d.ts +14 -5
  23. package/types/src/list/ListSearch.d.ts +31 -0
  24. package/types/src/list/ListSurface.d.ts +93 -0
  25. package/types/src/list/PickList.d.ts +71 -0
  26. package/types/src/list/_measureText.d.ts +14 -0
  27. package/types/src/list/index.d.ts +3 -0
  28. package/types/src/range/DragHandle.d.ts +14 -0
  29. package/types/src/range/Draggable.d.ts +111 -6
  30. package/types/src/range/FillHandle.d.ts +28 -1
  31. package/types/src/range/RangeInsertBar.d.ts +91 -0
  32. package/types/src/range/index.d.ts +2 -0
  33. package/types/src/selection/SelectionLayer.d.ts +91 -9
  34. package/types/src/selection/SelectionStyle.d.ts +30 -0
  35. package/types/src/selection/useDragCells.d.ts +36 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sheetxl/grid-react",
3
3
  "description": "Grid React - Advanced grid component for React",
4
- "version": "0.7.28-beta.4",
4
+ "version": "0.8.0-beta.2",
5
5
  "license": "SEE LICENSE IN <LICENSE>",
6
6
  "type": "module",
7
7
  "exports": {
@@ -42,8 +42,8 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@floating-ui/react-dom": "^2.1.9",
45
- "@sheetxl/utils": "^0.7.28-beta.4",
46
- "@sheetxl/utils-react": "^0.7.28-beta.4",
45
+ "@sheetxl/utils": "^0.8.0-beta.2",
46
+ "@sheetxl/utils-react": "^0.8.0-beta.2",
47
47
  "clsx": "^2.1.1",
48
48
  "react-merge-refs": "^3.0.2",
49
49
  "react-use": "^17.6.1",
@@ -1,14 +1,25 @@
1
1
  import { Bounds, CellCoords, RangeCoords } from '@sheetxl/utils';
2
- import { IGridPane } from '../surface';
3
2
  export interface CellRendererProps<T = any> extends React.HTMLAttributes<HTMLElement>, React.Attributes {
4
3
  /**
5
4
  * A value to render.
6
5
  */
7
6
  value: T;
8
7
  /**
9
- * The view that is rendering a collection of cells.
8
+ * The zoom {@link CellRendererProps.bounds} were measured at.
9
+ *
10
+ * @defaultValue 1
11
+ *
12
+ * @remarks
13
+ * A renderer is handed the zoom, never the pane it came from: everything it needs to draw one cell
14
+ * is data (the value, the bounds, the scale), so it stays a pure function of its props and can be
15
+ * rendered anywhere a caller can supply bounds — a preview, a tooltip, a cell drawn outside its own
16
+ * column. A renderer that genuinely needs the live view (hover, scroll) reads it from context with
17
+ * `useGridPane`.
18
+ *
19
+ * The bounds already have the zoom applied; this is for what CSS pixels do not carry — an unscaled
20
+ * inset, a glyph size, a stroke width the renderer picks itself.
10
21
  */
11
- pane: IGridPane;
22
+ zoom?: number;
12
23
  /**
13
24
  * The range bounds relative to the current viewport and zoom.
14
25
  */
@@ -40,6 +40,12 @@ export interface IEditable<C, D = string, A = EditCommitArgs> {
40
40
  getSession(): EditSession<C, D, A> | null;
41
41
  /** Subscribe to session changes (start/stop only). Returns an unsubscribe. */
42
42
  subscribe(listener: () => void): () => void;
43
+ /**
44
+ * Subscribe to commits the model REFUSED by throwing (an expected refusal such as "You can't change
45
+ * part of an array", or a real fault). The edit stays open either way; this is the seam through
46
+ * which the reason reaches a host that can show it. Returns an unsubscribe.
47
+ */
48
+ subscribeCommitError(listener: (error: unknown) => void): () => void;
43
49
  /** Begin an edit on `coords`. Returns `false` (and does nothing) if the cell is not editable. */
44
50
  startEdit(coords: CellCoords, options?: StartEditOptions<D>): boolean;
45
51
  /** Commit the current edit. On success clears the session; returns whether it succeeded. */
@@ -100,12 +106,15 @@ export declare class DefaultEditable<C, D = string, A = EditCommitArgs> implemen
100
106
  private readonly unsubscribeAnchor;
101
107
  private current;
102
108
  private readonly listeners;
109
+ private readonly commitErrorListeners;
103
110
  constructor(anchor: ICellAnchor, createEdit: CreateEdit<C, D, A>, options?: DefaultEditableOptions);
104
111
  private onAnchorChange;
105
112
  /** @inheritdoc IEditable.getAnchor */
106
113
  getAnchor(): ICellAnchor;
107
114
  /** @inheritdoc IEditable.subscribe */
108
115
  subscribe(listener: () => void): () => void;
116
+ /** @inheritdoc IEditable.subscribeCommitError */
117
+ subscribeCommitError(listener: (error: unknown) => void): () => void;
109
118
  /** @inheritdoc IEditable.getSession */
110
119
  getSession(): EditSession<C, D, A> | null;
111
120
  /** @inheritdoc IEditable.startEdit */
@@ -49,6 +49,11 @@ export interface EditModel<C, D = string, A = EditCommitArgs> {
49
49
  export interface EditSessionInit<C, D = string, A = EditCommitArgs> extends EditModel<C, D, A> {
50
50
  /** The cell being edited. */
51
51
  coords: CellCoords;
52
+ /**
53
+ * Hears the error a THROWING model write rejected the commit with (see {@link EditModel.commit}).
54
+ * The rejection itself only keeps the edit open; this is how the reason reaches whoever can show it.
55
+ */
56
+ onCommitError?: (error: unknown) => void;
52
57
  }
53
58
  /**
54
59
  * Options applied when an edit begins (`DefaultEditable.startEdit` → the `EditSession` constructor).
@@ -96,6 +101,7 @@ export interface CommitContext<D = string, A = EditCommitArgs> {
96
101
  */
97
102
  export declare class EditSession<C, D = string, A = EditCommitArgs> implements ICellAnchor {
98
103
  private readonly commitFn;
104
+ private readonly onCommitError?;
99
105
  private state;
100
106
  private readonly listeners;
101
107
  private readonly commitListeners;
@@ -20,6 +20,12 @@ export interface EditCommitArgs {
20
20
  * Excel's `Ctrl+Enter`. The active cell does not move (the leave-key does not navigate/traverse).
21
21
  */
22
22
  broadcast?: boolean;
23
+ /**
24
+ * Array-enter the value over the **current selection** — Excel's `Ctrl+Shift+Enter`: one formula
25
+ * at the selection's top-left whose result is fitted to the selection's extent, rather than
26
+ * spilling from the active cell. The active cell does not move.
27
+ */
28
+ arrayFixed?: boolean;
23
29
  }
24
30
  /**
25
31
  * A text selection range within the editor input. Mirrors the `HTMLTextAreaElement` selection API.
@@ -67,7 +67,8 @@ export interface EditorInputProps<C, D = string> {
67
67
  * (a rejected/invalid value) the input keeps the session open and stops the key so the selection
68
68
  * does not move away.
69
69
  *
70
- * `args` carries the commit "signal" — e.g. {@link EditCommitArgs.broadcast} for a Ctrl+Enter fill.
70
+ * `args` carries the commit "signal" — e.g. {@link EditCommitArgs.broadcast} for a Ctrl+Enter fill,
71
+ * {@link EditCommitArgs.arrayFixed} for a Ctrl+Shift+Enter array entry.
71
72
  */
72
73
  onCommit(args?: EditCommitArgs): boolean;
73
74
  /** Cancel/revert the edit. */
@@ -54,6 +54,17 @@ export interface EditorKeymapProps {
54
54
  */
55
55
  setCaret?(caret: number): void;
56
56
  }
57
+ /**
58
+ * The commit "signal" a leave key carries: `Ctrl+Shift+Enter` array-enters over the selection
59
+ * ({@link EditCommitArgs.arrayFixed}), `Ctrl+Enter` fills it ({@link EditCommitArgs.broadcast}), and
60
+ * anything else is a plain commit (`undefined`). Both modified forms keep the active cell put.
61
+ */
62
+ export declare const commitArgsFromKey: (e: {
63
+ which: number;
64
+ }, native: {
65
+ ctrlKey: boolean;
66
+ shiftKey: boolean;
67
+ }) => EditCommitArgs | undefined;
57
68
  /**
58
69
  * The shared spreadsheet editor keymap, as a `<textarea>` `onKeyDown` handler. Both the default and
59
70
  * the cell-aware inputs use it so the interaction contract stays identical:
@@ -55,8 +55,12 @@ export interface HeaderSelectionPointerOptions {
55
55
  onPointerDownInterceptor?: (e: React.PointerEvent, cell: CellCoords) => boolean | void;
56
56
  /** Called once at the start of a pointer gesture (before the selection changes). */
57
57
  onDragStart?: () => void;
58
- /** Called at the end of a pointer gesture. */
59
- onDragEnd?: (e: React.PointerEvent) => void;
58
+ /**
59
+ * Called at the end of a pointer gesture — on release AND on an abort (Escape / OS `pointercancel`),
60
+ * so whatever {@link HeaderSelectionPointerOptions.onDragStart} armed is always disarmed. The event is
61
+ * absent on an abort.
62
+ */
63
+ onDragEnd?: (e?: React.PointerEvent) => void;
60
64
  /** Called on pointer-up, after {@link HeaderSelectionPointerOptions.onDragEnd} (e.g. refocus the grid). */
61
65
  onPointerUp?: (e: React.PointerEvent) => void;
62
66
  }
@@ -0,0 +1,18 @@
1
+ import { default as React } from 'react';
2
+ import { FloatPanelProps } from '@sheetxl/utils-react';
3
+ /**
4
+ * A **float stack panel positioned by the grid**: pass {@link renderGridFloatPanel} as a
5
+ * `useFloatStack` `renderFloatPanel`, and a {@link GridAnchor} (a cell or range) as its `anchor`.
6
+ *
7
+ * @remarks
8
+ * The stack keeps what it does for every popup — the portal, light-dismiss, Escape, focus restore and
9
+ * stacking. The position comes from {@link StickyFloat}, the engine grid buttons and labels use, so the
10
+ * popup moves with its cell as the grid scrolls (which no DOM event reports), flips against the same
11
+ * boundary, and closes once its cell has scrolled out of view.
12
+ *
13
+ * The stack's `propsPopper` `placement` and `offsets` are read as the grid placement and offset.
14
+ */
15
+ export declare const GridFloatPanel: React.FC<FloatPanelProps>;
16
+ /** {@link GridFloatPanel} as a `useFloatStack` `renderFloatPanel`. */
17
+ export declare const renderGridFloatPanel: (props: FloatPanelProps) => React.ReactElement;
18
+ //# sourceMappingURL=GridFloatPanel.d.ts.map
@@ -1,29 +1,25 @@
1
1
  import { default as React } from 'react';
2
2
  import { CellCoords, RangeCoords } from '@sheetxl/utils';
3
3
  import { IGridPane } from '../surface';
4
- import { LayerProps } from './Layer';
5
4
  import { StickyState } from './StickyAnchor';
6
5
  import { HoverTipProps } from './useHoverIntent';
7
- import { GridPlacement, GridWhenOff, GridBoundary } from './useGridAnchor';
8
- /** A hit returned by a {@link HoverTipLayerProps.probe} — the range to anchor on plus arbitrary payload. */
9
- export interface HoverTarget<T = unknown> {
10
- /** The range the tip anchors to (e.g. the cell's own range, or an enclosing table). */
11
- range: RangeCoords;
12
- /**
13
- * Stable identity for dedupe — the tip re-opens only when this changes (a `pointermove` storm within
14
- * one target is a no-op). Defaults to the range's coords.
15
- */
16
- key?: string;
17
- /** Payload passed to the render-prop (the message, the table, …). */
18
- data: T;
19
- }
6
+ import { RangeFloatTarget, RangeFloatLayerProps } from './RangeFloatLayer';
7
+ /**
8
+ * A hit returned by a {@link HoverTipLayerProps.probe} — the range to anchor on plus arbitrary payload.
9
+ *
10
+ * The hover-flavored name for a {@link RangeFloatTarget}; they are the same shape.
11
+ */
12
+ export type HoverTarget<T = unknown> = RangeFloatTarget<T>;
20
13
  /** The context passed to a {@link HoverTipLayerProps} render-prop. */
21
14
  export interface HoverTipRender<T = unknown> {
22
15
  /** The anchored range. */
23
16
  range: RangeCoords;
24
17
  /** The probe payload. */
25
18
  data: T;
26
- /** Spread onto the tip element to keep it open while hovered (`pointer-events:auto` + enter/leave). */
19
+ /**
20
+ * Spread onto the tip element to keep it open while hovered (`pointer-events:auto` + enter/leave).
21
+ * The same object {@link RangeFloatLayer} calls `floatProps`.
22
+ */
27
23
  tipProps: HoverTipProps;
28
24
  /** The resolved sticky state (e.g. `state.scrollIntoView()`). */
29
25
  state: StickyState;
@@ -31,44 +27,21 @@ export interface HoverTipRender<T = unknown> {
31
27
  /**
32
28
  * Props for {@link HoverTipLayer}.
33
29
  */
34
- export interface HoverTipLayerProps<T = unknown> extends Omit<LayerProps, 'children'> {
35
- /** Class for the floated tip container. */
36
- className?: string;
37
- /** Style for the floated tip container. */
38
- style?: React.CSSProperties;
30
+ export interface HoverTipLayerProps<T = unknown> extends Omit<RangeFloatLayerProps<T>, 'activateOn' | 'selection' | 'target' | 'probe' | 'children'> {
39
31
  /** Probe the hovered cell → a {@link HoverTarget} (range + data), or `null` when nothing should show. */
40
32
  probe: (coords: CellCoords, pane: IGridPane) => HoverTarget<T> | null;
41
- /** Which side of the range the tip attaches to. @defaultValue `{ side: 't' }` */
42
- placement?: GridPlacement;
43
- /** Gap (px) perpendicular to the edge. @defaultValue 6 */
44
- offset?: number;
45
- /** Offset (px) along the edge (only `start` / `end` align). @defaultValue 0 */
46
- alignOffset?: number;
47
- /** Dwell (ms) before the tip shows. @defaultValue 300 */
48
- openDelay?: number;
49
- /** Grace (ms) before the tip hides (bridges the gap to the tip). @defaultValue 150 */
50
- closeDelay?: number;
51
- /** What happens when the anchor leaves the data area. @defaultValue 'hide' */
52
- whenOff?: GridWhenOff;
53
- /** Clip / clamp boundary. @defaultValue 'data' */
54
- boundary?: GridBoundary;
55
- /** @defaultValue 1250 */
56
- zIndex?: number;
57
- /**
58
- * Edge-triggered hard dismissal: when this transitions truthy the current tip closes **immediately**
59
- * (bypassing `closeDelay`) — e.g. the anchored cell entered edit mode. It does not hold the tip
60
- * closed; a subsequent hover re-opens it normally.
61
- */
62
- dismiss?: boolean;
63
33
  /** Render the tip. **Spread `tipProps`** on the returned element so hovering it keeps the tip open. */
64
34
  children: (info: HoverTipRender<T>) => React.ReactNode;
65
35
  }
66
36
  /**
67
- * The reusable **hover-tooltip layer**: sweeps the pointer over cells (a {@link Layer} tap +
68
- * `getCellFromPointer`), asks a {@link HoverTipLayerProps.probe} whether the hovered cell has something
69
- * to show, and — via the shared hover intent ({@link useHoverIntent}) — floats a {@link StickyFloat} tip
70
- * (with flip / shift). The probe supplies the anchor range + payload; the render-prop draws the tip and
71
- * keeps it open on hover. This is the shared engine behind `CellErrorLayer`, a table tip, etc.
37
+ * The **hover preset** of {@link RangeFloatLayer} — `activateOn: 'hover'`, with the render-prop's
38
+ * `floatProps` surfaced under its original name `tipProps`.
39
+ *
40
+ * @remarks
41
+ * The engine (pointer tap, hover intent, anchoring, dismissal) lives in {@link RangeFloatLayer}; this
42
+ * is the ergonomic entry point for the pure-tooltip case and the shape `CellErrorLayer` uses. Reach
43
+ * for `RangeFloatLayer` directly when the float is activated by the selection, driven by a gesture, or
44
+ * is something other than a tip.
72
45
  */
73
46
  export declare const HoverTipLayer: <T>(props: HoverTipLayerProps<T>) => React.ReactElement;
74
47
  //# sourceMappingURL=HoverTipLayer.d.ts.map
@@ -0,0 +1,146 @@
1
+ import { default as React } from 'react';
2
+ import { CellCoords, RangeCoords } from '@sheetxl/utils';
3
+ import { IGridPane } from '../surface';
4
+ import { ICellAnchor } from '../selection/ICellAnchor';
5
+ import { LayerProps } from './Layer';
6
+ import { StickyState } from './StickyAnchor';
7
+ import { HoverTipProps } from './useHoverIntent';
8
+ import { GridPlacement, GridWhenOff, GridBoundary } from './useGridAnchor';
9
+ /**
10
+ * How the layer decides WHICH range is floating.
11
+ *
12
+ * - `'hover'` — the pointer, through the shared hover intent (open / close delays, keep-open-on-float).
13
+ * - `'selection'` — the active cell of {@link RangeFloatLayerProps.selection}, with no delay: a resting
14
+ * affordance, not a transient tip.
15
+ *
16
+ * Pass an array to accept EITHER (`['hover','selection']` — not an AND; each one opens the float on its
17
+ * own). Omit entirely and drive {@link RangeFloatLayerProps.target} yourself (gesture-driven action
18
+ * popups — paste options, autofill options).
19
+ */
20
+ export type RangeFloatActivation = 'hover' | 'selection';
21
+ /** A hit from a {@link RangeFloatLayerProps.probe} — the range to anchor on plus arbitrary payload. */
22
+ export interface RangeFloatTarget<T = unknown> {
23
+ /** The range the float anchors to (e.g. the cell's own range, a merge, or an enclosing table). */
24
+ range: RangeCoords;
25
+ /**
26
+ * Stable identity for dedupe — the float re-opens only when this changes (a `pointermove` storm
27
+ * within one target is a no-op). Defaults to the range's coords.
28
+ */
29
+ key?: string;
30
+ /** Payload passed to the render-prop (the message, the validation, the table, …). */
31
+ data: T;
32
+ /**
33
+ * Where this target's float attaches, for a layer whose targets do not all belong on one side.
34
+ * Defaults to the layer's {@link RangeFloatLayerProps.placement}.
35
+ */
36
+ placement?: GridPlacement;
37
+ }
38
+ /** The context passed to a {@link RangeFloatLayerProps} render-prop. */
39
+ export interface RangeFloatRender<T = unknown> {
40
+ /** The anchored range. */
41
+ range: RangeCoords;
42
+ /** The probe payload. */
43
+ data: T;
44
+ /**
45
+ * Spread onto the floated element. Supplies `pointer-events: auto` (a layer's box never takes the
46
+ * pointer, so content must opt back in) and — under `'hover'` — the enter/leave handlers that keep
47
+ * the float open while the pointer is on it.
48
+ */
49
+ floatProps: HoverTipProps;
50
+ /** The resolved sticky state (e.g. `state.scrollIntoView()`, `state.slid`). */
51
+ state: StickyState;
52
+ }
53
+ /**
54
+ * Props for {@link RangeFloatLayer}.
55
+ */
56
+ export interface RangeFloatLayerProps<T = unknown> extends Omit<LayerProps, 'children'> {
57
+ /** Class for the floated container. */
58
+ className?: string;
59
+ /** Style for the floated container. */
60
+ style?: React.CSSProperties;
61
+ /**
62
+ * What makes a float appear. Omit to drive {@link target} yourself.
63
+ * @defaultValue none (controlled)
64
+ */
65
+ activateOn?: RangeFloatActivation | readonly RangeFloatActivation[];
66
+ /**
67
+ * Identity for the hover state this layer brokers across its per-pane copies. Defaults to the
68
+ * layer's z-slot, which is already unique per grid child — pass one only when a SINGLE grid child
69
+ * hosts more than one `RangeFloatLayer` (they would otherwise share a cell and fight).
70
+ */
71
+ name?: string;
72
+ /**
73
+ * Ask whether a cell has something to float — the anchor range + payload, or `null` for nothing.
74
+ * Called with the hovered cell (`'hover'`) and with the selection's active cell (`'selection'`), so
75
+ * one probe serves both. Not needed when {@link target} is controlled.
76
+ */
77
+ probe?: (coords: CellCoords, pane: IGridPane) => RangeFloatTarget<T> | null;
78
+ /**
79
+ * The selection to follow for `activateOn: 'selection'`. An {@link ICellAnchor} rather than a full
80
+ * selection — the layer only ever reads the active cell and subscribes to it.
81
+ */
82
+ selection?: ICellAnchor;
83
+ /**
84
+ * Controlled target: supply it (including `null`) and the layer floats exactly what you say,
85
+ * ignoring `activateOn` / `probe`. The escape hatch for gesture-driven popups whose lifetime is the
86
+ * action's, not the pointer's.
87
+ */
88
+ target?: RangeFloatTarget<T> | null;
89
+ /**
90
+ * Hard close. Under `'hover'` it is **edge-triggered**: the float drops now, bypassing `closeDelay`,
91
+ * and a later hover re-opens normally. Under `'selection'` there is no gesture to re-arm it, so it
92
+ * reads as a **level**: the float stays closed while this holds and returns when it clears (e.g. the
93
+ * anchored cell is in edit mode). Ignored when {@link target} is controlled — the lifetime is yours
94
+ * there, so close it by passing `null`.
95
+ */
96
+ dismiss?: boolean;
97
+ /** Which side of the range the float attaches to. @defaultValue `{ side: 't' }` */
98
+ placement?: GridPlacement;
99
+ /** Gap (px) perpendicular to the edge. @defaultValue 6 */
100
+ offset?: number;
101
+ /** Offset (px) along the edge (only `start` / `end` align). @defaultValue 0 */
102
+ alignOffset?: number;
103
+ /** Dwell (ms) before a hovered float shows. @defaultValue 300 */
104
+ openDelay?: number;
105
+ /** Grace (ms) before a hovered float hides (bridges the gap to the float). @defaultValue 150 */
106
+ closeDelay?: number;
107
+ /** What happens when the anchor leaves the data area. @defaultValue 'hide' */
108
+ whenOff?: GridWhenOff;
109
+ /** Where the float may live. @defaultValue 'data' */
110
+ boundary?: GridBoundary;
111
+ /** @defaultValue 1250 */
112
+ zIndex?: number;
113
+ /** Render the float. **Spread `floatProps`** on the returned element. */
114
+ children: (info: RangeFloatRender<T>) => React.ReactNode;
115
+ }
116
+ /**
117
+ * The **range float layer**: content anchored to a range that portals out of the pane clip. It owns
118
+ * the three things every such float needs — **anchoring** (a {@link StickyFloat} on the probed range),
119
+ * **activation** (what makes it appear) and **dismissal**. What it draws is entirely the consumer's:
120
+ * a tooltip, a button, a toolbar, an action popup.
121
+ *
122
+ * @remarks
123
+ * This is the **float half** of the grid's two mechanisms. A `CellTileDiv` covers everything whose
124
+ * content is a function of the cell alone — clicks and hover styling included — as a cached DOM
125
+ * snapshot. A float is for content that depends on transient UI state (selection, hover, open/closed)
126
+ * or that must leave the cell box. "Only on the active cell" is the expensive predicate: it would
127
+ * dirty a tile per selection move, which is exactly why it belongs here.
128
+ *
129
+ * Activation is pluggable and the rest of the layer is identical across the choices:
130
+ *
131
+ * ```tsx
132
+ * <RangeFloatLayer activateOn="hover" probe={probeError}>
133
+ * {({ data, floatProps }) => <div {...floatProps}>{data.message}</div>}
134
+ * </RangeFloatLayer>
135
+ *
136
+ * <RangeFloatLayer activateOn="selection" selection={selection} probe={probeList}>
137
+ * {({ range, floatProps }) => <Chevron {...floatProps} onPointerDown={() => open(range)} />}
138
+ * </RangeFloatLayer>
139
+ * ```
140
+ *
141
+ * When either can activate it, hover is the transient override and selection is the resting state:
142
+ * while the pointer holds a target the float shows that one, and it falls back to the selection's when
143
+ * the pointer leaves.
144
+ */
145
+ export declare const RangeFloatLayer: <T>(props: RangeFloatLayerProps<T>) => React.ReactElement;
146
+ //# sourceMappingURL=RangeFloatLayer.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { Bounds, Point, AnchorLocation } from '@sheetxl/utils';
3
3
  import { IGridPane } from '../surface';
4
- import { GridAnchor, GridPlacement, GridBoundary, GridWhenOff, GridSlide } from './useGridAnchor';
4
+ import { GridAnchor, GridAnchorReference, GridPlacement, GridBoundary, GridWhenOff, GridSlide } from './useGridAnchor';
5
5
  /**
6
6
  * The state a {@link StickyAnchor} publishes to its child. Carries the resolved `anchorPoint` (+ owner /
7
7
  * clip / slide state) so the child alone decides how to render:
@@ -14,9 +14,7 @@ import { GridAnchor, GridPlacement, GridBoundary, GridWhenOff, GridSlide } from
14
14
  */
15
15
  export interface StickyState {
16
16
  /** Client-space Floating UI virtual reference (stable identity; reads live). */
17
- reference: {
18
- getBoundingClientRect: () => DOMRect;
19
- };
17
+ reference: GridAnchorReference;
20
18
  /** Client-space boundary rect (for `flip`/`shift`/`hide` limiters). */
21
19
  getBoundaryRect: () => DOMRect;
22
20
  /** The placement's anchor pixel (client px). */
@@ -32,7 +32,10 @@ export interface StickyFloatProps {
32
32
  placement?: GridPlacement;
33
33
  /** What happens when the anchor leaves the data area. @defaultValue 'hide' */
34
34
  whenOff?: GridWhenOff;
35
- /** Clip / clamp boundary. @defaultValue 'data' */
35
+ /**
36
+ * Where the float may LIVE — one rect, resolved by {@link GridBoundary}: it is both the anchor's
37
+ * clip / clamp boundary and the rect `flip` / `shift` collide against. @defaultValue 'data'
38
+ */
36
39
  boundary?: GridBoundary;
37
40
  /** Px overlap below which a geometried anchor reads as clipped. @defaultValue 0 */
38
41
  minVisible?: number;
@@ -1,11 +1,13 @@
1
1
  export * from './LayerTypes';
2
2
  export * from './Layer';
3
3
  export * from './VirtualDiv';
4
- export type { GridAnchor, GridPlacement, GridWhenOff, GridBoundary, GridFloatSide, GridAlign, GridSlide } from './useGridAnchor';
4
+ export type { GridAnchor, GridAnchorReference, GridPlacement, GridWhenOff, GridBoundary, GridFloatSide, GridAlign, GridSlide } from './useGridAnchor';
5
5
  export * from './StickyAnchor';
6
6
  export * from './StickyFloat';
7
+ export * from './GridFloatPanel';
7
8
  export * from './useHoverIntent';
8
9
  export * from './useRangeHover';
10
+ export * from './RangeFloatLayer';
9
11
  export * from './HoverTipLayer';
10
12
  export * from './RangePrompt';
11
13
  //# sourceMappingURL=index.d.ts.map
@@ -60,15 +60,18 @@ export type GridAnchor = {
60
60
  point: Point;
61
61
  };
62
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.
63
+ * The clip / clamp boundary — a **rect fed to the positioning math**, NOT a DOM parent. One prop, one
64
+ * meaning: *where may this thing live*. It governs the anchor's clip / clamp AND (via `StickyFloat`)
65
+ * where `flip` / `shift` may land the float. This is how "don't slide under the headers" is expressed:
66
+ * clamp to the data viewport.
65
67
  *
66
68
  * - `'data'` — the union of the panes' cell areas (headers excluded). Default.
67
69
  * - `'pane'` — just the resolving pane's cell area.
68
70
  * - `'grid'` — currently the same as `'data'` (until a surface/header rect is threaded).
71
+ * - `'viewport'` — the browser viewport: the opt-out, for a float that may cover grid chrome.
69
72
  * - a {@link Bounds} — an explicit **client-px** rect.
70
73
  */
71
- export type GridBoundary = 'data' | 'pane' | 'grid' | Bounds;
74
+ export type GridBoundary = 'data' | 'pane' | 'grid' | 'viewport' | Bounds;
72
75
  /** A `DOMRect`-shaped plain object (works in Floating UI and in Node tests without a DOM). */
73
76
  export interface GridAnchorRect {
74
77
  x: number;
@@ -106,15 +109,29 @@ export interface GridAnchorResolved {
106
109
  */
107
110
  outOfView: boolean;
108
111
  }
112
+ /**
113
+ * The Floating UI **virtual reference** for a grid anchor. Stable identity (so `autoUpdate` subscribes
114
+ * once), but both members read **live**.
115
+ */
116
+ export interface GridAnchorReference {
117
+ /** The anchor's client rect, read live on every call (`autoUpdate` calls it between React renders). */
118
+ getBoundingClientRect: () => DOMRect;
119
+ /**
120
+ * The pane element. `autoUpdate` unwraps a virtual reference to *this* element and gates **every**
121
+ * DOM-side trigger on it — ancestor `scroll`, the `observeMove` layout-shift detector, element resize.
122
+ * Without it only the window `resize` listener survives, which is why the float used to need a 60fps
123
+ * `animationFrame` poll. A **getter**: the pane element only exists after mount, and resolving it
124
+ * lazily keeps this object's identity stable.
125
+ */
126
+ readonly contextElement?: Element;
127
+ }
109
128
  /** The hook result: stable live accessors + render-time snapshot values. */
110
129
  export interface UseGridAnchorResult {
111
130
  /**
112
131
  * A Floating UI **virtual reference** with **stable identity** — its `getBoundingClientRect` reads
113
132
  * the anchor **live** on every call (Floating UI's `autoUpdate` invokes it between React renders).
114
133
  */
115
- reference: {
116
- getBoundingClientRect: () => DOMRect;
117
- };
134
+ reference: GridAnchorReference;
118
135
  /** Live accessor for the boundary rect (for `shift`/`flip`/`hide` limiters). */
119
136
  getBoundaryRect: () => DOMRect;
120
137
  /** Render-time snapshot: anchor outside the boundary. Updates each scroll frame (pane re-render). */
@@ -26,10 +26,11 @@ export interface UseRangeHoverResult {
26
26
  *
27
27
  * @remarks
28
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).
29
+ * a {@link Layer} tap); it never consumes the event. Because a grid child renders per pane, the pointer
30
+ * is seen by whichever pane it is over while the tip renders on the pane owning the anchor — which for
31
+ * a range straddling a freeze is a DIFFERENT pane. The aimed target is therefore brokered through
32
+ * {@link useSurfaceState}, so every pane copy runs its intent off the same signal and the owner is
33
+ * armed no matter where the pointer is.
33
34
  */
34
35
  export declare const useRangeHover: (range: RangeCoords, options?: UseHoverIntentOptions) => UseRangeHoverResult;
35
36
  //# sourceMappingURL=useRangeHover.d.ts.map
@@ -1,7 +1,14 @@
1
1
  import { default as React } from 'react';
2
2
  import { ISelection } from '../selection';
3
+ import { ListProps } from './List';
3
4
  export interface CheckboxListAttributes {
4
5
  isCheckboxList: () => true;
6
+ /** The highlighted row, or `-1` when the list is empty. */
7
+ getHighlightIndex: () => number;
8
+ /** Move the highlight `delta` rows (clamped to the list) and bring it into view. */
9
+ moveHighlight: (delta: number) => void;
10
+ /** Toggle the selected rows, as Space does: the anchor's opposite, applied to every one of them. */
11
+ toggleHighlighted: () => void;
5
12
  }
6
13
  export interface ICheckboxListElement extends HTMLDivElement, CheckboxListAttributes {
7
14
  }
@@ -41,6 +48,10 @@ export interface CheckboxListProps extends React.HTMLAttributes<HTMLDivElement>
41
48
  selection?: ISelection;
42
49
  /** Content cache-bust; bump when checked state changes without `isChecked`'s identity changing. */
43
50
  cacheToken?: unknown;
51
+ /** See `ListSurfaceProps.autoFocus`. */
52
+ autoFocus?: boolean;
53
+ /** See `ListSurfaceProps.viewport`. */
54
+ viewport?: ListProps['viewport'];
44
55
  /** Ref for the Element. */
45
56
  ref?: React.Ref<ICheckboxListElement>;
46
57
  }
@@ -1,5 +1,6 @@
1
1
  import { default as React } from 'react';
2
2
  import { ISelection } from '../selection';
3
+ import { ListSurface } from './ListSurface';
3
4
  export interface ListAttributes {
4
5
  isList: () => true;
5
6
  }
@@ -36,20 +37,28 @@ export interface ListProps extends React.HTMLAttributes<HTMLDivElement> {
36
37
  * *rendered* content changed without `renderItem`'s identity changing.
37
38
  */
38
39
  cacheToken?: unknown;
40
+ /** See `ListSurfaceProps.scrollTo`. */
41
+ scrollTo?: {
42
+ row: number;
43
+ } | null;
44
+ /** See `ListSurfaceProps.autoFocus`. */
45
+ autoFocus?: boolean;
46
+ /** See `ListSurfaceProps.viewport`. */
47
+ viewport?: React.ComponentProps<typeof ListSurface>['viewport'];
39
48
  /** Ref for the Element. */
40
49
  ref?: React.Ref<IListElement>;
41
50
  }
42
51
  /**
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).
52
+ * A virtualized **list** of arbitrary React content, built on {@link ListSurface} — which is the
53
+ * grid's own machinery (a `CellLayer` for the items and a `SelectionLayer` for the active-row
54
+ * highlight + Arrow keys) dressed as a list.
46
55
  *
47
56
  * @remarks
48
57
  * The List's whole state is its {@link ListProps.selection}; it has no per-item callbacks and no notion
49
58
  * of "checked" or "toggle". Pointer selection is the `SelectionLayer`'s job (it hit-tests the pointer),
50
59
  * 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.
60
+ * See {@link CheckboxList} for the checkbox-list specialization and {@link PickList} for the
61
+ * single-choice text list.
53
62
  */
54
63
  export declare const List: React.ForwardRefExoticComponent<Omit<ListProps, "ref"> & React.RefAttributes<IListElement>>;
55
64
  //# sourceMappingURL=List.d.ts.map