@sheetxl/grid-react 0.7.28-beta.4 → 0.8.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/browser/cjs/index.cjs +3 -3
  2. package/browser/esm/index.mjs +2052 -1487
  3. package/licenses/dependencies.txt +35 -1
  4. package/node/esm/index.mjs +1662 -1108
  5. package/package.json +3 -3
  6. package/types/src/cell/CellRenderer.d.ts +14 -3
  7. package/types/src/editor/DefaultEditable.d.ts +9 -0
  8. package/types/src/editor/EditSession.d.ts +6 -0
  9. package/types/src/editor/EditState.d.ts +6 -0
  10. package/types/src/editor/EditorInput.d.ts +2 -1
  11. package/types/src/editor/useEditorKeymap.d.ts +11 -0
  12. package/types/src/header/useHeaderSelectionPointer.d.ts +6 -2
  13. package/types/src/layer/GridFloatPanel.d.ts +18 -0
  14. package/types/src/layer/HoverTipLayer.d.ts +20 -47
  15. package/types/src/layer/RangeFloatLayer.d.ts +146 -0
  16. package/types/src/layer/StickyAnchor.d.ts +2 -4
  17. package/types/src/layer/StickyFloat.d.ts +4 -1
  18. package/types/src/layer/index.d.ts +3 -1
  19. package/types/src/layer/useGridAnchor.d.ts +23 -6
  20. package/types/src/layer/useRangeHover.d.ts +5 -4
  21. package/types/src/list/CheckboxList.d.ts +11 -0
  22. package/types/src/list/List.d.ts +14 -5
  23. package/types/src/list/ListSearch.d.ts +31 -0
  24. package/types/src/list/ListSurface.d.ts +93 -0
  25. package/types/src/list/PickList.d.ts +71 -0
  26. package/types/src/list/_measureText.d.ts +14 -0
  27. package/types/src/list/index.d.ts +3 -0
  28. package/types/src/range/DragHandle.d.ts +14 -0
  29. package/types/src/range/Draggable.d.ts +111 -6
  30. package/types/src/range/FillHandle.d.ts +28 -1
  31. package/types/src/range/RangeInsertBar.d.ts +91 -0
  32. package/types/src/range/index.d.ts +2 -0
  33. package/types/src/selection/SelectionLayer.d.ts +91 -9
  34. package/types/src/selection/SelectionStyle.d.ts +30 -0
  35. package/types/src/selection/useDragCells.d.ts +36 -0
