Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 0.1.0-alpha.1 — 2026-09-06

- Preserve continuous offset and progress across React renders and preserve consumer transforms with independent CSS translation.
- Add velocity-aware bounded settling, deterministic gesture-start snapshots, and interruptible snap-back continuity.
- Add optional `onDismissComplete` after mounted departure while keeping `onDismiss` as immediate semantic commitment.
- Scope global blur handling to active pointer sessions and keep 1,000 idle instances free of global listeners, animation frames, and observers.
- Reject empty or mixed-axis directions and invalid thresholds with precise errors.
- Protect text-editing controls for mouse, touch, and pen, and add `data-drag-dismiss-ignore` for custom interaction surfaces.
- Expand lifecycle, integration, troubleshooting, performance, and real-device documentation.

## 0.1.0-alpha.0

- Initial drag-to-dismiss primitive with logical directions, intent arbitration, recent velocity, resistance, interruptible settling, reduced motion, SSR-safe packaging, and documentation.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import '@nipe-solutions/react-drag-dismiss/core.css'

;<DragDismiss
directions={['start', 'end']}
onDismiss={({ direction }) => removeItem(id, direction)}
onDismiss={({ direction }) => recordCommit(id, direction)}
onDismissComplete={() => removeItem(id)}
>
<Notification />
</DragDismiss>
Expand All @@ -24,6 +25,8 @@ import '@nipe-solutions/react-drag-dismiss/core.css'

The package detects intent and provides motion mechanics. Your application owns removal, unmounting, undo, focus, persistence, and side effects. Always provide an accessible semantic control for required dismissal actions.

`onDismiss` fires once when release commits. Optional `onDismissComplete` fires once after a mounted departure reaches its visual target; it does not fire if the consumer unmounts first. Use `data-drag-dismiss-ignore` on custom controls that must never initiate a gesture. Inputs, textareas, selects, and contenteditable descendants are ignored automatically.

Full documentation: [react-drag-dismiss.nipesolutions.com](https://react-drag-dismiss.nipesolutions.com)

Part of [NIPE Open Source](https://opensource.nipesolutions.com).
Expand All @@ -38,7 +41,7 @@ npm run check
npm run test:e2e
```

See [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), and [docs/REAL_DEVICE_QA.md](docs/REAL_DEVICE_QA.md).
See [CONTRIBUTING.md](CONTRIBUTING.md), [SECURITY.md](SECURITY.md), [interaction architecture](docs/ARCHITECTURE.md), [performance notes](docs/PERFORMANCE.md), and [real-device QA](docs/REAL_DEVICE_QA.md).

## License

Expand Down
27 changes: 27 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Interaction architecture

Drag Dismiss separates continuous interaction mechanics from semantic React state.

## Pointer session snapshot

Pointer down measures the element once and snapshots the axis, logical directions, physical signs, writing direction, threshold, size, and lifecycle callbacks. Prop changes and resizing during that gesture take effect on the next gesture. Pointer movement performs no layout or computed-style reads.

## Continuous DOM state

The interaction controller owns `translate`, `--drag-dismiss-offset`, and `--drag-dismiss-progress` through refs and direct DOM writes. React initializes these values when the node connects but does not render-own them afterward. React state owns only semantic attributes such as `data-state`.

Progress is signed and threshold-relative: zero is origin, `1` or `-1` is the configured threshold, and travel beyond the threshold may exceed that range. Offset is a signed CSS pixel value.

## Ownership and settling

A session starts pending, claims only after dominant-axis intent, and otherwise abandons without preventing default behavior. A claimed cancellation begins an interruptible return. A new gesture starts from the currently rendered return offset. Committed departure is final and cannot be re-grabbed.

Recent sampled velocity participates in both commitment and bounded settle duration. A pause expires stale velocity. The controller measures no geometry during animation frames and schedules no frame while idle.

## Dismiss lifecycle

`onDismiss` reports semantic commitment exactly once before departure. If the node remains mounted, `onDismissComplete` reports visual completion exactly once after the departure target is reached. Unmounting from `onDismiss` safely cancels motion and intentionally prevents completion. The package never manages presence or removes DOM.

## Resource lifecycle

Each idle instance owns only element-level React handlers. The window blur safeguard exists only while a pointer session is active and is removed when that session ends, abandons, or unmounts. There are no observers or idle animation frames.
9 changes: 9 additions & 0 deletions docs/PERFORMANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Performance notes

The performance fixture mounts 1,000 independent `DragDismiss` instances and instruments resources owned by the package.

While idle, the group creates no window blur listeners, animation frames, or observers. Each instance retains only its element-level React pointer handlers and refs. Starting one gesture attaches one session-scoped blur listener, measures that element once, and leaves the other 999 instances inactive. Ending, abandoning, disabling, or unmounting the active session removes that listener.

Pointer movement writes `translate`, offset, progress, and semantic data attributes directly to the active element. It performs no React render per frame, computed-style lookup, or geometry measurement. Settle frames reuse the gesture-start size and configuration snapshot.

These are qualitative resource invariants rather than timing claims. Render duration depends on React mode, consumer content, browser, and device, so the project does not advertise a synthetic rows-per-second benchmark.
12 changes: 11 additions & 1 deletion docs/REAL_DEVICE_QA.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ Record device, OS, browser version, date, and result for:
- Desktop Chrome
- Desktop Safari

For each environment verify horizontal and vertical dismissal, slow threshold drag, fast flick, pause before release, reversal, cancel, unsupported direction, buttons and links, scrolling, RTL, reduced motion, browser-edge behavior, resize, and repeated gestures.
For each environment verify:

- slow horizontal drag, fast flick, pause before release, reversal, cancel, and wrong-direction resistance;
- vertical dismissal where it does not conflict with primary scrolling;
- button tap, link navigation, drag beginning on a button/link, and release-click suppression;
- input, textarea, select, contenteditable, and `data-drag-dismiss-ignore` interaction;
- vertical scrolling feed coexistence, RTL, reduced motion, and browser-edge behavior;
- resize during a gesture, snap-back re-grab, repeated gestures, and committed departure;
- consumer `transform` composition during translation.

Record qualitative motion continuity for both a slow release and a fast flick. Record browser and OS versions rather than inferring physical iOS behavior from desktop WebKit.

Status for `0.1.0-alpha.0`: physical iPhone and Android testing pending.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nipe-solutions/react-drag-dismiss",
"version": "0.1.0-alpha.0",
"version": "0.1.0-alpha.1",
"description": "Composable drag-to-dismiss mechanics for React.",
"homepage": "https://react-drag-dismiss.nipesolutions.com",
"repository": {
Expand Down
Loading
Loading