@sheetxl/grid-react 0.8.0-beta.1 → 0.8.0-beta.11

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 (65) hide show
  1. package/README.md +16 -5
  2. package/browser/esm/index.mjs +3504 -2846
  3. package/licenses/dependencies.txt +0 -208
  4. package/node/esm/index.mjs +3861 -2284
  5. package/package.json +8 -12
  6. package/types/src/cell/CellRenderer.d.ts +14 -3
  7. package/types/src/cell/GridlineLayer.d.ts +39 -1
  8. package/types/src/editor/DefaultEditable.d.ts +25 -6
  9. package/types/src/editor/EditSession.d.ts +34 -5
  10. package/types/src/editor/EditState.d.ts +6 -0
  11. package/types/src/editor/EditorDiv.d.ts +3 -2
  12. package/types/src/editor/EditorInput.d.ts +7 -3
  13. package/types/src/editor/EditorLayer.d.ts +9 -4
  14. package/types/src/editor/EditorStartLayer.d.ts +33 -0
  15. package/types/src/editor/index.d.ts +1 -0
  16. package/types/src/editor/useEditorKeymap.d.ts +18 -2
  17. package/types/src/header/GridFreezeAnchor.d.ts +29 -0
  18. package/types/src/header/HeaderDividerAnchor.d.ts +29 -0
  19. package/types/src/header/HeaderFreezeAnchor.d.ts +37 -0
  20. package/types/src/header/_freezeSnap.d.ts +28 -0
  21. package/types/src/header/_useFreezeGrip.d.ts +28 -0
  22. package/types/src/header/index.d.ts +2 -0
  23. package/types/src/header/useHeaderSelectionPointer.d.ts +10 -6
  24. package/types/src/layer/GestureTip.d.ts +69 -0
  25. package/types/src/layer/GridFloatPanel.d.ts +19 -0
  26. package/types/src/layer/Layer.d.ts +6 -0
  27. package/types/src/layer/RangeFloatLayer.d.ts +143 -51
  28. package/types/src/layer/RangePrompt.d.ts +8 -8
  29. package/types/src/layer/RangeTooltip.d.ts +123 -0
  30. package/types/src/layer/RangeTooltipLayer.d.ts +53 -0
  31. package/types/src/layer/StickyAnchor.d.ts +15 -5
  32. package/types/src/layer/StickyPortal.d.ts +73 -0
  33. package/types/src/layer/TooltipProvider.d.ts +101 -0
  34. package/types/src/layer/index.d.ts +7 -4
  35. package/types/src/layer/useGridAnchor.d.ts +45 -4
  36. package/types/src/layer/useHoverIntent.d.ts +35 -8
  37. package/types/src/list/CheckboxList.d.ts +22 -1
  38. package/types/src/list/List.d.ts +11 -2
  39. package/types/src/list/ListSearch.d.ts +52 -0
  40. package/types/src/list/ListSurface.d.ts +25 -5
  41. package/types/src/list/PickList.d.ts +71 -0
  42. package/types/src/list/_measureText.d.ts +14 -0
  43. package/types/src/list/index.d.ts +2 -0
  44. package/types/src/range/DragHandle.d.ts +19 -2
  45. package/types/src/range/Draggable.d.ts +132 -7
  46. package/types/src/range/FillHandle.d.ts +33 -3
  47. package/types/src/range/RangeInsertBar.d.ts +91 -0
  48. package/types/src/range/index.d.ts +2 -0
  49. package/types/src/selection/SelectionLayer.d.ts +128 -15
  50. package/types/src/selection/SelectionStyle.d.ts +33 -0
  51. package/types/src/selection/SelectionTouchGrips.d.ts +0 -4
  52. package/types/src/selection/_SelectionUtils.d.ts +6 -0
  53. package/types/src/selection/useDragCells.d.ts +65 -9
  54. package/types/src/surface/GridPane.d.ts +2 -0
  55. package/types/src/surface/IGridPane.d.ts +15 -2
  56. package/types/src/surface/SurfaceStateProvider.d.ts +1 -1
  57. package/types/src/surface/getPrimaryPane.d.ts +13 -0
  58. package/types/src/surface/index.d.ts +1 -0
  59. package/types/src/surface/useSurfaceState.d.ts +19 -0
  60. package/types/src/svg/_Glide.d.ts +9 -0
  61. package/types/src/theme/GridTheme.d.ts +1 -1
  62. package/browser/cjs/index.cjs +0 -3
  63. package/types/src/layer/HoverTipLayer.d.ts +0 -47
  64. package/types/src/layer/StickyFloat.d.ts +0 -68
  65. package/types/src/layer/useRangeHover.d.ts +0 -36
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.8.0-beta.1",
4
+ "version": "0.8.0-beta.11",
5
5
  "license": "SEE LICENSE IN <LICENSE>",
