refactor(web): replace floating-form store bus with idiomatic floating-ui#1893
Merged
Conversation
…g-ui The Day view positioned its event form through a hand-rolled global state bus (`useOpenAtCursor`): five `createExternalStore` singletons plus a `useFloatingAtCursor` wrapper and `setTimeout`/`queueMicrotask` timing hacks, feeding one `useFloating` instance shared between the form and the context menu. Converge both views onto the existing, view-agnostic `useEventForm` hook: - Form "open" is derived from Redux: a new `status.isFormOpen` flag on the draft slice (kept distinct from `isDrafting` so drag-to-create stays closed) plus a `selectIsEventFormOpen` selector. Replaces `openStore`/`nodeIdStore`. - The form anchors to the rendered draft element via `refs.setReference` (merged into the active draft card), mirroring the Week view. Removes the registry lookups, `queueMicrotask`, and `setTimeout`. Replaces `referenceStore`. - The grid (Day + Week) positioning middleware now lives inside `useEventForm`'s shared timed/all-day branch — no per-view floating config. Callers only pass a `dismiss` override when their dismiss behavior differs (the Day all-day press guard). - The Day context menu gets its own local `useFloating` + virtual element at the cursor (matching `GridContextMenuWrapper`), removing the shared instance, the `CursorItem` discriminator, and the hidden anchor div. Deletes `useOpenAtCursor.ts` and `useFloatingAtCursor.ts` (the shared `external-store.util` is kept — it has other consumers). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
c6e05e5 to
19dd047
Compare
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.
Context
The Day view positioned its event form through a hand-rolled global state bus in
useOpenAtCursor.ts— fivecreateExternalStoresingletons (open,nodeId,placement,strategy,reference) read viauseSyncExternalStoreand written imperatively from interaction handlers, plus auseFloatingAtCursorwrapper and asetTimeout(…, 10)/queueMicrotaskto fight timing. All of it existed to feed oneuseFloatinginstance shared between the event form and the context menu (discriminated by aCursorItemenum).Meanwhile the rest of the app — including the Week view (
useEventForm) — already uses floating-ui the intended way: a localuseFloatingper component with the reference attached viarefs.setReferenceor a virtual element at the cursor. The Day view was the lone outlier.This PR converges both views onto the existing
useEventFormhook and deletes the store bus.What changed
status.isFormOpenflag on the draft slice (kept distinct fromisDrafting/activity— drag-to-create makes agridClickdraft without opening the form) plus aselectIsEventFormOpenselector. ReplacesopenStore+nodeIdStore.refs.setReference, merged into the active draft card withuseMergeRefs— exactly like the Week view'sGridDraft. Removes the registry lookups,queueMicrotask, andsetTimeout. ReplacesreferenceStore.useEventFormis the shared hook for both views, extended with optionalfloating/dismissoverrides. The Day view supplies its own positioning middleware (relocated todayEventFormFloating.ts); the Week path is unchanged.useFloating+ virtual element at the cursor (matchingGridContextMenuWrapper), removing the shared instance, theCursorItemdiscriminator, and the hidden anchor div.useOpenAtCursor.tsanduseFloatingAtCursor.ts. The sharedexternal-store.utilis kept (other consumers remain). Net −350 lines.Verification
bun run type-checkand biome lint: clean.bun test(events ducks, Day, Forms, Week views): 286 pass, 0 fail, including the updatedDayInteractionCoordinatoranduseCloseEventFormsuites.🤖 Generated with Claude Code