reflex-pragmatic-drag-and-drop
Reflex bindings for Atlassian's Pragmatic drag and drop. Build sortable lists and Kanban boards in pure Python, without writing JavaScript.
Wraps the full suite: @atlaskit/pragmatic-drag-and-drop (core),
-hitbox (closest edge), -auto-scroll, and -react-drop-indicator.
Status
Functional core (Phases 1–3 of the plan) with a test suite. See specs/
for the complete design (PRD, API, Tech Design, Data Model, Plan).
Requirements
- Python ≥ 3.12 · Reflex ≥ 0.9 ·
uvpackage manager
Pinned versions: core ^4.0.0, hitbox ^3.0.0, auto-scroll ^3.2.1,
react-drop-indicator ^4.2.4.
Installation
Add the published package to your Reflex project:
uv add reflex-pragmatic-drag-and-drop # or: pip install reflex-pragmatic-drag-and-drop
The pinned @atlaskit/* npm packages and the bundled React glue are installed by
Reflex into .web on the next reflex run — no manual JavaScript setup.
Running the Kanban demo (from a clone)
uv sync # installs Python dependencies
uv run reflex run # installs @atlaskit packages in .web and starts on :3000
Open http://localhost:3000 and drag the cards by their grip handle within and
between columns. The demo toggles the Atlaskit drop indicator line and switches
the auto-scroll speed of the column lists (standard / fast).
After upgrading the
@atlaskit/*pins, delete the local.web/directory so Reflex reinstalls the frontend fromreflex.lock/(a stale.web/package.jsonmakes the frozen-lockfile install fail with "lockfile is out of sync").
Usage
import reflex as rx
import reflex_pragmatic_dnd as dnd
class State(rx.State):
msg: str = ""
@rx.event
def on_drop(self, payload: dict):
self.msg = f"{payload['source']} -> {payload.get('target')}"
def index():
return rx.box(
# Global listener: a single handler for the whole page.
dnd.monitor(on_drop=State.on_drop),
# A draggable element that carries data.
dnd.draggable(
rx.text("Drag me"),
drag_id="card-1",
item_data={"cardId": "card-1"},
),
# A drop target with edge detection (for reordering).
dnd.drop_target(
rx.text("Drop it here"),
drop_id="zone-1",
target_data={"kind": "zone"},
with_closest_edge=True,
allowed_edges=["top", "bottom"],
),
rx.text(State.msg),
)
Components
| Factory | Wraps | Events |
|---|---|---|
dnd.draggable(...) |
draggable (element adapter) |
on_drag_start, on_drop |
dnd.drop_target(...) |
dropTargetForElements (+ hitbox) |
on_drag_enter, on_drag_leave, on_drop |
dnd.monitor(...) |
monitorForElements |
on_drag_start, on_drop |
dnd.scroll_container(...) |
autoScrollForElements |
— |
Built-in drop indicator and auto-scroll configuration:
dnd.drop_target(
card,
drop_id="c1",
with_closest_edge=True,
show_drop_indicator=True, # Atlaskit <DropIndicator> on the closest edge
drop_indicator_gap="8px", # spacing between siblings; line sits halfway
)
dnd.scroll_container(
cards,
max_scroll_speed="fast", # "standard" (default) | "fast"
allowed_axis="vertical", # "all" (default) | "vertical" | "horizontal"
max_height="300px",
overflow_y="auto",
)
Full details on props and payloads: specs/api/component-api-v1.md.
State reducers (optional)
The library includes two pure functions that transform the payload of
monitor(on_drop=...) into the new state, implementing the rules of
specs/data-model/event-payloads.md §4.
They do not mutate their inputs and return the original object unchanged on a no-op
(dropping outside, onto itself, or onto an unknown column):
@rx.event
def handle_drop(self, payload: dict):
self.cards = dnd.move_card(self.cards, payload, columns=["todo", "doing", "done"])
@rx.event
def reorder(self, payload: dict):
self.items = dnd.reorder_list(self.items, payload)
Visual states (CSS)
drop_target exposes attributes on the DOM for styling during the drag, and
draggable marks the element in progress:
| Attribute | Value | Component |
|---|---|---|
data-dragging |
true/false |
draggable |
data-over |
true/false |
drop_target |
data-closest-edge |
top/bottom/left/right/"" |
drop_target (with with_closest_edge) |
data-drop-id |
the drop_id |
drop_target |
dnd.drop_target(
rx.text("Drop it here"),
drop_id="zone-1",
with_closest_edge=True,
# Highlight the target and draw a line on the closest edge.
style={
"&[data-over='true']": {"background": "var(--accent-3)"},
"&[data-closest-edge='top']": {"box_shadow": "inset 0 2px 0 var(--accent-9)"},
"&[data-closest-edge='bottom']": {"box_shadow": "inset 0 -2px 0 var(--accent-9)"},
},
)
Examples
- Quickstart (minimal):
examples/quickstart.py— the smallest app after a PyPI install. - Sortable Kanban:
reflex_pragmatic_drag_and_drop/reflex_pragmatic_drag_and_drop.py(default app). - Sortable list:
examples/sortable_list.py.
Structure
reflex_pragmatic_dnd/ # the library (wrappers + JSX glue)
reflex_pragmatic_drag_and_drop/ # Kanban demo app
examples/ # additional examples
specs/ # SDD documentation
How it works
A local React glue (pragmatic_dnd.jsx, bundled with rx.asset) attaches
Pragmatic to the DOM elements via useEffect/useRef and emits JSON-serializable
payloads. NoSSRComponent wrappers expose them as Reflex components with typed
events. Only discrete events (start/drop) cross the WebSocket; the continuous
computation happens on the client. See
specs/technical/architecture.md.
Tests
uv run pytest
Covers the pure reducers (transformation rules from Data Model §4 and edge cases
from API Spec §4), the states of the demo/example app, and the wrapper contract
(tags, snake↔camel props, events, NoSSRComponent, pinned @atlaskit
dependencies).
Publishing
This package is a Reflex custom component. Build it with
uv run reflex component build and publish with uv publish. Full steps,
prerequisites, and the command reference are in PUBLISHING.md.
License
MIT.
Metadata
Release files for reflex-pragmatic-drag-and-drop 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| reflex_pragmatic_drag_and_drop-0.2.0.tar.gz | 19.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| reflex_pragmatic_drag_and_drop-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.4 kB
Release files / reflex_pragmatic_drag_and_drop-0.2.0.tar.gz
| Download URL | reflex_pragmatic_drag_and_drop-0.2.0.tar.gz |
|---|---|
| Size | 19.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc24492de19b071249f1a782bb61def3f350e6aed925fdf88e28cf1ac8a37477
|
|
BLAKE2b-256 checksum How to use checksums |
2fb07ff223fcd132c17c4fc50b3b0342b7507d542fbf074d5f464d00b546e94e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / reflex_pragmatic_drag_and_drop-0.2.0-py3-none-any.whl
| Download URL | reflex_pragmatic_drag_and_drop-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9fee98895c4c93b4c068f1231636fece59a1262947aca36d56cde226e718679e
|
|
BLAKE2b-256 checksum How to use checksums |
54f7e26735c2433b1524b95529d0189b37d44ad88fe2d37060559b4499c44eae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Security for reflex-pragmatic-drag-and-drop 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.