6
6
  "type": "module",
7
7
  "exports": {
@@ -9,13 +9,12 @@
9
9
  "types": "./types/index.d.ts",
10
10
  "browser": "./browser/esm/index.mjs",
11
11
  "node": "./node/esm/index.mjs",
12
- "import": "./browser/esm/index.mjs",
13
- "require": "./browser/cjs/index.cjs"
12
+ "default": "./browser/esm/index.mjs"
14
13
  }
15
14
  },
16
15
  "types": "./types/index.d.ts",
17
16
  "module": "./browser/esm/index.mjs",
18
- "main": "./browser/cjs/index.cjs",
17
+ "main": "./browser/esm/index.mjs",
19
18
  "sideEffects": [
20
19
  "*.css",
21
20
  "**/*.css"
@@ -41,20 +40,17 @@
41
40
  "table"
42
41
  ],
43
42
  "dependencies": {
44
- "@floating-ui/react-dom": "^2.1.9",
45
- "@sheetxl/utils": "^0.8.0-beta.1",
46
- "@sheetxl/utils-react": "^0.8.0-beta.1",
43
+ "@sheetxl/utils": "^0.8.0-beta.11",
44
+ "@sheetxl/utils-react": "^0.8.0-beta.11",
47
45
  "clsx": "^2.1.1",
48
- "react-merge-refs": "^3.0.2",
49
- "react-use": "^17.6.1",
50
- "tiny-invariant": "^1.3.3"
46
+ "react-merge-refs": "^3.0.2"
51
47
  },
52
48
  "peerDependencies": {
53
49
  "react": "^19.0.0",
54
50
  "react-dom": "^19.0.0"
55
51
  },
56
52
  "devDependencies": {
57
- "@types/react": "^19.2.17",
58
- "@types/react-dom": "^19.2.3"
53
+ "@types/react": "^19.3.0",
54
+ "@types/react-dom": "^19.3.0"
59
55
  }
60
56
  }
@@ -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
  */
@@ -1,5 +1,6 @@
1
1
  import { default as React } from 'react';
2
- import { OrientationFlags } from '@sheetxl/utils';
2
+ import { RangeCoords, OrientationFlags } from '@sheetxl/utils';
3
+ import { IGridPane } from '../surface';
3
4
  import { LayerProps } from '../layer';
4
5
  import { CellModelOptions } from './_CellModel';
5
6
  /**
@@ -28,6 +29,43 @@ export interface GridlineLayerProps extends Omit<LayerProps, 'children'>, CellMo
28
29
  /** Per-`<path>` props for the vertical (column) lines. */
29
30
  propsColumns?: React.SVGProps<SVGPathElement>;
30
31
  }
32
+ /**
33
+ * Options for {@link buildGridlinePath}.
34
+ */
35
+ export interface GridlinePathOptions {
36
+ /**
37
+ * Stroke width in device pixels; decides how each line center snaps.
38
+ *
39
+ * @defaultValue 1
40
+ */
41
+ width?: number;
42
+ /**
43
+ * CSS px added to each line's cross-axis position before it snaps (an even-width stroke that
44
+ * grows toward the trailing side).
45
+ *
46
+ * @defaultValue 0
47
+ */
48
+ shift?: number;
49
+ /**
50
+ * More gaps for one line, as view-relative `[start0, end0, start1, end1, ...]` along the line,
51
+ * sorted by start. `trailIndex` is the row (horizontal) or column (vertical) whose trailing edge
52
+ * the line is.
53
+ */
54
+ breaksAt?: (trailIndex: number) => readonly number[] | undefined;
55
+ }
56
+ /**
57
+ * Build a single SVG path `d` for one orientation's gridlines — each line broken around the merged
58
+ * ranges that straddle it. ONE path with many `M…L…` subpaths (vs one `<path>` per segment) keeps the
59
+ * DOM to a single node per axis; the segment count changes every scroll tick, so per-segment elements
60
+ * would thrash the DOM. Device snapping is baked into each subpath's coordinates and the stroke width
61
+ * is uniform, so a single path renders identically to N separate paths at any dpr.
62
+ *
63
+ * @param pane The pane to draw for.
64
+ * @param isHorizontal `true` for the row lines, `false` for the column lines.
65
+ * @param ranges Index-space ranges the lines break around (merges, overflow).
66
+ * @param options See {@link GridlinePathOptions}.
67
+ */
68
+ export declare const buildGridlinePath: (pane: IGridPane, isHorizontal: boolean, ranges: readonly RangeCoords[], options?: GridlinePathOptions) => string;
31
69
  /**
32
70
  * A **layer** (pane-aware): paints the gridlines for its pane, broken around merged cells, inside a
33
71
  * single full-pane {@link Layer} host box. Sources the merge spans from the merges-only
@@ -1,4 +1,4 @@
1
- import { CellCoords } from '@sheetxl/utils';
1
+ import { CellCoords, MaybePromise } from '@sheetxl/utils';
2
2
  import { ICellAnchor } from '../selection';
3
3
  import { EditCommitArgs } from './EditState';
4
4
  import { EditSession, StartEditOptions, EditModel } from './EditSession';
@@ -10,7 +10,7 @@ import { EditSession, StartEditOptions, EditModel } from './EditSession';
10
10
  * @remarks
11
11
  * An implementation detail of {@link DefaultEditable}, not part of the layer-facing contract. Collapses
12
12
  * the former `getClean` + `canEdit` + `commit` (`null` ⇒ "not editable"). A story closes over a values
13
- * map; the real app reads an `ISheet` (and returns `null` for a protected cell).
13
+ * map; the real app reads an `IWorksheet` (and returns `null` for a protected cell).
14
14
  *
15
15
  * @typeParam C - clean type. @typeParam D - dirty type. @typeParam A - commit args.
16
16
  */
