@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.
- package/README.md +16 -5
- package/browser/esm/index.mjs +3504 -2846
- package/licenses/dependencies.txt +0 -208
- package/node/esm/index.mjs +3861 -2284
- package/package.json +8 -12
- package/types/src/cell/CellRenderer.d.ts +14 -3
- package/types/src/cell/GridlineLayer.d.ts +39 -1
- package/types/src/editor/DefaultEditable.d.ts +25 -6
- package/types/src/editor/EditSession.d.ts +34 -5
- package/types/src/editor/EditState.d.ts +6 -0
- package/types/src/editor/EditorDiv.d.ts +3 -2
- package/types/src/editor/EditorInput.d.ts +7 -3
- package/types/src/editor/EditorLayer.d.ts +9 -4
- package/types/src/editor/EditorStartLayer.d.ts +33 -0
- package/types/src/editor/index.d.ts +1 -0
- package/types/src/editor/useEditorKeymap.d.ts +18 -2
- package/types/src/header/GridFreezeAnchor.d.ts +29 -0
- package/types/src/header/HeaderDividerAnchor.d.ts +29 -0
- package/types/src/header/HeaderFreezeAnchor.d.ts +37 -0
- package/types/src/header/_freezeSnap.d.ts +28 -0
- package/types/src/header/_useFreezeGrip.d.ts +28 -0
- package/types/src/header/index.d.ts +2 -0
- package/types/src/header/useHeaderSelectionPointer.d.ts +10 -6
- package/types/src/layer/GestureTip.d.ts +69 -0
- package/types/src/layer/GridFloatPanel.d.ts +19 -0
- package/types/src/layer/Layer.d.ts +6 -0
- package/types/src/layer/RangeFloatLayer.d.ts +143 -51
- package/types/src/layer/RangePrompt.d.ts +8 -8
- package/types/src/layer/RangeTooltip.d.ts +123 -0
- package/types/src/layer/RangeTooltipLayer.d.ts +53 -0
- package/types/src/layer/StickyAnchor.d.ts +15 -5
- package/types/src/layer/StickyPortal.d.ts +73 -0
- package/types/src/layer/TooltipProvider.d.ts +101 -0
- package/types/src/layer/index.d.ts +7 -4
- package/types/src/layer/useGridAnchor.d.ts +45 -4
- package/types/src/layer/useHoverIntent.d.ts +35 -8
- package/types/src/list/CheckboxList.d.ts +22 -1
- package/types/src/list/List.d.ts +11 -2
- package/types/src/list/ListSearch.d.ts +52 -0
- package/types/src/list/ListSurface.d.ts +25 -5
- package/types/src/list/PickList.d.ts +71 -0
- package/types/src/list/_measureText.d.ts +14 -0
- package/types/src/list/index.d.ts +2 -0
- package/types/src/range/DragHandle.d.ts +19 -2
- package/types/src/range/Draggable.d.ts +132 -7
- package/types/src/range/FillHandle.d.ts +33 -3
- package/types/src/range/RangeInsertBar.d.ts +91 -0
- package/types/src/range/index.d.ts +2 -0
- package/types/src/selection/SelectionLayer.d.ts +128 -15
- package/types/src/selection/SelectionStyle.d.ts +33 -0
- package/types/src/selection/SelectionTouchGrips.d.ts +0 -4
- package/types/src/selection/_SelectionUtils.d.ts +6 -0
- package/types/src/selection/useDragCells.d.ts +65 -9
- package/types/src/surface/GridPane.d.ts +2 -0
- package/types/src/surface/IGridPane.d.ts +15 -2
- package/types/src/surface/SurfaceStateProvider.d.ts +1 -1
- package/types/src/surface/getPrimaryPane.d.ts +13 -0
- package/types/src/surface/index.d.ts +1 -0
- package/types/src/surface/useSurfaceState.d.ts +19 -0
- package/types/src/svg/_Glide.d.ts +9 -0
- package/types/src/theme/GridTheme.d.ts +1 -1
- package/browser/cjs/index.cjs +0 -3
- package/types/src/layer/HoverTipLayer.d.ts +0 -47
- package/types/src/layer/StickyFloat.d.ts +0 -68
- 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.
|
|
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
|
-
"
|
|
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/
|
|
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
|
-
"@
|
|
45
|
-
"@sheetxl/utils": "^0.8.0-beta.
|
|
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.
|
|
58
|
-
"@types/react-dom": "^19.
|
|
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
|
|
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
|
-
|
|
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 `
|
|
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 `
|
|
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
|
-
/**
|
|
46
|
-
|
|
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
|
|
169
|
-
*
|
|
170
|
-
*
|
|
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
|
|
59
|
-
*
|
|
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
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
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
|
|
@@ -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
|
-
|
|
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
|