aidenybai / aidenybai/react-grab
Feature Request: Add onSelectionCommit — one interceptable hook per committed selection gesture
- 主要言語
- TypeScript
- スター
- 7.6k
- フォーク
- 340
- 平均マージ
- 5時間 12分
- マージ済み PR(30日)
- 6
説明
## Problem
As of `0.2.0`, plugins still don't have a single interceptable boundary for a committed selection gesture.
The existing hooks expose different pieces of the lifecycle:
* `onElementSelect(element)` fires once **per element**. Drag-selecting 3 elements produces 3 calls, so integrations have to reconstruct the gesture boundary themselves.
* No selection hook exposes the **pointer position** that committed the selection. Plugins that need to anchor UI at the commit point have to keep their own pointer listener running.
* `onDragEnd(elements, bounds)` exposes the committed group, but it is notification-only: it cannot intercept the default clipboard / `Copied` feedback path.
My current workaround reconstructs the commit boundary by batching `onElementSelect` calls:
```ts
let pendingBatch: {
elements: Element[];
resolvers: Array<(handled: boolean) => void>;
} | null = null;
const onElementSelect = (element: Element): Promise => {
if (!pendingBatch) {
pendingBatch = { elements: [], resolvers: [] };
queueMicrotask(flushBatch);
}
const batch = pendingBatch;
if (!batch.elements.includes(element)) {
batch.elements.push(element);
}
return new Promise((resolve) => {
batch.resolvers.push(resolve);
});
};
```
The integration also keeps separate pointer tracking so it can recover the position associated with that commit.
## Proposal
Add one async, interceptable hook that fires once for each committed selection gesture:
```ts
interface SelectionCommit {
readonly elements: readonly Element[];
readonly point: Position; // pointer/client position at commit time
readonly kind: "click" | "drag";
readonly additive: boolean;
readonly bounds: DragRect | null; // non-null for drag commits
}
interface PluginHooks {
onSelectionCommit?: (
selection: SelectionCommit,
) => boolean | void | Promise;
}
```
Semantics:
* Invoke it **once per committed gesture**, regardless of the number of selected elements.
* Resolving `true` means the plugin handled the selection — skip React Grab's built-in clipboard write and `Copied` feedback.
* `false` / `undefined` preserves the existing copy behavior.
* `additive` indicates that the gesture extended the existing selection, e.g. Shift + click or Shift + drag.
* For keyboard-committed selections, `point` falls back to the center of the target element.
* `onElementSelect` and `onDragEnd` stay unchanged for backward compatibility.
## Why a commit-level hook?
The plugin API already exposes most of the required information, but at different lifecycle boundaries:
```ts
onElementSelect(element)
onDragEnd(elements, bounds)
```
The missing piece is a single interception point representing:
> "This selection gesture has now committed these elements."
For pointer-driven selections, React Grab already has the selected elements, gesture type, pointer position, and drag bounds while processing the commit.
This would keep gesture reconstruction inside the component that already owns selection state rather than requiring every integration to rebuild it independently.
## Plugin use cases
A commit-level boundary would also make higher-level workflows implementable outside core as plugins, for example:
* accumulated selections / "Copy list" workflows (#317)
* the former comments dropdown / Copy All workflow (#421)
* comment + element accumulation workflows such as #634
Those plugins could consume the committed selection and own their storage/UI/export behavior without requiring those workflows to live in React Grab core.
## Current integration cost
My integration currently has roughly ~200 lines across batching, resolver fan-out, pointer tracking, and tests to reconstruct this boundary.
With this hook, the integration becomes roughly:
```ts
hooks: {
onSelectionCommit: async (selection) => {
await annotations.add(selection);
return true;
},
}
```
Related: #340 (multi-select, shipped) · #421 · #317 · #634
Happy to submit a PR if this API direction sounds useful.
コントリビューションガイド
評価
この issue はまだ評価されていません。