@@ -0,0 +1,31 @@
1
+ import { default as React } from 'react';
2
+ /**
3
+ * Props for {@link ListSearch}.
4
+ */
5
+ export interface ListSearchProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange'> {
6
+ /** The search text. */
7
+ value: string;
8
+ /** The search text changed — including to `''` when Escape clears it. */
9
+ onChange: (value: string) => void;
10
+ /** Escape on an empty field: the search has nothing left to clear, so the host closes. */
11
+ onClose?: () => void;
12
+ /** Ref for the input. */
13
+ ref?: React.Ref<HTMLInputElement>;
14
+ }
15
+ /**
16
+ * The rows of `items` whose `text` contains `query`, ignoring case. An empty query keeps every row.
17
+ */
18
+ export declare const filterByText: <T extends {
19
+ text: string;
20
+ }>(items: readonly T[], query: string) => readonly T[];
21
+ /**
22
+ * The **search field** shown above a list ({@link PickList}, `CheckboxList`): a plain input that
23
+ * narrows the rows as typed. Filtering itself is the host's, with {@link filterByText}.
24
+ *
25
+ * @remarks
26
+ * Escape clears the text first and closes only when there is none left, so a mistyped search is
27
+ * one key from a full list and two from a closed one. Every other key is the host's through
28
+ * `onKeyDown`, which is how the arrows and Enter reach the list while the field keeps focus.
29
+ */
30
+ export declare const ListSearch: React.ForwardRefExoticComponent<Omit<ListSearchProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
31
+ //# sourceMappingURL=ListSearch.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { default as React } from 'react';
2
+ import { Viewport } from '@sheetxl/utils-react';
3
+ import { CellLayerProps, CellRendererProps } from '../cell';
4
+ import { LayerEventProps } from '../layer';
5
+ import { ISelection } from '../selection';
6
+ /**
7
+ * Props for {@link ListSurface}.
8
+ */
9
+ export interface ListSurfaceProps extends React.HTMLAttributes<HTMLDivElement> {
10
+ /** Number of rows. */
11
+ itemCount: number;
12
+ /**
13
+ * Render one row, as a grid cell — the `CellLayer` renderer, so the row is handed its bounds, its
14
+ * pane (zoom) and its resolved `value`. The cell is positioned and clipped by the tiling engine.
15
+ */
16
+ renderCell: (props: CellRendererProps) => React.ReactNode;
17
+ /**
18
+ * Resolve the visible rows to render (and their `value`). Omit to render every visible row with no
19
+ * pre-resolved value — `renderCell` then derives its content from the row index.
20
+ */
21
+ getVisibleCells?: CellLayerProps['getVisibleCells'];
22
+ /**
23
+ * Uniform row height in px. Also the list's natural height (`itemCount * itemHeight`) unless
24
+ * `style.height` overrides it. @defaultValue 24
25
+ */
26
+ itemHeight?: number;
27
+ /**
28
+ * Width the **column** should be, in px — the list sizes itself to this PLUS its own chrome (the
29
+ * vertical scrollbar's gutter), so the column actually ends up this wide.
30
+ *
31
+ * @remarks
32
+ * The distinction matters because the natural thing to pass — a measured content width, e.g.
33
+ * `ICellRange.getBestFit().width` — describes the column, not the box. Setting it as the box's
34
+ * `width` instead silently hands the gutter's worth of pixels back, and content sized to fit
35
+ * exactly then does not. Omit to size from `style` and let the column take what is left.
36
+ */
37
+ contentWidth?: number;
38
+ /**
39
+ * The selection model — the active-row highlight + Arrow-key navigation. The surface's *state*.
40
+ * Owned by the caller (so it can read what's selected); a private one is created when omitted.
41
+ */
42
+ selection?: ISelection;
43
+ /**
44
+ * Event taps installed as the list's TOP layer — so they are offered keys and pointers before the
45
+ * built-in Arrow navigation and before the selection's own hit test. Handle Space/Enter here (the
46
+ * grid would otherwise consume them) and `stopPropagation` to override; anything you don't consume
47
+ * falls through to navigation.
48
+ */
49
+ propsLayer?: LayerEventProps;
50
+ /** Take focus on mount, so the Arrow keys work without a click first. @defaultValue false */
51
+ autoFocus?: boolean;
52
+ /**
53
+ * Whether the vertical scrollbar (and its gutter) is drawn. `'auto'` draws it only while the rows
54
+ * overflow the box — a list that fits has nothing to scroll and the gutter would only widen it,
55
+ * and one squeezed by its host (a popup capped to the window) scrolls as a plain list does.
56
+ * @defaultValue 'auto'
57
+ */
58
+ showScrollbar?: boolean | 'auto';
59
+ /**
60
+ * Bring `row` into view. Read by identity: a new object scrolls again, the same object does not, so
61
+ * a caller keeps one per intent (the row highlighted on open, the row a key moved to). The scroll
62
+ * waits for the surface to have a size, so it can be set before the first paint.
63
+ */
64
+ scrollTo?: {
65
+ row: number;
66
+ } | null;
67
+ /**
68
+ * The scroll state, when the caller owns it — a host that sizes the list itself (`setSizes`) or
69
+ * drives its scroll. A private one is created when omitted.
70
+ */
71
+ viewport?: Viewport;
72
+ /** Opaque content cache-bust forwarded to the `CellLayer`. */
73
+ cacheToken?: unknown;
74
+ /** Ref for the Element. */
75
+ ref?: React.Ref<HTMLDivElement>;
76
+ }
77
+ /**
78
+ * The **list chrome**: a virtualized single-column `GridSurface` wearing the grid's own layers — a
79
+ * `CellLayer` for the rows and a `SelectionLayer` for the active-row highlight + Arrow keys — dressed
80
+ * so it reads as a list rather than a grid.
81
+ *
82
+ * @remarks
83
+ * Not a list *component*: it renders no row content of its own, has no notion of an item, and adds no
84
+ * padding — {@link ListSurfaceProps.renderCell} owns the whole row box. It exists because the lists
85
+ * built on it ({@link List}, whose rows are arbitrary React, and {@link PickList}, whose rows are text)
86
+ * differ ONLY in that renderer. Everything they share — the viewport, the measured single column, the
87
+ * selection styling and its rationale, the layer order — is here, once.
88
+ *
89
+ * The single column tracks the measured content width, so a row spans the full list; scrolling is
90
+ * vertical only.
91
+ */
92
+ export declare const ListSurface: React.ForwardRefExoticComponent<Omit<ListSurfaceProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
93
+ //# sourceMappingURL=ListSurface.d.ts.map
@@ -0,0 +1,71 @@
1
+ import { default as React } from 'react';
2
+ import { Viewport } from '@sheetxl/utils-react';
3
+ /** One row of a {@link PickList}: what it shows. */
4
+ export interface PickListItem {
5
+ /** The row's text. */
6
+ text: string;
7
+ }
8
+ export interface PickListAttributes {
9
+ isPickList: () => true;
10
+ /** The highlighted row, or `-1` when the list is empty. */
11
+ getHighlightIndex: () => number;
12
+ /** Move the highlight `delta` rows (clamped to the list) and bring it into view. */
13
+ moveHighlight: (delta: number) => void;
14
+ /** Choose the highlighted row, as Enter does. `false` when there is none. */
15
+ chooseHighlighted: () => boolean;
16
+ }
17
+ export interface IPickListElement extends HTMLDivElement, PickListAttributes {
18
+ }
19
+ /**
20
+ * Props for {@link PickList}.
21
+ */
22
+ export interface PickListProps extends React.HTMLAttributes<HTMLDivElement> {
23
+ /** The rows, in the order shown. */
24
+ items: readonly PickListItem[];
25
+ /** A row was chosen — clicked, or Enter/Space on the highlighted row. */
26
+ onChoose?: (index: number, item: PickListItem) => void;
27
+ /**
28
+ * The row highlighted when the list (re)appears — the current value's, so a pick from the keyboard
29
+ * starts from it. Out of range, or omitted, highlights the first row. Re-read when `items` changes.
30
+ */
31
+ highlightIndex?: number;
32
+ /** Uniform row height in px. @defaultValue 24 */
33
+ itemHeight?: number;
34
+ /**
35
+ * Rows shown before the list scrolls. The list is as tall as its rows up to this many; a caller's
36
+ * `style.maxHeight` caps it further (a popup fit to the space left). @defaultValue 8
37
+ */
38
+ visibleRows?: number;
39
+ /**
40
+ * Width (px) the rows need, when the caller already knows it. Otherwise it is measured from the
41
+ * widest `text` in the list's own font (see {@link PickListProps.measureText}).
42
+ */
43
+ contentWidth?: number;
44
+ /**
45
+ * How to measure a row's text, in px. @defaultValue a canvas measure in the font `style` names
46
+ * (or the browser default when it names none).
47
+ */
48
+ measureText?: (text: string) => number;
49
+ /** Render a row's content. @defaultValue the text, clipped with an ellipsis. */
50
+ renderItem?: (item: PickListItem, index: number) => React.ReactNode;
51
+ /** Take focus on mount, so the Arrow keys work without a click first. @defaultValue false */
52
+ autoFocus?: boolean;
53
+ /** The scroll state, when the caller owns it (see `ListSurfaceProps.viewport`). */
54
+ viewport?: Viewport;
55
+ /** Ref for the Element. */
56
+ ref?: React.Ref<IPickListElement>;
57
+ }
58
+ /**
59
+ * A single-choice **pick list** of text rows over {@link ListSurface}: a dropdown's menu.
60
+ *
61
+ * @remarks
62
+ * The list's whole state is which row is highlighted; choosing is a callback. One row is always
63
+ * highlighted (the current value's on open, then wherever the keys or the pointer put it), Arrow keys
64
+ * move it, Enter or Space choose it, a click chooses the clicked row. The width follows the widest
65
+ * row's text; the height follows the rows up to {@link PickListProps.visibleRows}, then scrolls.
66
+ *
67
+ * A host with its own input (a search field above the list) drives the highlight through the element's
68
+ * {@link PickListAttributes} rather than moving focus into the list.
69
+ */
70
+ export declare const PickList: React.ForwardRefExoticComponent<Omit<PickListProps, "ref"> & React.RefAttributes<IPickListElement>>;
71
+ //# sourceMappingURL=PickList.d.ts.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The CSS `font` shorthand a box's style resolves to, for measuring text the way that box draws it.
3
+ * Falls back to a plain sans-serif at the browser's default size when the style names no font.
4
+ */
5
+ export declare const fontFromStyle: (style?: React.CSSProperties | null) => string;
6
+ /**
7
+ * The width (px) of `text` drawn in `font` (a CSS `font` shorthand).
8
+ *
9
+ * @remarks
10
+ * Measured on a shared offscreen canvas. Where no 2-D context exists (a non-DOM test host) the width
11
+ * is estimated from the font size, so a caller sizing a box never sees `0` or `NaN`.
12
+ */
13
+ export declare const measureTextWidth: (text: string, font: string) => number;
14
+ //# sourceMappingURL=_measureText.d.ts.map
@@ -1,3 +1,6 @@
1
+ export * from './ListSurface';
1
2
  export * from './List';