@@ -25,7 +25,7 @@ export type CreateEdit<C, D = string, A = EditCommitArgs> = (coords: CellCoords)
25
25
  * (`startEdit`/`commit`/`cancel`); an app-side bridge can publish {@link getSession} onto the edit-mode
26
26
  * bus on each `subscribe` notification. It does **not** vend sessions — there is only
27
27
  * ever one active edit, held here. {@link DefaultEditable} is the default implementation; the real app
28
- * supplies its own (or reuses it with an `ISheet`-backed {@link CreateEdit}).
28
+ * supplies its own (or reuses it with an `IWorksheet`-backed {@link CreateEdit}).
29
29
  *
30
30
  * @typeParam C - clean type. @typeParam D - dirty type. @typeParam A - commit args.
31
31
  */
@@ -40,10 +40,20 @@ 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
- /** Commit the current edit. On success clears the session; returns whether it succeeded. */
46
- commit(args?: A): boolean;
51
+ /**
52
+ * Commit the current edit. On success clears the session; returns whether it succeeded, or a
53
+ * promise of that while the model's {@link EditModel.validate} answers asynchronously (the session
54
+ * stays open until then).
55
+ */
56
+ commit(args?: A): MaybePromise<boolean>;
47
57
  /** Cancel the current edit (revert + clear the session). */
48
58
  cancel(): void;
49
59
  /**
@@ -73,6 +83,10 @@ export interface DefaultEditableOptions {
73
83
  * — an invalid entry keeps you in the cell); the owner — who holds the writable selection the
74
84
  * anchor reads — performs the move. When omitted, the editable **cancels** the failed edit instead
75
85
  * (the typed value is discarded, but session and selection stay in sync).
86
+ *
87
+ * A click-away whose commit is still being checked ({@link EditModel.validate} answering
88
+ * asynchronously) is held on the edited cell the same way, and once the commit succeeds this is
89
+ * called again with the cell the user went to.
76
90
  */
77
91
  revertAnchor?: (coords: CellCoords) => void;
78
92
  }
@@ -99,19 +113,24 @@ export declare class DefaultEditable<C, D = string, A = EditCommitArgs> implemen
99
113
  private readonly revertAnchor?;
100
114
  private readonly unsubscribeAnchor;
101
115
  private current;
116
+ private leaveTarget;
102
117
  private readonly listeners;
118
+ private readonly commitErrorListeners;
103
119
  constructor(anchor: ICellAnchor, createEdit: CreateEdit<C, D, A>, options?: DefaultEditableOptions);
104
120
  private onAnchorChange;
105
121
  /** @inheritdoc IEditable.getAnchor */
106
122
  getAnchor(): ICellAnchor;
107
123
  /** @inheritdoc IEditable.subscribe */
108
124
  subscribe(listener: () => void): () => void;
125
+ /** @inheritdoc IEditable.subscribeCommitError */
126
+ subscribeCommitError(listener: (error: unknown) => void): () => void;
109
127
  /** @inheritdoc IEditable.getSession */
110
128
  getSession(): EditSession<C, D, A> | null;
111
129
  /** @inheritdoc IEditable.startEdit */
112
130
  startEdit(coords: CellCoords, options?: StartEditOptions<D>): boolean;
113
131
  /** @inheritdoc IEditable.commit */
114
- commit(args?: A): boolean;
132
+ commit(args?: A): MaybePromise<boolean>;
133
+ private end;
115
134
  /** @inheritdoc IEditable.cancel */
116
135
  cancel(): void;
