Repository navigation
Release 0.6.0 - #199
Merged
Merged
Release 0.6.0#199
Conversation
# Conflicts: # dist/README.md # dist/index.cjs # dist/index.cjs.map # dist/index.mjs # dist/index.mjs.map # dist/package.json # pnpm-lock.yaml
Vue 3.5's useTemplateRef() returns Ref<HTMLElement | null>, which the previous VueElement type rejected. Widen the ref value to include null (the runtime already waits for a non-null element before initializing) and reuse VueElement for the parent field. Convert the Vue Test1 component to useTemplateRef so the framework e2e suite exercises it. Co-authored-by: jf908 <2487520+jf908@users.noreply.github.com>
initDrag captured originalZIndex after already setting the element to 9999, so handleEnd wrote 9999 back and the elevated z-index persisted on the dropped item forever. Capture the user's inline value first. Adds a regression test asserting the inline z-index is empty again after a completed drag. Co-authored-by: DanielASMPT <63778582+DanielASMPT@users.noreply.github.com>
The 'number of draggable items does not match the number of values' warning now reports both counts so the mismatch is debuggable at a glance. Also hoists getValues so it is only called once per remap. Co-authored-by: Carl Olsen <913047+unstoppablecarl@users.noreply.github.com>
npm, Renovate, and other tooling can now link back to the docs site and repo from the published package (mirrors formkit/tempo#61). Co-authored-by: Ghislain B <643976+ghiscoding@users.noreply.github.com>
The React and Vue dragAndDrop examples showed a nested config property, but those adapters extend ParentConfig directly — config options must be spread at the top level. Only the vanilla core API takes a nested config object. Also removes a duplicated 'a' in the useDragAndDrop description. Co-authored-by: RHellenes <16990441+RHellenes@users.noreply.github.com>
…point (#182) The insert plugin bypassed transfer validation entirely: - processParentDragEvent adopted any hovered parent as state.currentParent even when moveOutside's group/accepts checks rejected it, so handleEnd would transfer values into lists that should never receive them. - checkPosition treated *any* registered parent under the cursor as a valid hover target, keeping the insert point visible over foreign lists. - moveOutside returned early when accepts() passed, skipping the insert point UI until a second dragover. Extract an isValidDropTarget() helper mirroring core validateTransfer semantics (initial parent always valid; otherwise dropZone, accepts, or truthy group equality) and use it in both paths. The checkPosition ancestor walk now continues past invalid parents so nested lists with different groups don't mask a valid ancestor. The drag() Playwright helper now honors the position field (top/bottom/ left/right/center) the same way syntheticDrag already did, which new tests use to target insert ranges deterministically. Fixes #149, fixes #151. Co-authored-by: Mikkel Almonte--Ringaud <59152884+Vexcited@users.noreply.github.com>
validateDragHandle relied on querySelectorAll + elementFromPoint, which both stop at shadow boundaries — a handle rendered inside a web component's shadow root could never validate (#170). validateDragHandle now takes the event and first scans e.composedPath() for a handle match, bounded to elements inside the draggable node, falling back to the previous coordinate hit-test. Because native dragstart fires on the draggable element itself (its composed path never includes the handle), handleDragstart now reuses the validation recorded on pointerdown for the same node, only re-validating from the event when no pointerdown was seen (Safari). The synthetic/touch drag path already keys off pointerDown.validated, so it picks up shadow DOM support from the same change. Adds a shadow DOM test page and Playwright specs covering both the positive (drag via shadow handle) and negative (no drag from item body) paths using real event ordering: composed pointerdown on the handle, dragstart on the draggable element. Fixes #170. Co-authored-by: Matt Driscoll <1231455+driskull@users.noreply.github.com>
…184) handleLongPress armed its timeout unconditionally, so with the default longPress: false a one second touch hold still fired the timer — setting state.longPress, applying longPressClass, and preventDefault-ing the stale pointerdown event. On touch devices this corrupted in-flight state (reported as 'Cannot read properties of undefined (reading el)' on Android Chrome) for a feature the consumer never enabled. With longPress off, a press-and-hold is now simply a pointer that has not moved yet. New mobile Playwright specs: a 1.3s hold with longPress disabled stays a no-op (no class, no errors, dragging still works afterwards) and longPress: true continues to apply the class and drag after the hold. The disabled spec fails red on the unfixed code (the class appears). Fixes #173.
…op (#185) The dropOrSwap plugin replaced the core transfer path but never called validateTransfer, so any registered list could receive any drag: two lists with different groups (or no groups at all) could exchange items, and accepts() was never consulted while hovering — it only mattered after release, when nothing could be styled. The node/parent dragover and pointerover handlers now gate on a canTransferTo() check (current parent and initial parent always allowed, everything else goes through core validateTransfer). Hovering an invalid parent clears targeting state so the subsequent drop is a no-op instead of swapping against stale targets. New test page and Playwright specs: same-group transfer, foreign-group rejection (values and styling), no-group rejection, accepts() invoked during hover and allowing transfer, and within-list sorting. Three of the five fail red on the unfixed plugin. Fixes #157, fixes #148.
…186) SortEvent, TransferEvent, DragstartEvent, and DragendEvent each declared their own generic (<T>(data) => void) instead of reusing ParentConfig's T, so onSort/onTransfer/onDragstart/onDragend parameters were typed against an unrelated T — accessing your item type's properties inside a handler was a type error. The handler types now take the config's generic (SortEvent<T>), and the internal payload builders that leaned on the old universally-quantified form (casting parents/state to unknown) are cast to T properly. Adds src/types.test-d.ts, compile-time assertions checked by tsc --noEmit: with the old declarations it fails with 8 errors; with the fix it compiles. Fixes #162.
handleNodePointerdown called stopPropagation as its first statement, so pointerdown never reached document level. React (and other frameworks) attach delegated listeners at the root, which means consumer onPointerDown handlers inside draggable items never fired at all. Replace stopPropagation with an event-claim: the innermost draggable node marks the event in a WeakSet and ancestor node handlers plus the root pointerdown handler (which clears selection state) skip claimed events. Propagation continues to framework roots while preserving the two behaviors stopPropagation provided — innermost-node-wins for nested lists, and no self-deselection when pressing on a node. Adds a React framework e2e: a real click on a button inside a draggable item must invoke its onPointerDown (fails red with the old stopPropagation). Multi-drag selection suites pass unchanged. Fixes #166.
…188) Two related desyncs between library state and the native draggable attribute: - With dragHandle configured, setupNode still set draggable=true on the whole item, so browsers disabled text selection everywhere inside it and items could be picked up natively from anywhere (#139). The item now stays draggable=false until a pointerdown on the handle validates, and is disarmed again on release (pointerup over the node, root pointerup, blur, and drag end all restore per config). - Elements excluded by the draggable callback kept draggable=true on the MutationObserver remap path, allowing native drags the library would ignore (#96). remapNodes now clears the attribute for excluded elements. The pointerup disarm is scoped to dragHandle configs and leaves state.pointerDown set, since the focus/blur handlers rely on it to manage draggable while editing inputs inside items (#142 — the Firefox issue-142 spec caught a first attempt that cleared it). New Playwright specs: handle-gated attribute arming over a real pointer press/release cycle, real mouse text selection inside an item with drag handles (both red on unfixed code), and draggable-callback toggling. Fixes #139, fixes #96.
Three layers of the same lifecycle bug (#145): - The Vue adapter registered tearDown in onUnmounted, but template refs are already null by then — tearDown never ran at all. Moved to onBeforeUnmount. The React adapter had the equivalent bug: it read parent.current inside the effect cleanup, after React nulls DOM refs on unmount. The element is now captured when the effect runs. - tearDown itself only aborted the parent's listeners. The document level handlers and pending long press timer kept firing against the removed list: a held pointer that moved after unmount started a synthetic drag on a disconnected parent, where showPopover throws InvalidStateError ('Invalid on disconnected popover elements' — the exact error reported). tearDown now clears pointerDown state and its timer when they belong to the torn-down parent, and resets any in-flight drag that involves it. - Both showPopover call sites are guarded with isConnected as a final backstop. New e2e (tests/pages/teardown.vue + teardown.spec.ts): press an item, unmount the list programmatically mid-press, move the held pointer — must produce no errors — then remount and verify dragging works. Fails red on unfixed code with the exact InvalidStateError from the issue. Fixes #145.
The animations plugin hardcoded ease-in-out for every sort/transfer animation. AnimationsConfig now accepts an easing string (any value the Web Animations API accepts) threaded through all animate() calls, defaulting to the previous ease-in-out. Adds an e2e that reads the running animation's timing mid-drag and asserts the configured cubic-bezier is applied. Closes #70.
) remapNodes pairs values to DOM children positionally. During a drag that's only safe when the DOM has caught up with the values array — React commits are asynchronous, so under fast drags with slow renders the remap ran against stale DOM and reassigned node identities: elements got values they don't render, state.draggedNode was re-pointed at a different element, and subsequent sorts spliced at wrong indices, compounding the corruption (#169). While a drag is active, remapNodes now pairs each known node to its existing value and derives the index from the current values array (consumed-slot matching, so duplicate values stay 1:1). It falls back to positional pairing whenever existing node values can't be matched onto the current values — initial setup, transfers committing, and wholesale value replacement keep their previous behavior. Outside of drags nothing changes. New React test component commits its state 50ms late (the adapter's value store updates synchronously, the DOM lags — same shape as a slow React render), and the new spec sorts twice inside that window. It asserts the invariant that breaks userland: a registered node must never carry a value other than the one it renders. On unfixed code the probe catches an element rendering 'one' registered as 'two'; fixed, identities hold mid-drag and the drag lands where the user hovered. Fixes #169.
The non-React language panes are Nuxt server components mounted lazily on tab click. On the statically deployed site there is no server to answer the runtime island request, so the Vue/Solid/Native/Marko tabs rendered empty (#174). All panes now render during prerender and toggle with v-show — the highlighted HTML ships in the static page, so the client-perf benefit of server components is preserved. Prerendering all panes also surfaced a build-breaking error the lazy islands had been hiding: docs/examples/config/config.ts mirrors ParentConfig and still used the unparameterized event handler types (TS2314 after the #186 fix) — twoslash rejected the sample and the island 500'd. Updated to SortEvent<T> et al. Verified: nuxt generate succeeds (71 island routes prerendered, zero errors) and the generated index.html contains raw-code containers for all five languages. Fixes #174.
The suite grew past what a single serial job can finish inside the 30-minute timeout — every recent run was killed at 30m and reported as cancelled. One matrix shard per Playwright project (Chrome, Firefox, WebKit, mobile Chromium) keeps each job well under the limit, installs only the browser it needs, and fails independently per engine.
Framework integration docs belong on the docs site; the README stays a short introduction. Also drops the empty 'About this repository' stub and points the npm version badge at drag-and-drop instead of tempo.
The browser download from the Playwright CDN intermittently stalls with no output; without a step timeout it consumed the entire 30-minute job (every recent run was killed there before tests even started — both the single-job and the sharded layout). Each install attempt is now bounded at 5 minutes with up to 3 retries, and the browser cache makes the step a no-op on subsequent runs of the same Playwright version.
Node 24.16.0 (pulled in by check-latest) hangs Playwright's browser download at 100% — the stream never finalizes, deterministically, for every browser. The last green run (Apr 30) used 24.15.0 with the same workflow. Pin it and bump deliberately with a verified run instead of floating to whatever the runner cache serves.
The #169 regression probe imported the library module through Vite's /@fs/ endpoint with a hardcoded absolute path from the machine it was written on, failing instantly on CI runners. The path now resolves from the repo root at runtime, and the probe runs as a string-evaluated script so the dynamic import reaches the browser untransformed.
devices['Desktop Webkit'] is not a registered Playwright device descriptor, so the spread was undefined and the project silently ran on the default browser — chromium — since it was added. The CI shard that installs only webkit exposed it (every launch failed wanting chromium-headless-shell). Switch to devices['Desktop Safari'], the real WebKit descriptor. Full suite passes on actual WebKit locally.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
) Publishing is now a GitHub workflow instead of a local pnpm publish: - Pushing a v* tag triggers publish.yml. The version comes from the tag (v0.6.0 -> 0.6.0, v0.6.0-dev.abc1234 -> 0.6.0-dev.abc1234) and the npm dist-tag is inferred from it: plain versions publish as latest (allowed only from main), -next.* as next, -dev.* as dev, with the short commit hash as the prerelease suffix. - The full Playwright matrix runs first (ci.yml is now callable and gates the publish job). - npm trusted publishing via OIDC: id-token permission + npm@latest, no tokens anywhere. - Build is validated with publint and published from dist/. - changelogithub generates GitHub release notes for latest releases. Replaces release.yml; ci.yml no longer fires directly on tag pushes since publish.yml invokes it.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In this PR: the 0.6.0 release. Everything below landed through individually reviewed PRs on
release/0.6.0, and CI is fully green (all four shards) — Chrome, Firefox, real WebKit, and mobile Chromium, 150 Playwright tests.Core fixes (each with red→green regression coverage)
validateTransfercrash guard on undefined parents (thanks @stanislav-janu)group/accepts(built on @vexcited's #152)longPressis off (mobile crash)group/accepts, andaccepts()fires during hoverdraggableattribute synced with handles/callbacks; text selection works with drag handlesonBeforeUnmount, React ref capture) + mid-interaction cleanupanimations({ easing })Vue/types/docs/meta
MaybeRef+isRef/unrefand fix(vue): accept null template refs so useTemplateRef type-checks #177useTemplateRefsupport (thanks @jf908)homepage/repository/bugs(thanks @ghiscoding)CI overhaul (#193 #195 #196 #197 #198)
Every recent "cancelled" run was the same failure: Playwright's browser download hangs at 100% on Node 24.16.0, which
check-latestsilently pulled in (the last green run used 24.15.0). In this PR the workflow pins Node to the verified-good version, adds a browser cache plus bounded install retries, and shards the suite per browser project (~3 min each). Along the way I found thatdevices["Desktop Webkit"]was never a real Playwright device descriptor — that project had silently been running on Chromium since it was added. It now runs on actual WebKit, and the full suite passes on it.SortEvent/TransferEvent/DragstartEvent/DragendEventnow take a type argument (#186). Handlers written inline are inferred and unaffected; explicit annotations needSortEvent<MyItem>.Issue tracker
51 of the 59 open issues are resolved in this release cycle (fixed, answered, or closed with an explanation). Intentionally still open: #174/#156/#131 (docs fixes merged here, awaiting deploy verification) and the roadmap trackers #8 (cloning), #30 (tree view), #25 (drop animation), #17 (Svelte), #150 (selection API).