2
3
  export * from './CheckboxList';
4
+ export * from './PickList';
5
+ export * from './ListSearch';
3
6
  //# sourceMappingURL=index.d.ts.map
@@ -22,6 +22,8 @@ export interface DragHandleRenderProps {
22
22
  * repositioning under an edge auto-scroll).
23
23
  */
24
24
  handleProps: {
25
+ /** The handle's identifying class(es) — `drag-handle` plus whatever the flavor tagged it with. */
26
+ className?: string;
25
27
  onPointerDown: (e: React.PointerEvent) => void;
26
28
  onDoubleClick?: (e: React.MouseEvent) => void;
27
29
  onPointerEnter?: (e: React.PointerEvent) => void;
@@ -108,6 +110,10 @@ export interface DragHandleProps {
108
110
  renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
109
111
  /** Static content inside the default dot. Ignored when `renderHandle` is set. */
110
112
  children?: React.ReactNode;
113
+ /**
114
+ * Extra class(es) for the handle element. Joined with the `drag-handle` tag every handle carries,
115
+ * so a flavor names itself (`fill-handle`, `resize-handle`) without losing the generic hook.
116
+ */
111
117
  className?: string;
112
118
  style?: React.CSSProperties;
113
119
  /**
@@ -117,6 +123,14 @@ export interface DragHandleProps {
117
123
  * it preserves whatever had focus rather than stealing it. @defaultValue false
118
124
  */
119
125
  preserveFocus?: boolean;
126
+ /**
127
+ * Also start a drag from the **secondary** (right) button — Excel's right-drag of the fill handle,
128
+ * which offers a menu on release instead of filling. The drag callbacks receive the event, so a
129
+ * consumer tells the two apart by `e.button`. The `contextmenu` the button raises (on press on some
130
+ * platforms, on release on others) is swallowed for the gesture's duration: the drag IS the intent.
131
+ * @defaultValue false
132
+ */
133
+ secondaryButton?: boolean;
120
134
  }
121
135
  /**
122
136
  * A generic, draggable handle anchored to a corner/edge of a grid range. It owns only the **gesture
@@ -1,4 +1,86 @@
1
- import { RangeCoords } from '@sheetxl/utils';
1
+ import { RangeAxis, CellCoords, RangeCoords } from '@sheetxl/utils';
2
+ import { IGridPane } from '../surface';
3
+ import { DragModifiers } from '../selection/useDragCells';
4
+ /**
5
+ * Where an **insert**-style drop would land: the dragged block placed at `origin`, with the cells there
6
+ * stepping aside along `axis`. `origin`'s edge on that axis is the grid line the I-beam is drawn on.
7
+ *
8
+ * @remarks
9
+ * An insert re-anchors where a move does not. A move keeps the grab offset (grab A1:C3 by C3 and the
10
+ * block stays under your hand); an insert has no "under whatever you grabbed" to preserve — it goes at
11
+ * the line you are pointing at, so the block's top-left is the thing that must land there. Same reason
12
+ * the I-beam starts at the pointer's cell rather than trailing off to one side of it.
13
+ */
14
+ export interface DragInsert {
15
+ /** `Row` — the cells shift down (a horizontal beam); `Column` — they shift right (a vertical one). */
16
+ axis: RangeAxis;
17
+ /**
18
+ * Where the block's TOP-LEFT lands. On the cross axis it is the cell under the pointer (the
19
+ * re-anchor); on `axis` it is the grid line nearest the pointer, which may be the FAR side of that
20
+ * cell — an index of `n` meaning the line before row/column `n`, so the two readings coincide.
21
+ */
22
+ origin: CellCoords;
23
+ }
24
+ /**
25
+ * The in-flight drag's live state, reported alongside the translated range.
26
+ *
27
+ * @remarks
28
+ * `mods` is the reason this exists: a modifier pressed with the pointer standing still changes what
29
+ * the drop will DO, and no pointer event carries that. Read the modifiers from here, never off the
30
+ * event handed to the same callback (which is the last one the *pointer* fired).
31
+ */
32
+ export interface DragMoveInfo {
33
+ /** The live Ctrl / Shift / Alt / Meta state (Excel: Ctrl = copy, Shift = insert). */
34
+ mods: DragModifiers;
35
+ /**
36
+ * Where an insert-style (Shift) drop would land, resolved from the pointer's position within its
37
+ * cell (see {@link insertAt}). Tracked whether or not Shift is down, so the I-beam appears the
38
+ * instant it goes down. `null` only when the pane could not resolve the pointer. {@link insertRange}
39
+ * turns it into a destination range.
40
+ */
41
+ insert: DragInsert | null;
42
+ }
43
+ /**
44
+ * The insert a drop would perform from the pointer's position, as Excel resolves it — the first half
45
+ * of the drop geometry ({@link insertRange} is the second: this picks the LINE, that places the block
46
+ * on it). Two rules that are deliberately NOT symmetric:
47
+ *
48
+ * - the pointer's **horizontal** position within the cell picks the axis: within
49
+ * {@link INSERT_COLUMN_BAND} of a vertical edge it is a column insert, anywhere in the middle half a
50
+ * row insert. So rows are the easy, default direction and columns are aimed for — you go for a
51
+ * column edge to get one. Skipped entirely when `forced` names the axis (a whole row / column);
52
+ * - the line then snaps to the **nearest** grid line on that axis, which may be the far side of the
53
+ * cell (below it / to the right of it), not only its leading edge. A forced axis is therefore a
54
+ * straight 50/50 across the cell, the axis pick no longer taking a cut of it.
55
+ *
56
+ * The cross axis re-anchors to the pointer's own cell, so the beam always starts where you point.
57
+ *
58
+ * @remarks
59
+ * Both measured proportionally (a fraction of the cell), which is how Excel behaves across cell sizes
60
+ * — and necessary anyway: a row is typically a third the height of a column's width, so any pixel
61
+ * threshold would behave differently at every zoom and row height.
62
+ *
63
+ * A MERGE is IGNORED: the line falls on the true cell under the pointer, exactly as if the merge
64
+ * weren't there. It has to be resolved here rather than taken from the drag's cell, which is a
65
+ * merge's top-left (what `getCellFromPointer` reports, and the right answer for a move destination) —
66
+ * measured against that anchor the fractions describe a box the pointer is nowhere near, and the far
67
+ * line lands one cell in from it, which is how the beam ends up drawn against the top-left corner of
68
+ * a merge instead of under the cursor. Snapping to the merge's outer edge instead is no better: the
69
+ * beam then jumps to a far edge the pointer isn't near. A line that falls INSIDE a merge is a drop
70
+ * the model refuses, which the preview already says in its own way — see `MoveDrop.refused`.
71
+ */
72
+ export declare const insertAt: (pane: IGridPane, cell: CellCoords, clientX: number, clientY: number, forced: RangeAxis | null, prior: DragInsert | null) => DragInsert | null;
73
+ /**
74
+ * Where an **insert** drop of `range` lands: the block placed at {@link DragInsert.origin} — its
75
+ * top-left RE-ANCHORED on the pointer's own cell across the insert axis, and on the snapped grid line
76
+ * along it (existing cells shifting from there). Clamped into `bounds`, which can pull a block back so
77
+ * it still fits at the far edge.
78
+ *
79
+ * Unlike a move — which keeps the offset of wherever you grabbed the block — an insert drops the grab
80
+ * offset entirely: it goes where you are pointing. The same function drives the I-beam, the tip and
81
+ * the commit, so what is drawn is what lands.
82
+ */
83
+ export declare const insertRange: (range: RangeCoords, insert: DragInsert, bounds: RangeCoords) => RangeCoords;
2
84
  /**
3
85
  * Props for {@link Draggable}.
4
86
  */
@@ -43,21 +125,39 @@ export interface DraggableProps {
43
125
  */
44
126
  onDragStateChange?: (dragging: boolean) => void;
45
127
  /** Drag started; `range` is the original. */
46
- onDragStart?: (range: RangeCoords, e: React.PointerEvent) => void;
128
+ onDragStart?: (range: RangeCoords, e: React.PointerEvent, info: DragMoveInfo) => void;
47
129
  /**
48
130
  * The translated range during the drag. Wire it to update `range` (the content follows the pointer —
49
131
  * "live"), or to position a composed ghost preview. `Draggable` itself stays silent.
132
+ *
133
+ * @remarks
134
+ * Fires when the destination changes AND when the modifiers do (Ctrl / Shift pressed or released with
135
+ * the pointer standing still) — the two are one signal on purpose: everything a consumer reflects
136
+ * during a drag (a mode description, a cursor, a drop preview) depends on both, and splitting them
137
+ * into two callbacks is how one of the pair ends up stale. Since a modifier change fires no pointer
138
+ * event, `e` is then the last one the POINTER produced: read the modifiers off `info.mods`, not `e`.
50
139
  */
51
- onDragMove?: (range: RangeCoords, e: React.PointerEvent) => void;
52
- /** Drag released: commit the translated range. */
53
- onDragEnd?: (range: RangeCoords, e: React.PointerEvent) => void;
140
+ onDragMove?: (range: RangeCoords, e: React.PointerEvent, info: DragMoveInfo) => void;
141
+ /** Drag released: commit the translated range. `info` carries the modifiers held at the drop. */
142
+ onDragEnd?: (range: RangeCoords, e: React.PointerEvent, info: DragMoveInfo) => void;
54
143
  /**
55
144
  * Render a **ghost** at the in-flight translated range while a drag is in progress (nothing is rendered
56
145
  * when idle). `Draggable` owns the preview state internally, so the consumer doesn't have to stash it
57
146
  * — return e.g. a faded {@link RangeRect} to trail the pointer while the original stays put (the
58
147
  * cut-move pattern). For a "live" feel (content follows the pointer) wire `onDragMove → range` instead.
148
+ *
149
+ * `info` re-renders the preview on a modifier change, so the ghost can become an insert I-beam the
150
+ * moment Shift goes down.
59
151
  */
60
- renderMove?: (range: RangeCoords) => React.ReactNode;
152
+ renderMove?: (range: RangeCoords, info: DragMoveInfo) => React.ReactNode;
153
+ /**
154
+ * The cursor painted for the duration of the drag. The grip's element is pointer-captured, so ITS
155
+ * cursor is the one the browser shows once a drag is in flight — pass a mode-derived cursor (`copy`
156
+ * while Ctrl is held, say) to make the drop mode visible under the pointer.
157
+ *
158
+ * @defaultValue 'grabbing'
159
+ */
160
+ dragCursor?: string;
61
161
  /**
62
162
  * Keep the current focus across a move drag (`preventDefault` on pointer-down, suppressing the compat
63
163
  * `mousedown` that would move focus to the grid's `tabIndex` Stage). Used by the reference-range picker
@@ -74,6 +174,11 @@ export interface DraggableProps {
74
174
  * name, so the nearest one is still reported but rarely means anything.
75
175
  */
76
176
  onEdgeDoubleClick?: (range: RangeCoords, edge: RangeEdge, e: React.PointerEvent) => void;
177
+ /**
178
+ * Extra class(es) for the grip box. Joined with the `drag-grip` tag every grip carries, so a
179
+ * flavor names itself (`move-grip`) without losing the generic hook.
180
+ */
181
+ className?: string;
77
182
  }
78
183
  /** Which border of a range a gesture landed on. */
79
184
  export type RangeEdge = 'top' | 'bottom' | 'left' | 'right';
@@ -1,5 +1,5 @@
1
1
  import { default as React } from 'react';
2
- import { RangeCoords } from '@sheetxl/utils';
2
+ import { CellCoords, RangeCoords } from '@sheetxl/utils';
3
3
  import { VisualSVGProps } from '../svg';
4
4
  import { DragHandleRenderProps } from './DragHandle';
5
5
  /**
@@ -29,6 +29,16 @@ export interface FillHandleProps extends VisualSVGProps {
29
29
  onDragMove?: (range: RangeCoords, e: React.PointerEvent) => void;
30
30
  /** Drag released: commit the fill to this destination range. Fires only when there IS a fill. */
31
31
  onDragEnd?: (range: RangeCoords, e: React.PointerEvent) => void;
32
+ /**
33
+ * Released from a **secondary-button** (right) drag: offer a menu of fills at the pointer instead
34
+ * of committing one — Excel's right-drag of the fill handle. Fires INSTEAD of
35
+ * {@link FillHandleProps.onDragEnd} for that gesture (then {@link FillHandleProps.onDragDone} as
36
+ * usual); supplying it is what enables the right-button drag at all. Left-drag is untouched.
37
+ *
38
+ * The dashed preview STAYS on the release — the fill is still being decided — until `done` is
39
+ * called: once the menu has answered, or closed without answering. A new drag clears it regardless.
40
+ */
41
+ onMenu?: (range: RangeCoords, e: React.PointerEvent, done: () => void) => void;
32
42
  /**
33
43
  * The drag finished — fires on **every** release after a press (unlike {@link FillHandleProps.onDragEnd},
34
44
  * which fires only when a fill was drawn) AND on an abort (Escape / OS pointercancel). Use it for
@@ -49,6 +59,23 @@ export interface FillHandleProps extends VisualSVGProps {
49
59
  */
50
60
  renderHandle?: (props: DragHandleRenderProps) => React.ReactNode;
51
61
  }