117
136
  /** @inheritdoc IEditable.dispose */
@@ -1,4 +1,4 @@
1
- import { CellCoords } from '@sheetxl/utils';
1
+ import { CellCoords, MaybePromise } from '@sheetxl/utils';
2
2
  import { ICellAnchor } from '../selection';
3
3
  import { EditState, EditorMode, EditorTextSelection, EditCommitArgs } from './EditState';
4
4
  /**
@@ -36,10 +36,25 @@ export interface IInputHandle {
36
36
  * @typeParam C - clean type. @typeParam D - dirty type. @typeParam A - commit args.
37
37
  */
38
38
  export interface EditModel<C, D = string, A = EditCommitArgs> {
39
+ /**
40
+ * The cell as the model homes it, when that is more than the cell it was asked to edit (a sheet
41
+ * model adds the sheet's scope). The session reports these; omitted, it reports the cell asked for.
42
+ */
43
+ coords?: CellCoords;
39
44
  /** The clean value, captured now (edit start). */
40
45
  clean: C;
41
46
  /** Write the dirty value to the model. Return `false`, or throw, to reject (keeps the edit open). */
42
47
  commit(dirty: D, args?: A): boolean | void;
48
+ /**
49
+ * Check the dirty value before it is written. Return `false` to refuse: the edit stays open and
50
+ * the leave-key does not navigate.
51
+ *
52
+ * @remarks
53
+ * May answer with a promise, and the commit then waits for it with the edit still open. If the
54
+ * edit is cancelled, or its value changes, before the answer arrives, nothing is written. Whoever
55
+ * refuses says why: the edit only stays open.
56
+ */
57
+ validate?(dirty: D, args?: A): MaybePromise<boolean>;
43
58
  }
44
59
  /**
45
60
  * What the {@link EditSession} constructor takes: the {@link EditModel} bits plus the cell. The owning
@@ -49,6 +64,11 @@ export interface EditModel<C, D = string, A = EditCommitArgs> {
49
64
  export interface EditSessionInit<C, D = string, A = EditCommitArgs> extends EditModel<C, D, A> {
50
65
  /** The cell being edited. */
51
66
  coords: CellCoords;
67
+ /**
68
+ * Hears the error a THROWING model write rejected the commit with (see {@link EditModel.commit}).
69
+ * The rejection itself only keeps the edit open; this is how the reason reaches whoever can show it.
70
+ */
71
+ onCommitError?: (error: unknown) => void;
52
72
  }
53
73
  /**
54
74
  * Options applied when an edit begins (`DefaultEditable.startEdit` → the `EditSession` constructor).
@@ -96,6 +116,10 @@ export interface CommitContext<D = string, A = EditCommitArgs> {
96
116
  */
