@sheetxl/grid-react 0.8.0-beta.2 → 0.8.0-beta.21
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/LICENSE.md +8 -7
- package/README.md +21 -8
- package/browser/esm/index.mjs +3632 -3122
- package/licenses/dependencies.txt +0 -208
- package/node/esm/index.mjs +3968 -2545
- package/package.json +9 -13
- package/types/src/cell/CellRenderer.d.ts +32 -0
- package/types/src/cell/GridlineLayer.d.ts +39 -1
- package/types/src/editor/DefaultEditable.d.ts +16 -6
- package/types/src/editor/EditSession.d.ts +28 -5
- package/types/src/editor/EditorDiv.d.ts +3 -2
- package/types/src/editor/EditorInput.d.ts +5 -2
- 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 +7 -2
- package/types/src/grid/Grid.d.ts +1 -1
- package/types/src/grid/IGrid.d.ts +4 -4
- package/types/src/header/GridFreezeAnchor.d.ts +29 -0
- package/types/src/header/HeaderDividerAnchor.d.ts +36 -1
- 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 +4 -4
- package/types/src/layer/GestureTip.d.ts +77 -0
- package/types/src/layer/GridFloatPanel.d.ts +3 -2
- package/types/src/layer/Layer.d.ts +6 -0
- package/types/src/layer/RangeFloatLayer.d.ts +145 -53
- package/types/src/layer/RangePrompt.d.ts +8 -8
- package/types/src/layer/RangeTooltip.d.ts +128 -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 +104 -0
- package/types/src/layer/_Utils.d.ts +9 -3
- package/types/src/layer/index.d.ts +6 -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 +18 -2
- package/types/src/list/ListSearch.d.ts +24 -3
- package/types/src/list/PickList.d.ts +6 -1
- package/types/src/range/DragHandle.d.ts +13 -6
- package/types/src/range/Draggable.d.ts +29 -3
- package/types/src/range/FillHandle.d.ts +11 -2
- package/types/src/range/RangeGlide.d.ts +160 -0
- package/types/src/range/RangeRect.d.ts +7 -0
- package/types/src/range/RangeResizer.d.ts +7 -1
- package/types/src/range/index.d.ts +2 -0
- package/types/src/range/useRangeGlide.d.ts +11 -0
- package/types/src/selection/RangesPickerLayer.d.ts +4 -1
- package/types/src/selection/SelectionLayer.d.ts +71 -15
- package/types/src/selection/SelectionStyle.d.ts +4 -1
- package/types/src/selection/SelectionTouchGrips.d.ts +6 -4
- package/types/src/selection/_SelectionUtils.d.ts +6 -0
- package/types/src/selection/useDragCells.d.ts +29 -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 +29 -0
- package/types/src/svg/RectangleSVG.d.ts +21 -4
- 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
|
@@ -29,6 +29,12 @@ export interface DragInsert {
|
|
|
29
29
|
* the drop will DO, and no pointer event carries that. Read the modifiers from here, never off the
|
|
30
30
|
* event handed to the same callback (which is the last one the *pointer* fired).
|
|
31
31
|
*/
|
|
32
|
+
/** What {@link DraggableProps.renderMove} is handed. */
|
|
33
|
+
export interface DragMoveRenderProps {
|
|
34
|
+
/** The in-flight range. */
|
|
35
|
+
range: RangeCoords;
|
|
36
|
+
info: DragMoveInfo;
|
|
37
|
+
}
|
|
32
38
|
export interface DragMoveInfo {
|
|
33
39
|
/** The live Ctrl / Shift / Alt / Meta state (Excel: Ctrl = copy, Shift = insert). */
|
|
34
40
|
mods: DragModifiers;
|
|
@@ -39,6 +45,22 @@ export interface DragMoveInfo {
|
|
|
39
45
|
* turns it into a destination range.
|
|
40
46
|
*/
|
|
41
47
|
insert: DragInsert | null;
|
|
48
|
+
/**
|
|
49
|
+
* Where the block was grabbed, fixed at the press. `null` only when the pane could not resolve the
|
|
50
|
+
* pointer.
|
|
51
|
+
*/
|
|
52
|
+
grab: DragGrab | null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Where a drag took hold of its block: the border nearest the press and the cell it pressed. A move keeps
|
|
56
|
+
* the grab offset, so the same cell of the translated block stays under the pointer — translate
|
|
57
|
+
* `cell` by the drag's delta to find it.
|
|
58
|
+
*/
|
|
59
|
+
export interface DragGrab {
|
|
60
|
+
/** The border nearest the press. With `grip: 'entire'` it is still the nearest, though seldom meant. */
|
|
61
|
+
edge: RangeEdge;
|
|
62
|
+
/** The cell of the source range that was pressed (clamped into it: the ring reaches just outside). */
|
|
63
|
+
cell: CellCoords;
|
|
42
64
|
}
|
|
43
65
|
/**
|
|
44
66
|
* The insert a drop would perform from the pointer's position, as Excel resolves it — the first half
|
|
@@ -124,8 +146,12 @@ export interface DraggableProps {
|
|
|
124
146
|
* mid-drag; use this to keep a drag affordance (e.g. the thickened move border) lit for the whole drag.
|
|
125
147
|
*/
|
|
126
148
|
onDragStateChange?: (dragging: boolean) => void;
|
|
127
|
-
/**
|
|
128
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Drag started; `range` is the original. `cancel` aborts this drag as Escape would
|
|
151
|
+
* ({@link DraggableProps.onDragStateChange} `false` follows, nothing committed) — hand it to chrome
|
|
152
|
+
* that offers a way out.
|
|
153
|
+
*/
|
|
154
|
+
onDragStart?: (range: RangeCoords, e: React.PointerEvent, info: DragMoveInfo, cancel: () => void) => void;
|
|
129
155
|
/**
|
|
130
156
|
* The translated range during the drag. Wire it to update `range` (the content follows the pointer —
|
|
131
157
|
* "live"), or to position a composed ghost preview. `Draggable` itself stays silent.
|
|
@@ -149,7 +175,7 @@ export interface DraggableProps {
|
|
|
149
175
|
* `info` re-renders the preview on a modifier change, so the ghost can become an insert I-beam the
|
|
150
176
|
* moment Shift goes down.
|
|
151
177
|
*/
|
|
152
|
-
renderMove?: (
|
|
178
|
+
renderMove?: (props: DragMoveRenderProps) => React.ReactNode;
|
|
153
179
|
/**
|
|
154
180
|
* The cursor painted for the duration of the drag. The grip's element is pointer-captured, so ITS
|
|
155
181
|
* cursor is the one the browser shows once a drag is in flight — pass a mode-derived cursor (`copy`
|
|
@@ -2,6 +2,7 @@ import { default as React } from 'react';
|
|
|
2
2
|
import { CellCoords, RangeCoords } from '@sheetxl/utils';
|
|
3
3
|
import { VisualSVGProps } from '../svg';
|
|
4
4
|
import { DragHandleRenderProps } from './DragHandle';
|
|
5
|
+
import { RangeGlide } from './RangeGlide';
|
|
5
6
|
/**
|
|
6
7
|
* Props for {@link FillHandle}. Follows the shared **RangeHandle** contract: a controlled `range`
|
|
7
8
|
* (the fill template/source) in, the fill **destination** range out via `onDrag*`. The destination
|
|
@@ -16,8 +17,11 @@ export interface FillHandleProps extends VisualSVGProps {
|
|
|
16
17
|
* `enableFill` is set (or via a `singleLayer` slot); required in practice for standalone use.
|
|
17
18
|
*/
|
|
18
19
|
range?: RangeCoords;
|
|
19
|
-
/**
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Drag started; `range` is the template. `cancel` aborts this drag as Escape would
|
|
22
|
+
* ({@link FillHandleProps.onDragDone} follows, no fill) — hand it to chrome that offers a way out.
|
|
23
|
+
*/
|
|
24
|
+
onDragStart?: (range: RangeCoords, e: React.PointerEvent, cancel: () => void) => void;
|
|
21
25
|
/**
|
|
22
26
|
* Adjust the raw destination before it drives the preview + `onDrag*` — e.g. snap the fill edge to a
|
|
23
27
|
* merged-cell boundary so the dashed preview and the committed fill agree (and never land on an
|
|
@@ -58,6 +62,11 @@ export interface FillHandleProps extends VisualSVGProps {
|
|
|
58
62
|
* Replaces the default dot. Use it for a custom fill mark (e.g. the Excel fill square).
|
|
59
63
|
*/
|
|
60
64
|
renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
|
|
65
|
+
/**
|
|
66
|
+
* The glide the template's outline is drawn with, so the handle rides its corner frame by frame
|
|
67
|
+
* instead of arriving first. See {@link DragHandleProps.glide}.
|
|
68
|
+
*/
|
|
69
|
+
glide?: RangeGlide | null;
|
|
61
70
|
}
|
|
62
71
|
/**
|
|
63
72
|
* The destination range when filling `template` toward `to`. Excel-style single axis: extend along
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Bounds, RangeCoords, TopLeft } from '@sheetxl/utils';
|
|
2
|
+
import { IGridPane } from '../surface';
|
|
3
|
+
/**
|
|
4
|
+
* What a {@link RangeGlide} travels to: a range, plus the cell cut out of its fill (a selection's
|
|
5
|
+
* active cell), which travels on the same clock.
|
|
6
|
+
*/
|
|
7
|
+
export interface RangeGlideTarget {
|
|
8
|
+
range: RangeCoords;
|
|
9
|
+
hole?: RangeCoords;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One frame of a glide: the rectangle to draw and its hole, in a pane's VIEW space (the space
|
|
13
|
+
* `IGridPane.getBoundsFromRange` returns).
|
|
14
|
+
*/
|
|
15
|
+
export interface RangeGlideFrame {
|
|
16
|
+
outer: Bounds;
|
|
17
|
+
hole?: Bounds;
|
|
18
|
+
/**
|
|
19
|
+
* Where `outer` is heading: the target's box, clamped as `outer` is. Whatever is anchored to the
|
|
20
|
+
* target travels by `outer`'s offset from it.
|
|
21
|
+
*/
|
|
22
|
+
to: Bounds;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The geometry a glide resolves through: one pane's view. A glide keeps no pixels of its own - the
|
|
26
|
+
* target is a range and the start point is absolute (content) space - so every pane that draws it
|
|
27
|
+
* asks through its own view, and a scroll mid-glide moves the frame with the cells.
|
|
28
|
+
*/
|
|
29
|
+
export interface RangeGlideView {
|
|
30
|
+
/** A range's box in view space. */
|
|
31
|
+
boundsOf(range: RangeCoords): Bounds;
|
|
32
|
+
/** Absolute (content) space → view space. */
|
|
33
|
+
toView(bounds: Bounds): Bounds;
|
|
34
|
+
/** View space → absolute (content) space. */
|
|
35
|
+
toAbsolute(bounds: Bounds): Bounds;
|
|
36
|
+
/** The view's visible box (view space). */
|
|
37
|
+
getViewport(): Bounds;
|
|
38
|
+
/** Where the view is scrolling to; a change mid-glide that the glide did not start with stops it. */
|
|
39
|
+
getScrollTarget(): TopLeft;
|
|
40
|
+
/** Device pixel ratio, for keeping a moving box's size steady. */
|
|
41
|
+
getDevicePixelRatio(): number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Timing of a {@link RangeGlide}.
|
|
45
|
+
*/
|
|
46
|
+
export interface RangeGlideOptions {
|
|
47
|
+
/** How long a glide takes, in ms. `0` never glides. @defaultValue 60 */
|
|
48
|
+
duration?: number;
|
|
49
|
+
/**
|
|
50
|
+
* How far into the glide its first frame is drawn, in ms. The clock starts on the first animation
|
|
51
|
+
* frame, not when the target changes: the change usually lands inside the work it causes (a render
|
|
52
|
+
* of everything that reads the selection), and a clock running through that work would spend the
|
|
53
|
+
* glide where nothing paints and show it as a jump. @defaultValue one 60Hz frame
|
|
54
|
+
*/
|
|
55
|
+
frameLead?: number;
|
|
56
|
+
/**
|
|
57
|
+
* How long a glide takes when it starts from inside another one - a held arrow key, a drag. Shorter,
|
|
58
|
+
* so a stream of retargets keeps up instead of trailing. @defaultValue 60% of `duration`
|
|
59
|
+
*/
|
|
60
|
+
retargetDuration?: number;
|
|
61
|
+
/** Progress (0..1) → eased progress (0..1). @defaultValue ease-out quad */
|
|
62
|
+
easing?: (t: number) => number;
|
|
63
|
+
/** The clock. @defaultValue `performance.now` */
|
|
64
|
+
now?: () => number;
|
|
65
|
+
/** Schedule the next frame. @defaultValue `requestAnimationFrame` */
|
|
66
|
+
requestFrame?: (callback: () => void) => number;
|
|
67
|
+
/** Cancel a scheduled frame. @defaultValue `cancelAnimationFrame` */
|
|
68
|
+
cancelFrame?: (handle: number) => void;
|
|
69
|
+
/** Whether motion is reduced (no glides). @defaultValue the `prefers-reduced-motion` media query */
|
|
70
|
+
isReducedMotion?: () => boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Interpolate `from` → `to` at eased progress `e`, edge by edge. Edges shared by both ends are
|
|
74
|
+
* returned unchanged; with one `e` for a box and its hole, a hole inside its box at both ends stays
|
|
75
|
+
* inside it throughout.
|
|
76
|
+
*/
|
|
77
|
+
export declare const lerpGlideBounds: (from: Bounds, to: Bounds, e: number, dpr: number) => Bounds;
|
|
78
|
+
/**
|
|
79
|
+
* How far a frame is from where it is heading, at the edge something hangs off — the offset that
|
|
80
|
+
* carries a thing anchored to the target (a handle on its corner, a tip off its side) along with the
|
|
81
|
+
* frame. `side` and `align` name the edge as a grid placement does: `side` the edge, `align` the point
|
|
82
|
+
* along it.
|
|
83
|
+
*/
|
|
84
|
+
export declare const glideOffset: (frame: RangeGlideFrame, side: "t" | "b" | "l" | "r", align?: "start" | "center" | "end") => {
|
|
85
|
+
dx: number;
|
|
86
|
+
dy: number;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* The {@link RangeGlideView} of a grid pane.
|
|
90
|
+
*/
|
|
91
|
+
export declare const paneGlideView: (pane: IGridPane) => RangeGlideView;
|
|
92
|
+
/**
|
|
93
|
+
* A glide between ranges on one clock: the animation of a selection moving, growing or shrinking from
|
|
94
|
+
* one range to the next. Everything that travels with the range - its outline, its fill and hole, its
|
|
95
|
+
* handles, its tip - reads the same frame, so they move in lockstep.
|
|
96
|
+
*
|
|
97
|
+
* @remarks
|
|
98
|
+
* A glide holds ranges and one start point, not pixels: the target is a range and the start is in
|
|
99
|
+
* absolute (content) space, and each frame is resolved through the view that asks for it. So a glide
|
|
100
|
+
* is shared by every pane of a surface (one per frozen pane, each drawing its own clipped slice), and
|
|
101
|
+
* a scroll mid-glide carries the frame with the cells instead of dragging it. A scroll the glide did
|
|
102
|
+
* not start with - the user's wheel - ends it.
|
|
103
|
+
*
|
|
104
|
+
* It renders nothing. Readers {@link subscribe} and pull {@link frame} for their view on each tick;
|
|
105
|
+
* `frame` is `null` when settled, which means: draw the target as rendered.
|
|
106
|
+
*/
|
|
107
|
+
export declare class RangeGlide {
|
|
108
|
+
private readonly _duration;
|
|
109
|
+
private readonly _frameLead;
|
|
110
|
+
private readonly _retargetDuration;
|
|
111
|
+
private readonly _easing;
|
|
112
|
+
private readonly _now;
|
|
113
|
+
private readonly _requestFrame;
|
|
114
|
+
private readonly _cancelFrame;
|
|
115
|
+
private readonly _isReducedMotion;
|
|
116
|
+
private _target;
|
|
117
|
+
/** Where the glide started, in absolute space; `null` when settled. */
|
|
118
|
+
private _from;
|
|
119
|
+
private _start;
|
|
120
|
+
private _length;
|
|
121
|
+
/** Eased progress of the glide in flight. */
|
|
122
|
+
private _progress;
|
|
123
|
+
/** The view the glide started in, watched for a scroll it did not start with. */
|
|
124
|
+
private _view;
|
|
125
|
+
private _scrollBase;
|
|
126
|
+
private _handle;
|
|
127
|
+
private readonly _listeners;
|
|
128
|
+
constructor(options?: RangeGlideOptions);
|
|
129
|
+
/** The range the glide is heading to (or rests at). */
|
|
130
|
+
getTarget(): RangeGlideTarget | null;
|
|
131
|
+
/** A glide is in flight. */
|
|
132
|
+
isGliding(): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Head to `target`. The same target again does nothing, so every pane drawing one selection can
|
|
135
|
+
* call this on each render and only the first change counts. With `animate` the glide starts from
|
|
136
|
+
* what is drawn now - mid-flight, from the frame in flight; otherwise it jumps.
|
|
137
|
+
*
|
|
138
|
+
* @param view The view of the caller, used to capture the start point and watch for a user scroll.
|
|
139
|
+
* @returns Whether the target changed.
|
|
140
|
+
*/
|
|
141
|
+
retarget(target: RangeGlideTarget, view: RangeGlideView, animate: boolean): boolean;
|
|
142
|
+
/** Jump to the target now. */
|
|
143
|
+
finish(): void;
|
|
144
|
+
/**
|
|
145
|
+
* The frame to draw in `view` now, or `null` when settled (draw the target as rendered). A glide
|
|
146
|
+
* whose start is nowhere near the view - a jump to the far end of the data - draws no frame.
|
|
147
|
+
*/
|
|
148
|
+
frame(view: RangeGlideView): RangeGlideFrame | null;
|
|
149
|
+
/** Listen for frames (each tick, and on start and settle). Returns the unsubscribe. */
|
|
150
|
+
subscribe(listener: () => void): () => void;
|
|
151
|
+
/**
|
|
152
|
+
* What is drawn now, in absolute space: the frame in flight, else the target at rest. Unclamped, so
|
|
153
|
+
* it is the same whichever pane asks - a frozen pane the range is not in included.
|
|
154
|
+
*/
|
|
155
|
+
private _shownAbsolute;
|
|
156
|
+
private readonly _tick;
|
|
157
|
+
private _settle;
|
|
158
|
+
private _notify;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=RangeGlide.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RangeCoords } from '@sheetxl/utils';
|
|
2
2
|
import { VirtualDivProps } from '../layer';
|
|
3
3
|
import { VisualSVGProps } from '../svg';
|
|
4
|
+
import { RangeGlide } from './RangeGlide';
|
|
4
5
|
/**
|
|
5
6
|
* Props for {@link RangeRect}: a thin range→bounds wrapper that places the {@link RectangleSVG}
|
|
6
7
|
* **shape** at a grid range — it resolves the range to bounds via the pane, culls off-screen,
|
|
@@ -23,6 +24,12 @@ export interface RangeRectProps extends Omit<VirtualDivProps, 'fill'>, Omit<Visu
|
|
|
23
24
|
* concern {@link RectangleSVG} can't express (it has no pane and does no culling).
|
|
24
25
|
*/
|
|
25
26
|
clipInsets?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Draw the frames of this glide while it is in flight; at rest, `range` and `hole` as given. The glide
|
|
29
|
+
* must be heading to `range` (and to `hole`, when there is one) - the caller retargets it. Every
|
|
30
|
+
* rectangle reading one glide moves on one clock.
|
|
31
|
+
*/
|
|
32
|
+
glide?: RangeGlide | null;
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* Places one device-aligned {@link RectangleSVG} at a grid range. Resolves the range to bounds via
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RangeCoords } from '@sheetxl/utils';
|
|
2
2
|
import { VisualSVGProps } from '../svg';
|
|
3
3
|
import { HandleAnchor, DragHandleRenderProps } from './DragHandle';
|
|
4
|
+
import { RangeGlide } from './RangeGlide';
|
|
4
5
|
/** The four corner handles (chart series / formula precedent rectangles use these). */
|
|
5
6
|
export declare const RESIZE_ANCHORS_4: readonly HandleAnchor[];
|
|
6
7
|
/** Corners + edge midpoints. */
|
|
@@ -9,7 +10,7 @@ export declare const RESIZE_ANCHORS_8: readonly HandleAnchor[];
|
|
|
9
10
|
* Props for {@link RangeResizer}. Follows the shared **RangeHandle** contract: a controlled
|
|
10
11
|
* `range` in, the resized range out via `onDrag*`. Extends {@link VisualSVGProps} (like
|
|
11
12
|
* {@link RangeRectProps}) so the outline's paint props — `stroke`, `fill`, `strokeWidth`,
|
|
12
|
-
* `animatedStroke`,
|
|
13
|
+
* `animatedStroke`, … — are passed directly; the handles inherit
|
|
13
14
|
* {@link DEFAULT_HANDLE_STYLE} for whatever you don't set. The grip color follows `stroke`.
|
|
14
15
|
*/
|
|
15
16
|
export interface RangeResizerProps extends VisualSVGProps {
|
|
@@ -51,6 +52,11 @@ export interface RangeResizerProps extends VisualSVGProps {
|
|
|
51
52
|
renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
|
|
52
53
|
/** Keep the current focus across a handle drag (forwarded to {@link DragHandle.preserveFocus}). */
|
|
53
54
|
preserveFocus?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Draw the outline and its grips from this glide's frames while it is in flight, on one clock. The
|
|
57
|
+
* glide must be heading to `range`.
|
|
58
|
+
*/
|
|
59
|
+
glide?: RangeGlide | null;
|
|
54
60
|
}
|
|
55
61
|
/**
|
|
56
62
|
* A draggable, resizable rectangle: an outlined range with {@link DragHandle} grips on the requested
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* (`useWindowDragCells`) for their gesture mechanics — capture, de-dup, edge auto-scroll, touch, cancel.
|
|
6
6
|
*/
|
|
7
7
|
export * from './RangeStyle';
|
|
8
|
+
export * from './RangeGlide';
|
|
9
|
+
export * from './useRangeGlide';
|
|
8
10
|
export * from './RangeRect';
|
|
9
11
|
export * from './RangeInsertBar';
|
|
10
12
|
export * from './DragHandle';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RangeGlide, RangeGlideOptions } from './RangeGlide';
|
|
2
|
+
/**
|
|
3
|
+
* The {@link RangeGlide} shared by every pane of the surface under `key` - one clock for one logical
|
|
4
|
+
* range (a selection), so each pane's copy, and everything that travels with the range, reads the same
|
|
5
|
+
* frame. Never re-renders: readers subscribe to the glide itself.
|
|
6
|
+
*
|
|
7
|
+
* @param key Unique within the surface. `null` returns `null` (no glide).
|
|
8
|
+
* @param options Timing, read when the glide is first created under `key`.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useRangeGlide: (key: string | null, options?: RangeGlideOptions) => RangeGlide | null;
|
|
11
|
+
//# sourceMappingURL=useRangeGlide.d.ts.map
|
|
@@ -55,7 +55,10 @@ export interface RangesPickerLayerProps extends LayerProps {
|
|
|
55
55
|
* {@link RESIZE_ANCHORS_8} for corners + edge midpoints.
|
|
56
56
|
*/
|
|
57
57
|
anchors?: readonly HandleAnchor[];
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Enable the move grip (drag the border to relocate a range, keeping its size). The moved range glides
|
|
60
|
+
* from cell to cell, its corner grips with it. @defaultValue false
|
|
61
|
+
*/
|
|
59
62
|
enabledMove?: boolean;
|
|
60
63
|
/**
|
|
61
64
|
* Don't claim the grid-wide **draw** gesture — the pointer-drag over empty grid that re-scopes the
|
|
@@ -51,11 +51,27 @@ export interface MoveDrop {
|
|
|
51
51
|
/**
|
|
52
52
|
* The drop cannot be performed and a release does nothing: a cut INSERT whose line falls inside the
|
|
53
53
|
* block being dragged, or one that would shift only part of it (see `CoordUtils.classifyInsertCut`
|
|
54
|
-
* - the same call the model makes, so a beam drawn as legal is one the model will accept)
|
|
55
|
-
*
|
|
54
|
+
* - the same call the model makes, so a beam drawn as legal is one the model will accept), or
|
|
55
|
+
* whatever else {@link SelectionLayerProps.isDropRefused} rules out. The preview draws the I-beam
|
|
56
|
+
* (or the ghost) in the refused color instead, and `onMove` never fires.
|
|
56
57
|
*/
|
|
57
58
|
refused?: boolean;
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* How a selection glides from one range to the next — see {@link SelectionLayerProps.animate}.
|
|
62
|
+
*/
|
|
63
|
+
export interface SelectionAnimateOptions {
|
|
64
|
+
/**
|
|
65
|
+
* Names the selection's glide within the surface. Every pane drawing ONE selection must share it, and
|
|
66
|
+
* two selections drawn on one surface must not, so a host that swaps the model underneath (a sheet
|
|
67
|
+
* switch) puts the model's identity in it: a new key starts at rest rather than gliding in from the
|
|
68
|
+
* last model's range.
|
|
69
|
+
* @defaultValue 'selection'
|
|
70
|
+
*/
|
|
71
|
+
key?: string;
|
|
72
|
+
/** How long a glide takes, in ms. @defaultValue 60 */
|
|
73
|
+
duration?: number;
|
|
74
|
+
}
|
|
59
75
|
/**
|
|
60
76
|
* Props for {@link SelectionLayer}.
|
|
61
77
|
*/
|
|
@@ -105,16 +121,26 @@ export interface SelectionLayerProps extends LayerProps {
|
|
|
105
121
|
/** The Shift-drag insert I-beam. Merged over {@link DEFAULT_INSERT_BAR_STYLE}. */
|
|
106
122
|
insertBarStyle?: InsertBarStyle;
|
|
107
123
|
/**
|
|
108
|
-
* Whether a drop would be REFUSED - drawn as a refused I-beam, and inert on release.
|
|
109
|
-
*
|
|
110
|
-
*
|
|
124
|
+
* Whether a drop would be REFUSED - drawn as a refused I-beam (or ghost), and inert on release.
|
|
125
|
+
* Asked for every drop, insert or not; a copy INSERT is never refused, as it leaves no gap to
|
|
126
|
+
* resolve. Defaults to the geometry a grid can see on its own (`CoordUtils.classifyInsertCut`: a
|
|
127
|
+
* cut whose gap the insert would only half close, or a line dropped inside the block itself); a
|
|
128
|
+
* plain drop is then never refused.
|
|
111
129
|
*
|
|
112
130
|
* @remarks
|
|
113
|
-
* A model knows more than the grid does - a merge the shift would tear, a table it would split
|
|
114
|
-
* so a host that has one should answer from it
|
|
115
|
-
*
|
|
131
|
+
* A model knows more than the grid does - a merge the shift would tear, a table it would split, a
|
|
132
|
+
* merged cell a drop would land across - so a host that has one should answer from it
|
|
133
|
+
* (`ICellRange.canInsertFrom`, `ICellRange.getDropTarget`), which is what keeps the preview honest
|
|
134
|
+
* about drops the grid could not have ruled out.
|
|
116
135
|
*/
|
|
117
136
|
isDropRefused?: (from: RangeCoords, drop: MoveDrop) => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Adjust where a plain (non-insert) move drop lands before it drives the ghost, the tip and the
|
|
139
|
+
* callbacks - e.g. onto the whole of a merged cell a single cell is dropped onto, so the ghost shows
|
|
140
|
+
* what the drop will cover. Given the source and the pointer-derived drop, return the destination
|
|
141
|
+
* to use. Omit for no snapping.
|
|
142
|
+
*/
|
|
143
|
+
snapMoveDestination?: (from: RangeCoords, drop: MoveDrop) => RangeCoords;
|
|
118
144
|
/**
|
|
119
145
|
* The mute drawn over the source while a **cut** (plain) move drag is in flight — see
|
|
120
146
|
* {@link DEFAULT_MOVE_SOURCE_STYLE}, which it merges over. `null` draws no mute, leaving the cursor
|
|
@@ -141,8 +167,12 @@ export interface SelectionLayerProps extends LayerProps {
|
|
|
141
167
|
* side-effect, which `drop` describes: a cut-move, a copy, or an insert along `drop.insert`.
|
|
142
168
|
*/
|
|
143
169
|
onMove?: (from: RangeCoords, to: RangeCoords, e: React.PointerEvent, drop: MoveDrop) => void;
|
|
144
|
-
/**
|
|
145
|
-
|
|
170
|
+
/**
|
|
171
|
+
* The move drag started (before any translation) — `from` is the range being moved. `cancel` aborts
|
|
172
|
+
* the drag as Escape would ({@link SelectionLayerProps.onMoveEnd} follows, nothing moved) — for a
|
|
173
|
+
* mode's chrome cancel.
|
|
174
|
+
*/
|
|
175
|
+
onMoveStart?: (from: RangeCoords, e: React.PointerEvent, drop: MoveDrop, cancel: () => void) => void;
|
|
146
176
|
/**
|
|
147
177
|
* Live during the move drag — `to` is the in-flight destination and `drop` what a release would do.
|
|
148
178
|
* Fires on every change, INCLUDING a modifier pressed or released with the pointer standing still
|
|
@@ -184,8 +214,11 @@ export interface SelectionLayerProps extends LayerProps {
|
|
|
184
214
|
* to use. Omit for no snapping.
|
|
185
215
|
*/
|
|
186
216
|
snapFillDestination?: (template: RangeCoords, destination: RangeCoords) => RangeCoords;
|
|
187
|
-
/**
|
|
188
|
-
|
|
217
|
+
/**
|
|
218
|
+
* The fill drag started — `template` is the source range. `cancel` aborts the drag as Escape would
|
|
219
|
+
* ({@link SelectionLayerProps.onFillEnd} follows, nothing filled) — for a mode's chrome cancel.
|
|
220
|
+
*/
|
|
221
|
+
onFillStart?: (template: RangeCoords, e: React.PointerEvent, cancel: () => void) => void;
|
|
189
222
|
/**
|
|
190
223
|
* Live during the fill drag — `destination` is the in-flight fill range (incl. the template). Use it
|
|
191
224
|
* to preview what will be filled (e.g. a value tooltip). The fill axis is `template` vs `destination`.
|
|
@@ -236,13 +269,26 @@ export interface SelectionLayerProps extends LayerProps {
|
|
|
236
269
|
* `pointercancel`), which never reaches a release.
|
|
237
270
|
*/
|
|
238
271
|
onDragModeChange?: (dragMode: 'extend' | 'remove' | null) => void;
|
|
272
|
+
/**
|
|
273
|
+
* The active range is being grown by a drag this layer's own tap cannot see — a header drag, which
|
|
274
|
+
* writes through the model. The gesture tip shows for it as for a body drag-select, off the corner
|
|
275
|
+
* being pulled.
|
|
276
|
+
* @defaultValue false
|
|
277
|
+
*/
|
|
278
|
+
externalDrag?: boolean;
|
|
239
279
|
/**
|
|
240
280
|
* Render a transient tip during a gesture — a drag-select ({@link SelectionGesture} `select`, live
|
|
241
|
-
* R×C), a move (`move`, the destination address), or a fill (`fill`, the preview). Return
|
|
242
|
-
* for no tip (e.g. skip a 1×1 drag-select). The tip floats on the
|
|
281
|
+
* R×C), a move (`move`, the destination address), or a fill (`fill`, the preview). Return an empty
|
|
282
|
+
* node for no tip (e.g. skip a 1×1 drag-select). The tip is a {@link GestureTip}: it floats on the
|
|
283
|
+
* in-flight range in the tooltip group's bubble, and no hover tip opens while the gesture runs.
|
|
243
284
|
*/
|
|
244
285
|
renderTip?: (gesture: SelectionGesture) => React.ReactNode;
|
|
245
|
-
/**
|
|
286
|
+
/**
|
|
287
|
+
* Fixed placement of the gesture tip relative to the in-flight range. By default the tip follows the
|
|
288
|
+
* gesture: off the corner a drag-select is pulling (the one opposite the anchor cell, or the touch grip
|
|
289
|
+
* held), trailing the edge a fill grows from, on the far side of an insert's I-beam, and at the
|
|
290
|
+
* bottom-right of a plain move.
|
|
291
|
+
*/
|
|
246
292
|
tipPlacement?: GridPlacement;
|
|
247
293
|
/**
|
|
248
294
|
* Disable the layer's **built-in keyboard** routing (arrows → {@link ISelection.navigate}, Tab/Enter
|
|
@@ -261,6 +307,16 @@ export interface SelectionLayerProps extends LayerProps {
|
|
|
261
307
|
* @defaultValue true
|
|
262
308
|
*/
|
|
263
309
|
anchorHole?: boolean;
|
|
310
|
+
/**
|
|
311
|
+
* Glide the single-range selection from one range to the next — moving, growing and shrinking edge by
|
|
312
|
+
* edge, its active-cell cutout on the same clock — instead of jumping. Edges that don't change stay
|
|
313
|
+
* put, a scroll during a glide carries it with the cells, and a scroll the user starts ends it. A
|
|
314
|
+
* multi-range selection, and the selection a move or fill commits, jump. Off under
|
|
315
|
+
* `prefers-reduced-motion`.
|
|
316
|
+
*
|
|
317
|
+
* @defaultValue false
|
|
318
|
+
*/
|
|
319
|
+
animate?: boolean | SelectionAnimateOptions;
|
|
264
320
|
}
|
|
265
321
|
/**
|
|
266
322
|
* The selection layer: renders the selection (single-range "rich" treatment vs multi-range), routes
|
|
@@ -29,7 +29,10 @@ export declare const DEFAULT_REMOVE_PREVIEW_STYLE: SelectionRangeStyle;
|
|
|
29
29
|
export interface InsertBarStyle {
|
|
30
30
|
/** The bar color. */
|
|
31
31
|
color?: string;
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* The bar color when the drop would be REFUSED (`MoveDrop.refused`) - the theme's warning. A refused
|
|
34
|
+
* plain drop draws its ghost in it too.
|
|
35
|
+
*/
|
|
33
36
|
refusedColor?: string;
|
|
34
37
|
/** Bar thickness in DEVICE px (the same currency as a border's `alignWidth`). */
|
|
35
38
|
alignWidth?: number;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
+
import { RangeGlide } from '../range';
|
|
2
3
|
import { ISelection } from './ISelection';
|
|
3
4
|
/**
|
|
4
5
|
* Props for {@link SelectionTouchGrips}.
|
|
@@ -6,10 +7,11 @@ import { ISelection } from './ISelection';
|
|
|
6
7
|
export interface SelectionTouchGripsProps {
|
|
7
8
|
/** The selection the touch handles read (for placement) and extend (via `modify`). */
|
|
8
9
|
selection: ISelection;
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The glide the selection is drawn with (the `SelectionLayer`'s, by its `animate` key), so the grips
|
|
12
|
+
* ride its frames on the outline's corners instead of arriving first.
|
|
13
|
+
*/
|
|
14
|
+
glide?: RangeGlide | null;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Touch selection handles: round grips at the active range's corners (or edges, for a whole row/column)
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { SelectionRangeStyle } from './SelectionStyle';
|
|
2
|
+
/**
|
|
3
|
+
* The surface cell a touch grip holds while it drags: the corner it pulls, as the placement a tip hangs
|
|
4
|
+
* off (`null` between drags). The grips and the selection layer are siblings, so the layer learns of
|
|
5
|
+
* the drag here rather than from its own pointer tap.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GRIP_DRAG_KEY = "selection-grip-drag";
|
|
2
8
|
/**
|
|
3
9
|
* Merge a {@link SelectionRangeStyle} override over a base, slot-by-slot (each rectangle merges
|
|
4
10
|
* independently).
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { CellCoords } from '@sheetxl/utils';
|
|
2
2
|
import { IGridPane } from '../surface';
|
|
3
|
+
/**
|
|
4
|
+
* A drag engine's pane: the pane itself, or a getter for the CURRENT one.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A getter is what a hook passes. Panes are rebuilt on structural changes — including any model change
|
|
8
|
+
* that invalidates the grid — and a drag is transient UI that must outlive that: an engine built per pane
|
|
9
|
+
* would drop the gesture in flight the moment a live edit (a reference picked into a field that writes
|
|
10
|
+
* per change) touched the model.
|
|
11
|
+
*/
|
|
12
|
+
export type DragCellsPane = IGridPane | (() => IGridPane);
|
|
3
13
|
/**
|
|
4
14
|
* Repeat interval (ms) for the held-still auto-scroll. While a drag is parked past the viewport edge no
|
|
5
15
|
* pointer-move events fire, so the engine re-applies the drag at the last pointer position on this
|
|
@@ -114,7 +124,7 @@ export interface DragCellsSession {
|
|
|
114
124
|
* Abort cleanup for a CANCELLED gesture — the OS/browser seized the pointer (`pointercancel`: a touch
|
|
115
125
|
* became a system pan/zoom, a pen left range, a window blurred), OR the user pressed **Escape**
|
|
116
126
|
* (Windows-style drag abort). The engine always runs the mechanical teardown (stop the auto-scroll
|
|
117
|
-
* timer,
|
|
127
|
+
* timer, release capture); this hook is for the policy's own rollback —
|
|
118
128
|
* restore the pre-drag selection, clear a rubber-band / preview — WITHOUT committing, since a cancel is
|
|
119
129
|
* an abort, not a release. {@link DragCellsSession.end} never runs on a cancel. The event is absent on
|
|
120
130
|
* an Escape abort (no pointer event drives it).
|
|
@@ -175,7 +185,7 @@ export interface DragCellsController {
|
|
|
175
185
|
onPointerUp: (e: React.PointerEvent) => void;
|
|
176
186
|
/** Abort the in-flight drag (see {@link DragCellsResult.onPointerCancel}). */
|
|
177
187
|
onPointerCancel: (e: React.PointerEvent) => void;
|
|
178
|
-
/** Stop the auto-scroll timer
|
|
188
|
+
/** Stop the auto-scroll timer. Call on unmount (the hook wires this). */
|
|
179
189
|
dispose: () => void;
|
|
180
190
|
}
|
|
181
191
|
/**
|
|
@@ -200,7 +210,7 @@ export interface DragCellsController {
|
|
|
200
210
|
* @param onDragModeChange Notified when the active session's `dragMode` changes (a hook turns this into
|
|
201
211
|
* React state); omit for a headless controller that doesn't surface it.
|
|
202
212
|
*/
|
|
203
|
-
export declare const createDragCells: (pane:
|
|
213
|
+
export declare const createDragCells: (pane: DragCellsPane, onStart: (cell: CellCoords, e: React.PointerEvent) => DragCellsSession | null, onDragModeChange?: (mode: string | null) => void) => DragCellsController;
|
|
204
214
|
/** The headless controller behind {@link useWindowDragCells}. */
|
|
205
215
|
export interface WindowDragCellsController {
|
|
206
216
|
/**
|
|
@@ -209,7 +219,13 @@ export interface WindowDragCellsController {
|
|
|
209
219
|
* `window` regardless, surviving the element repositioning / unmounting. Defaults to `e.currentTarget`.
|
|
210
220
|
*/
|
|
211
221
|
start: (e: React.PointerEvent, cursorEl?: Element | null) => void;
|
|
212
|
-
/**
|
|
222
|
+
/**
|
|
223
|
+
* Abort the in-flight gesture exactly as Escape does: the session's `cancel` rollback, no commit. A
|
|
224
|
+
* no-op between gestures. For a cancel offered by chrome (the status bar's ✕), which a phone needs
|
|
225
|
+
* because it has no Escape.
|
|
226
|
+
*/
|
|
227
|
+
cancel: () => void;
|
|
228
|
+
/** Stop any in-flight gesture (drop the `window` listeners + timer). Call on unmount. */
|
|
213
229
|
dispose: () => void;
|
|
214
230
|
}
|
|
215
231
|
/**
|
|
@@ -223,15 +239,16 @@ export interface WindowDragCellsController {
|
|
|
223
239
|
* The source element is still pointer-captured (for its CURSOR only — see {@link WindowDragCellsController.start});
|
|
224
240
|
* the actual move / up / cancel are `window` listeners, so a re-render or unmount of the handle never
|
|
225
241
|
* interrupts the gesture. Consumers supply `onStart` policy exactly as for the tap engine, and get the
|
|
226
|
-
* same held-still auto-scroll,
|
|
242
|
+
* same held-still auto-scroll, per-cell de-dup, and abort-on-cancel.
|
|
227
243
|
*/
|
|
228
|
-
export declare function createWindowDragCells(pane:
|
|
244
|
+
export declare function createWindowDragCells(pane: DragCellsPane, onStart: (cell: CellCoords, e: React.PointerEvent) => DragCellsSession | null, onDragModeChange?: (mode: string | null) => void,
|
|
229
245
|
/** Override cell resolution (a touch grip resolves against the pane the finger is over). */
|
|
230
246
|
resolveCell?: (clientX: number, clientY: number) => CellCoords | null): WindowDragCellsController;
|
|
231
247
|
/**
|
|
232
248
|
* The React hook over {@link createDragCells}: reads the pane from context, owns the `dragMode` state,
|
|
233
|
-
* and stops the auto-scroll timer on unmount. `onStart`
|
|
234
|
-
* closure over changing values (selection, options) needs no handler rebuild
|
|
249
|
+
* and stops the auto-scroll timer on unmount. `onStart` and the pane are read live, so a consumer's
|
|
250
|
+
* closure over changing values (selection, options) needs no handler rebuild — and a pane rebuilt
|
|
251
|
+
* mid-drag does not end the drag (see {@link DragCellsPane}).
|
|
235
252
|
*/
|
|
236
253
|
export declare const useDragCells: (onStart: (cell: CellCoords, e: React.PointerEvent) => DragCellsSession | null) => DragCellsResult;
|
|
237
254
|
/** Handlers + transient `dragMode` produced by {@link useWindowDragCells}. */
|
|
@@ -242,13 +259,16 @@ export interface WindowDragCellsResult {
|
|
|
242
259
|
* `currentTarget` other than that element (e.g. a grip below an interactive child).
|
|
243
260
|
*/
|
|
244
261
|
start: (e: React.PointerEvent, cursorEl?: Element | null) => void;
|
|
262
|
+
/** Abort the in-flight gesture as Escape does (see {@link WindowDragCellsController.cancel}). */
|
|
263
|
+
cancel: () => void;
|
|
245
264
|
/** The active session's `dragMode`, or `null` between drags (flips only on start / end). */
|
|
246
265
|
dragMode: string | null;
|
|
247
266
|
}
|
|
248
267
|
/**
|
|
249
268
|
* The React hook over {@link createWindowDragCells}: reads the pane from context, owns the `dragMode`
|
|
250
269
|
* state, and disposes the gesture on unmount. For handle / grip flavors (fill, resize, move, touch grips)
|
|
251
|
-
* whose element repositions or unmounts mid-drag. `onStart`
|
|
270
|
+
* whose element repositions or unmounts mid-drag. `onStart` and the pane are read live, as in
|
|
271
|
+
* {@link useDragCells}.
|
|
252
272
|
*/
|
|
253
273
|
export declare const useWindowDragCells: (onStart: (cell: CellCoords, e: React.PointerEvent) => DragCellsSession | null) => WindowDragCellsResult;
|
|
254
274
|
//# sourceMappingURL=useDragCells.d.ts.map
|
|
@@ -35,6 +35,8 @@ export interface GridPaneDef {
|
|
|
35
35
|
rowIndex: number;
|
|
36
36
|
elemStage: React.ReactNode;
|
|
37
37
|
refStage: IStageElement | null;
|
|
38
|
+
/** See {@link IGridPane.registerFocusTarget}. */
|
|
39
|
+
focusTarget?: (() => HTMLElement | null) | null;
|
|
38
40
|
scrollOrientation: OrientationFlags;
|
|
39
41
|
insets: Rectangle;
|
|
40
42
|
}
|