62
+ /**
63
+ * The destination range when filling `template` toward `to`. Excel-style single axis: extend along
64
+ * whichever direction the pointer reaches furthest beyond the template, clamped to `bounds`. The
65
+ * destination includes the template.
66
+ *
67
+ * A pointer INSIDE the template shrinks it instead — Excel's drag of the fill handle back over the
68
+ * source, which vacates the cells past the pointer. Only the edges the handle sits on can move, the
69
+ * bottom and the right, along whichever axis the pointer pulled furthest from the corner; a whole-row
70
+ * or whole-column template has no such edge on its long axis and shrinks only on the other. Returns
71
+ * `null` when `to` is the handle's own corner (nothing to fill or vacate).
72
+ */
73
+ export declare const resolveFillDestination: (template: RangeCoords, to: CellCoords, bounds: RangeCoords) => RangeCoords | null;
74
+ /**
75
+ * The cells a shrinking `destination` vacates from `template` — the strip past its bottom or right
76
+ * edge — or `null` for an outward fill.
77
+ */
78
+ export declare const vacatedByFill: (template: RangeCoords, destination: RangeCoords) => RangeCoords | null;
52
79
  /**
53
80
  * The fill layer: a {@link DragHandle} at a corner of the `range` (bottom-right by default). It owns
54
81
  * the handle placement — including the whole-row/column special-casing (pin to bottom-left / top-right
@@ -0,0 +1,91 @@
1
+ import { RangeAxis, RangeCoords } from '@sheetxl/utils';
2
+ /**
3
+ * Props for {@link RangeInsertBar}.
4
+ */
5
+ export interface RangeInsertBarProps {
6
+ /**
7
+ * The range the insert lands on — the block's destination. Its LEADING edge on {@link axis} is the
8
+ * grid line the bar is drawn at (`insertRange` produces exactly this), and its other axis is the
9
+ * extent the bar spans, so the bar shows how wide the incoming block is.
10
+ */
11
+ range: RangeCoords;
12
+ /**
13
+ * Which way the existing cells shift: `Row` draws a horizontal bar on the range's top edge (cells
14
+ * shift down), `Column` a vertical bar on its left edge (cells shift right).
15
+ */
16
+ axis: RangeAxis;
17
+ /** The bar color. @defaultValue the grid primary. */
18
+ color?: string;
19
+ /**
20
+ * Bar thickness in DEVICE px — the same currency as a selection outline's `alignWidth`, so a bar and
21
+ * a border of the same number read as the same weight at any dpr.
22
+ *
23
+ * @defaultValue 3
24
+ */
25
+ alignWidth?: number;
26
+ /**
27
+ * How far each end cap reaches past the bar, per side, in DEVICE px. `0` draws a plain bar.
28
+ *
29
+ * @defaultValue 3
30
+ */
31
+ capExtent?: number;
32
+ /**
33
+ * How long the bar takes to travel to a new line, in ms. `0` places it instantly.
34
+ *
35
+ * @defaultValue 100
36
+ */
37
+ transitionDuration?: number;
38
+ /**
39
+ * The curve it travels on. The default is a hard decelerate — most of the distance is covered in
40
+ * the first third, then it settles — which is what reads as SNAPPING onto a gridline rather than
41
+ * sliding to a stop near one.
42
+ *
43
+ * @defaultValue `cubic-bezier(0.16, 1, 0.3, 1)`
44
+ */
45
+ transitionEasing?: string;
46
+ /**
47
+ * How long the beam takes to TURN, in ms — the outgoing orientation drawing in and the incoming one
48
+ * drawing out, together. `0` turns instantly. Shorter than
49
+ * {@link RangeInsertBarProps.transitionDuration} on purpose: a turn is a change of meaning (rows
50
+ * step aside, not columns), so it wants to read as a flip rather than as travel.
51
+ *
52
+ * @defaultValue 70
53
+ */
54
+ axisDuration?: number;
55
+ }
56
+ /**
57
+ * The **insert I-beam**: the Excel bar shown while Shift-dragging a range, marking the grid line the
58
+ * block will be inserted at (rather than dropped over). Drawn as a heavy line with a short cap at each
59
+ * end — the serifs are what separate "inserting AT this line" from "a border of something".
60
+ *
61
+ * @remarks
62
+ * Three device-aligned boxes rather than an SVG path: each is snapped independently, so the bar sits
63
+ * on the boundary pixel exactly like a gridline or a selection edge, at any dpr or zoom. It is placed
64
+ * from the destination range alone ({@link RangeInsertBarProps.range}) — the same range the drop
65
+ * commits — so the drawing and the operation can never disagree.
66
+ *
67
+ * The bar is clipped to the pane (a whole-column insert spans millions of pixels, and a box that size
68
+ * is a compositing tax for ink nobody can see), and an end cap is drawn only where the block's real
69
+ * end is on screen — a cap at a clipped edge would claim an end that isn't there.
70
+ *
71
+ * It GLIDES between lines: the three boxes transition their placement and their size together, on one
72
+ * duration and one curve, so they travel as a single object, and the color rides the same timing (a
73
+ * drop turning refused fades rather than blinks).
74
+ *
75
+ * A change of AXIS is a TURN, and both orientations are always mounted to draw it: each keeps its own
76
+ * shape forever and shows or hides by scaling, so the outgoing beam draws in while the incoming one
77
+ * draws out and something is on screen the whole way. They scale about the corner they SHARE — the
78
+ * destination's top-left, where a row beam starts running right and a column beam starts running
79
+ * down — so the pair reads as one beam swinging around that point, which is the part a rotation would
80
+ * have sold. That pivot is stated in PANE coordinates: these boxes lay out at the pane origin and
81
+ * move by transform, so their transform-origin is measured from the pane's corner, and a pivot stated
82
+ * relative to the box would scale the placement along with the beam and fly it in from the top-left.
83
+ *
84
+ * Two things this deliberately does NOT do. It does not interpolate one orientation into the other:
85
+ * width and height would cross and a bar whose width and height cross is a box at the halfway point,
86
+ * which smears. And it does not cut the old one and grow the new one, which leaves a moment with no
87
+ * beam at all — read as a flicker. (A real rotation is not something one transition can express: the
88
+ * orientations differ in LENGTH as well as angle.)
89
+ */
90
+ export declare const RangeInsertBar: import('react').MemoExoticComponent<(props: RangeInsertBarProps) => import("react").JSX.Element | null>;
91
+ //# sourceMappingURL=RangeInsertBar.d.ts.map
@@ -6,8 +6,10 @@
6
6
  */
7
7
  export * from './RangeStyle';
8
8
  export * from './RangeRect';
9
+ export * from './RangeInsertBar';
9
10
  export * from './DragHandle';
10
11
  export * from './Draggable';
11
12
  export * from './RangeResizer';
12
13
  export * from './FillHandle';
14
+ export * from './useDoubleClick';
13
15
  //# sourceMappingURL=index.d.ts.map