97
117
  export declare class EditSession<C, D = string, A = EditCommitArgs> implements ICellAnchor {
98
118
  private readonly commitFn;
119
+ private readonly validateFn?;
120
+ private pending;
121
+ private cancelled;
122
+ private readonly onCommitError?;
99
123
  private state;
100
124
  private readonly listeners;
101
125
  private readonly commitListeners;
@@ -165,11 +189,16 @@ export declare class EditSession<C, D = string, A = EditCommitArgs> implements I
165
189
  /**
166
190
  * @internal Driven by {@link DefaultEditable.commit}. Runs **every** beforeCommit consumer (a
167
191
  * `veto()` or throw rejects — a throw is also logged, never silently swallowed), then the model's
168
- * write. Returns whether it succeeded. A no-op (nothing typed) skips only the model write — the
169
- * listeners still run (and may veto), so a consumer observing "commit happened" is never skipped.
170
- * Does NOT clear the owning editable's session — `editable.commit` does that on success.
192
+ * {@link EditModel.validate}, then its write. Returns whether it succeeded, or a promise of that
193
+ * while `validate` answers asynchronously. A no-op (nothing typed) skips the check and the write —
194
+ * the listeners still run (and may veto), so a consumer observing "commit happened" is never
195
+ * skipped. Does NOT clear the owning editable's session — `editable.commit` does that on success.
171
196
  */
172
- commit(args?: A): boolean;
197
+ commit(args?: A): MaybePromise<boolean>;
198
+ private write;
199
+ private reportCommitError;
200
+ /** Whether a commit is waiting on an asynchronous {@link EditModel.validate}. */
201
+ isCommitPending(): boolean;
173
202
  /** @internal Driven by {@link DefaultEditable.cancel}. Notifies cancel observers; the editable clears the session. */
174
203
  cancel(): void;
175
204
  /** Register a beforeCommit consumer (validation, peers, …). Many may register; any may veto. */
@@ -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.
@@ -55,8 +55,9 @@ export interface EditorDivProps<C, D = string, A = EditCommitArgs> {
55
55
  *
56
56
  * @remarks
57
57
  * Unlike the layer, this is **always visible** — while idle it shows {@link EditorDivProps.idleClean}
58
- * (the active cell). It never auto-grabs focus (`autoFocus: false`): the field is focused by the
59
- * user's click, so the in-grid editor and the formula bar don't fight over focus when an edit begins.
58
+ * (the active cell). It does not grab focus for an ordinary edit: the field is focused by the user's
59
+ * click, so the in-grid editor and the formula bar don't fight over focus when an edit begins. An edit
60
+ * whose session names this host as its active editor from the start is the exception: it opens here.
60
61
  */
61
62
  export declare function EditorDiv<C, D = string, A = EditCommitArgs>(props: EditorDivProps<C, D, A>): React.ReactElement;
62
63
  export declare namespace EditorDiv {
@@ -1,4 +1,4 @@
1
- import { CellCoords, Size } from '@sheetxl/utils';
1
+ import { CellCoords, MaybePromise, Size } from '@sheetxl/utils';
2
2
  import { EditorMode, EditorTextSelection, EditCommitArgs } from './EditState';
3
3
  /**
4
4
  * Props passed to the (swappable) editor input — the visual + keyboard surface of the editor. The
@@ -67,9 +67,13 @@ 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.
72
+ *
73
+ * A promise means the commit is still being checked (the model's `validate`); the input stops
74
+ * the key and the session stays open until it lands.
71
75
  */
72
- onCommit(args?: EditCommitArgs): boolean;
76
+ onCommit(args?: EditCommitArgs): MaybePromise<boolean>;
73
77
  /** Cancel/revert the edit. */
74
78
  onCancel(): void;
75
79
  /**
@@ -60,10 +60,15 @@ export interface EditorLayerProps<C, D = string, A = EditCommitArgs> extends Vir
60
60
  * Always-mounted inline cell editor, as a grid layer.
61
61
  *
62
62
  * @remarks
63
- * The input is mounted for the life of the grid (hidden when idle);
64
- * starting an edit just seeds the first character **by value** into the {@link IEditable} and
65
- * focuses the input. Render it inside `renderLayers`; with frozen panes each pane instantiates one,
66
- * and only the pane containing the active cell shows it.
63
+ * The input is mounted for the life of the grid (hidden when idle); starting an edit seeds the first
64
+ * character **by value** into the {@link IEditable} and focuses the input. Mount it as the grid's LAST
65
+ * child so the box paints over everything; with frozen panes each pane instantiates one, and only the
66
+ * pane containing the active cell shows it.
67
+ *
68
+ * This layer carries no event handlers. The ready-mode starters (typed key / F2 / double-click) are
69
+ * the {@link EditorStartLayer}, mounted as the grid's FIRST child: a Layer's handlers are its slot's
70
+ * tap, heard on every layer's turn top first, so start handlers on the top layer would intercept keys
71
+ * and double-clicks aimed at any widget below it before that widget's own handlers ran.
67
72
  */
68
73
  export declare function EditorLayer<C, D = string, A = EditCommitArgs>(props: EditorLayerProps<C, D, A>): React.ReactElement;
69
74
  //# sourceMappingURL=EditorLayer.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { EditCommitArgs } from './EditState';
2
+ import { IEditable } from './DefaultEditable';
3
+ /**
4
+ * Props for {@link EditorStartLayer}.
5
+ *
6
+ * @typeParam C - The clean value type. @typeParam D - The dirty value type. @typeParam A - Commit args.
7
+ */
8
+ export interface EditorStartLayerProps<C, D = string, A = EditCommitArgs> {
9
+ /** The editable to start edits on — the same instance the {@link EditorLayer} renders. */
10
+ editable: IEditable<C, D, A>;
11
+ }
12
+ /**
13
+ * The grid's **ready-mode** edit starters, as a content-less grid layer: a typed character / `F2` /
14
+ * `Backspace` opens an edit on the anchor, a double-click opens one on the cell under the pointer.
15
+ *
16
+ * @remarks
17
+ * This is deliberately a separate layer from the {@link EditorLayer} that paints the box. The box must
18
+ * sit at the **top** of the stack to paint over everything, but a Layer's handlers are its slot's tap
19
+ * — grid-wide ears that fire on every layer's turn, top first — so handlers on the top layer would
20
+ * intercept keys and double-clicks aimed at any widget below it (a chart, a note) before that widget's
21
+ * own handlers ran. Starting an edit is the grid *floor's* behavior: it belongs at the **bottom** of
22
+ * the stack, where it only hears what no widget above consumed. Mount it as the grid's first child.
23
+ *
24
+ * Keys are positionless, so the floor position alone is not enough for them: a focused widget that
25
+ * does not consume a printable key (it has no use for it, and must let keys through so its own
26
+ * commands can resolve) would otherwise still reach this tap. The key start therefore acts only when
27
+ * the key is aimed at the grid surface itself — the surface (or the pane's focus target, which
28
+ * stands in for it) is the focused element — which is what "the grid is in
29
+ * ready mode" means. A double-click needs no such guard: it has a position, and a widget that owns
30
+ * that position consumes it.
31
+ */
32
+ export declare function EditorStartLayer<C, D = string, A = EditCommitArgs>(props: EditorStartLayerProps<C, D, A>): React.ReactElement;
33
+ //# sourceMappingURL=EditorStartLayer.d.ts.map
@@ -6,5 +6,6 @@ export * from './DefaultEditable';
6
6
  export * from './EditorPlacement';
7
7
  export * from './EditorInput';
8
8
  export * from './EditorLayer';
9
+ export * from './EditorStartLayer';
9
10
  export * from './EditorDiv';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { RefObject } from 'react';
2
- import { Direction, RangeAxis } from '@sheetxl/utils';
2
+ import { Direction, RangeAxis, MaybePromise } from '@sheetxl/utils';
3
3
  import { EditorMode, EditCommitArgs } from './EditState';
4
4
  import { StartEditOptions } from './EditSession';
5
5
  /**
@@ -29,7 +29,12 @@ export interface EditorKeymapProps {
29
29
  value: string;
30
30
  onDirtyChange(dirty: string): void;
31
31
  onModeChange(mode: EditorMode): void;
32
- onCommit(args?: EditCommitArgs): boolean;
32
+ /**
33
+ * Commit the edit. A promise means the commit is still being checked: the key is consumed, and the
34
+ * move it asks for is made through {@link onNavigate} / {@link onTraverse} once the commit lands
35
+ * (with neither supplied, the active cell stays put).
36
+ */
37
+ onCommit(args?: EditCommitArgs): MaybePromise<boolean>;
33
38
  onCancel(): void;
34
39
  /**
35
40
  * Optional. When provided, a successful **enter-mode arrow** commit **navigates here, in the input**,
@@ -54,6 +59,17 @@ export interface EditorKeymapProps {
54
59
  */
55
60
  setCaret?(caret: number): void;
56
61
  }
62
+ /**
63
+ * The commit "signal" a leave key carries: `Ctrl+Shift+Enter` array-enters over the selection
64
+ * ({@link EditCommitArgs.arrayFixed}), `Ctrl+Enter` fills it ({@link EditCommitArgs.broadcast}), and
65
+ * anything else is a plain commit (`undefined`). Both modified forms keep the active cell put.
66
+ */
67
+ export declare const commitArgsFromKey: (e: {
68
+ which: number;
69
+ }, native: {
70
+ ctrlKey: boolean;
71
+ shiftKey: boolean;
72
+ }) => EditCommitArgs | undefined;
57
73
  /**
58
74
  * The shared spreadsheet editor keymap, as a `<textarea>` `onKeyDown` handler. Both the default and
59
75
  * the cell-aware inputs use it so the interaction contract stays identical:
@@ -0,0 +1,29 @@
1
+ import { RangeAxis } from '@sheetxl/utils';
2
+ import { FreezeDragPosition } from './_freezeSnap';
3
+ export interface GridFreezeAnchorProps {
4
+ /** The axis the freeze moves along: `Column` drags the vertical freeze line. */
5
+ axis: RangeAxis;
6
+ /** Disable the grip (the pointer falls through to the cells). */
7
+ disabled?: boolean;
8
+ /** The pointer has moved past the threshold and the drag has begun. */
9
+ onFreezeDragStart?: (position: FreezeDragPosition) => void;
10
+ /** Called on every move while dragging. */
11
+ onFreezeDrag?: (position: FreezeDragPosition) => void;
12
+ /** The pointer was released after a drag. */
13
+ onFreezeDragEnd?: (position: FreezeDragPosition) => void;
14
+ /** The drag was abandoned (Escape); nothing should be committed. */
15
+ onFreezeDragCancel?: () => void;
16
+ /** The grip became active (hovered or dragging) or stopped being so — e.g. to highlight the freeze line. */
17
+ onActiveChange?: (active: boolean) => void;
18
+ }
19
+ /**
20
+ * The freeze grip over the cells: a hit strip along the main grid's freeze line, dragged like
21
+ * {@link HeaderFreezeAnchor}. It draws nothing — the grid draws the line, and can highlight it from
22
+ * `onActiveChange`.
23
+ *
24
+ * @remarks
25
+ * Render it in the main grid's layers. It exists only while the axis is frozen, in the frozen panes whose
26
+ * trailing edge is the line (for columns, the left and top-left panes).
27
+ */
28
+ export declare const GridFreezeAnchor: import('react').MemoExoticComponent<(props: GridFreezeAnchorProps) => string | number | bigint | boolean | Iterable<import('react').ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<import('react').ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined>;
29
+ //# sourceMappingURL=GridFreezeAnchor.d.ts.map
@@ -56,15 +56,32 @@ export interface IHeaderDividerAttributes {
56
56
  export interface IHeaderDividerElement extends HTMLDivElement, IHeaderDividerAttributes {
57
57
  }
58
58
  export interface HeaderDividerRendererProps extends HeaderDividerProps {
59
+ /**
60
+ * The divider's line in pane coordinates — zero-width for a column divider, zero-height for a row —
61
+ * snapped to the device pixel the gridline paints. The renderer positions its element (hit area and
62
+ * visible line) around it.
63
+ */
59
64
  bounds: Bounds;
60
65
  /**
61
66
  * Handle mouse down and all dragging
62
67
  */
63
68
  onPointerDown: React.PointerEventHandler<HTMLDivElement>;
64
69
  onAutoFit: () => void;
70
+ /**
71
+ * Reveal the hidden headers this divider sits on. Present only when the divider has hidden headers
72
+ * to reveal and the consumer handles it.
73
+ */
74
+ onUnhide?: () => void;
75
+ /** The divider is pressed or being dragged. */
76
+ isActive?: boolean;
65
77
  }
66
78
  export interface HeaderDividerAnchorProps extends HeaderDividerProps {
67
79
  renderDivider?: (props: HeaderDividerRendererProps, pane: IGridPane) => React.ReactNode;
80
+ /**
81
+ * The clipped bounds of the pane's visible headers, from {@link getHeaderDividerBounds}. Computed when
82
+ * omitted; pass it when rendering a pane's worth of dividers so it is computed once.
83
+ */
84
+ visibleBounds?: Bounds | null;
68
85
  /**
69
86
  * The number of pixels to mouse before resizing starts.
70
87
  * If this number is less than 1 then it will be 1.
@@ -110,6 +127,13 @@ export interface HeaderDividerAnchorProps extends HeaderDividerProps {
110
127
  * @param index
111
128
  */
112
129
  onDividerAutoFit?: (index: number) => void;
130
+ /**
131
+ * Called when the unhide button on a divider over hidden headers is clicked. Without it the divider
132
+ * shows no unhide button.
133
+ *
134
+ * @param index the index of the divider.
135
+ */
136
+ onDividerUnhide?: (index: number) => void;
113
137
  }
114
138
  export declare const DefaultHeaderDivider: import('react').NamedExoticComponent<Omit<HeaderDividerRendererProps, "ref"> & import('react').RefAttributes<IHeaderDividerElement>>;
115
139
  /**
@@ -117,4 +141,9 @@ export declare const DefaultHeaderDivider: import('react').NamedExoticComponent<
117
141
  * Resize, hide/unhide, etc.
118
142
  */
119
143
  export declare const HeaderDividerAnchor: import('react').NamedExoticComponent<Omit<HeaderDividerAnchorProps, "ref"> & import('react').RefAttributes<IHeaderDividerElement>>;
144
+ /**
145
+ * The clipped bounds of a pane's visible headers on an axis — the span every divider in the pane shares —
146
+ * or `null` when none are visible.
147
+ */
148
+ export declare const getHeaderDividerBounds: (pane: IGridPane, axis: RangeAxis) => Bounds | null;
120
149
  //# sourceMappingURL=HeaderDividerAnchor.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { RangeAxis } from '@sheetxl/utils';
2
+ import { FreezeDragPosition } from './_freezeSnap';
3
+ export type { FreezeDragPosition } from './_freezeSnap';
4
+ export interface HeaderFreezeAnchorProps {
5
+ /** The axis the freeze moves along: `Column` drags the vertical freeze line along the column header. */
6
+ axis: RangeAxis;
7
+ /** Disable the grip entirely (no hit area, no hover). */
8
+ disabled?: boolean;
9
+ /**
10
+ * Draw the bar in its active color although this grip is not hovered — e.g. while the freeze line's grip
11
+ * in the body is.
12
+ */
13
+ highlight?: boolean;
14
+ /** The pointer has moved past the threshold and the drag has begun. */
15
+ onFreezeDragStart?: (position: FreezeDragPosition) => void;
16
+ /** Called on every move while dragging. */
17
+ onFreezeDrag?: (position: FreezeDragPosition) => void;
18
+ /** The pointer was released after a drag. */
19
+ onFreezeDragEnd?: (position: FreezeDragPosition) => void;
20
+ /** The drag was abandoned (Escape); nothing should be committed. */
21
+ onFreezeDragCancel?: () => void;
22
+ /** The grip became active (hovered or dragging) or stopped being so — e.g. to highlight the freeze line. */
23
+ onActiveChange?: (active: boolean) => void;
24
+ }
25
+ /**
26
+ * The freeze grip: a bar in a header on the freeze line (or at the header's start when nothing is frozen
27
+ * on this axis). Dragging it snaps the freeze to the nearest header boundary on screen.
28
+ *
29
+ * @remarks
30
+ * Render it in the header's layers, above the resize dividers. It draws in one pane only: the frozen pane,
31
+ * centered on its trailing edge (frozen panes sit over the scrolling one, so the grip covers the last
32
+ * frozen header's divider), or the scrolling pane's leading edge when nothing is frozen. The drag reports
33
+ * positions only — committing the freeze is the caller's. {@link GridFreezeAnchor} is the same grip on the
34
+ * freeze line over the cells.
35
+ */
36
+ export declare const HeaderFreezeAnchor: import('react').MemoExoticComponent<(props: HeaderFreezeAnchorProps) => import("react").JSX.Element | null>;
37
+ //# sourceMappingURL=HeaderFreezeAnchor.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { IGridPane } from '../surface';
2
+ /**
3
+ * Where a freeze drag would land: the snapped boundary and the raw pointer, both as CLIENT coordinates
4
+ * on the drag axis so any pane (header or body) maps them with `getPanePointFromPointer`.
5
+ */
6
+ export interface FreezeDragPosition {
7
+ /**
8
+ * The freeze count the drop commits: the index of the first track in the scrolling area. `0` ⇒
9
+ * unfreeze this axis.
10
+ */
11
+ index: number;
12
+ /** Client coordinate (x for columns, y for rows) of the snapped boundary — the solid line. */
13
+ snapClient: number;
14
+ /**
15
+ * Client coordinate of the pointer — the dashed line. Absent once the pointer is past the start of the
16
+ * header (where the drop unfreezes), so no line is drawn outside the grid.
17
+ */
18
+ pointerClient?: number;
19
+ }
20
+ /**
21
+ * Snap a client coordinate to the nearest track boundary on screen across the header's panes.
22
+ *
23
+ * Candidates are the leading edges of the visible tracks in the frozen pane and the scrolling pane, plus
24
+ * the surface's own leading edge (which unfreezes). Only boundaries actually on screen are candidates, so
25
+ * a drag can never freeze past what is visible.
26
+ */
27
+ export declare const snapToBoundary: (pane: IGridPane, isColumn: boolean, pointerClient: number, crossClient: number) => FreezeDragPosition;
28
+ //# sourceMappingURL=_freezeSnap.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { RangeAxis } from '@sheetxl/utils';
2
+ import { FreezeDragPosition } from './_freezeSnap';
3
+ export interface FreezeGripOptions {
4
+ axis: RangeAxis;
5
+ disabled: boolean;
6
+ /** Whether this pane draws the grip. A pane that stops drawing it never hears the pointer leave. */
7
+ isOwner: boolean;
8
+ onFreezeDragStart?: (position: FreezeDragPosition) => void;
9
+ onFreezeDrag?: (position: FreezeDragPosition) => void;
10
+ onFreezeDragEnd?: (position: FreezeDragPosition) => void;
11
+ onFreezeDragCancel?: () => void;
12
+ onActiveChange?: (active: boolean) => void;
13
+ }
14
+ export interface FreezeGrip {
15
+ /** Hovered or dragging, in the owning pane, while enabled. */
16
+ isActive: boolean;
17
+ cursor: string;
18
+ /** Spread onto the hit area. */
19
+ hitProps: React.HTMLAttributes<HTMLDivElement>;
20
+ /** The drag backdrop (forces the cursor, shields other components); render it alongside the hit area. */
21
+ backdrop: React.ReactNode;
22
+ }
23
+ /**
24
+ * The gesture behind a freeze grip, wherever its hit area sits: press, threshold, snapped drag positions,
25
+ * Escape to abandon, and the active (hovered or dragging) state.
26
+ */
27
+ export declare const useFreezeGrip: (options: FreezeGripOptions) => FreezeGrip;
28
+ //# sourceMappingURL=_useFreezeGrip.d.ts.map
@@ -1,3 +1,5 @@
1
1
  export * from './HeaderDividerAnchor';
2
+ export * from './HeaderFreezeAnchor';
3
+ export * from './GridFreezeAnchor';
2
4
  export * from './useHeaderSelectionPointer';
3
5
  //# sourceMappingURL=index.d.ts.map