Skip to content

fix(panel): floating actions stay while the pointer crosses to them — 0.78.4 - #713

Closed
davidnich wants to merge 10 commits into
developfrom
bugfix/panel-floating-actions-reachable
Closed

davidnich wants to merge 10 commits into
developfrom
bugfix/panel-floating-actions-reachable

Conversation

@davidnich

Copy link
Copy Markdown
Member

From David's review of qorus#646.

Stacked on #710, which is stacked on #708. Their commits show in this diff until they land; merge #708, then #710, then this.

Symptom. In reqraft's expression builder, hovering an expression and clicking its floating Remove (the trash can) did nothing visible. The click switched the Visual / Text view underneath instead.

Root cause. floatingActions floats a panel's show: 'hover' actions above its top-right corner. A pointer heading for them leaves the panel through its top edge, usually to the left of the actions, on a diagonal.

  • handleMouseLeave ended the hover unless the pointer went straight into the actions, so they were gone before it reached them.
  • The click then landed on what they had been covering. Verified with a real mouse path on the IDE's Storybook: the element under the pointer at the Remove button's position was the Visual / Text switch.

Fix. While the pointer is in the strip between the panel's top edge and the actions, across the panel's width, the hover stays. It ends when the pointer leaves that strip for anything that is neither the panel nor the actions. This is decided by where the pointer is on each move, with no timer. The same applies when the pointer goes from the actions back towards the panel.

Tests.

  • __tests__/panelFloatingActionsReachable.test.tsx: crossing to the actions (fails without the change), leaving the strip elsewhere, and leaving the panel away from the actions (unchanged).
  • The placement tests still pass. yarn precheck passes.

Docs: COMPONENTS.md, ReqorePanel row.

Version: 0.78.4. #708 is 0.78.1, #709 is to be 0.78.2 and #710 is 0.78.3.

🤖 Generated with Claude Code

…mounts — 0.77.9

A popover with handler='focus' listened for focusin only. A field focused
as it mounts - an editor that opens with the cursor in its field, as a
read-first form row does when it is clicked - was focused before the
listener was attached, so no focusin came and its list stayed closed. The
user then clicked in the field, which already had the focus, and still
saw nothing until they left it and came back. In the Qorus IDE this hid
every template a typed field offers (qorus#646: an added field of type
Whole number showed no fields of the record to refer to).

When the listener is attached, a focus popover whose field already has the
focus opens.

0.77.9: 0.77.8 is taken by #696.
… committed, so a key typed after it is kept

The editor compared a value it was given with its document in a passive
effect, which the browser runs after painting - under load, after the next
key. A value that matched the document when it was rendered was then
compared with a document that had a key more, taken as a value from
outside, and put in its place: the key was lost and the caret went to the
end. reqraft's SmartEditor › With Markdown Docs typed "arr.for" and got
"arr.fr" in CI; under 6x CPU throttling it lost a key in most runs.

The comparison runs in a layout effect, before the browser handles another
key. A value from outside still replaces the document.

richTextEditorValueSync.test.tsx types a key between a value's commit and
its passive effects: lost before, kept now.
…s a focus list again — 0.78.1

Foxhoundn's review of #708: the check for a field focused as it mounts ran
on every re-run of the listener effect, and the effect re-runs whenever
the list opens or closes (it depends on `open`, which depends on
`isOpen`). Closing the list with the cursor still in the field - Escape,
or a click on it with closeOnTargetClick - re-ran it, found the field
focused, and reopened the list at once. The check is made once per
trigger, remembered in a ref, as the listener is first attached.

Test: a list closed with Escape stays closed while the field keeps the
focus (fails without the change).

0.78.1: #696 goes in first as 0.78.0.
…croll pulls it with the mouse — 0.78.3

From David's review of qorus#646: on a phone, a swipe on a wide table's
header did not scroll it, while the same swipe on its rows did. The header
is scrolled WITH the body (the body syncs it) but is not a scroller of its
own (overflow hidden), so the browser has nothing to pan, and nothing
forwarded the swipe; only the wheel was forwarded.

- The header now takes sideways touch swipes (touch-action: pan-y; the page
  still pans up and down) and scrolls the body with them.
- New `dragToScroll` (default false): a mouse or pen presses and pulls the
  table by its body or its header. A press that does not move is still a
  click (a header cell opens its options), the click that ends a real drag
  is not delivered, and a press on a text field is the field's.
- The gesture is FadeScroller's dragToScroll, moved into a shared
  useDragToScroll hook that both use, so a fix to it reaches both. It
  rebinds when the element it scrolls appears after mount (a table renders
  no body until it has rows).

Tests: table-drag-to-scroll (4 of 6 fail on develop), the FadeScroller
drag tests unchanged and passing; story Collections/Table · Drag To
Scroll; COMPONENTS.md.
…e-header-scrolls-the-table: stacked on #708

# Conflicts:
#	package.json
… pullable body shows a grab cursor

The header forwarded only a wheel's deltaX to the body, so a plain wheel
with Shift held did nothing over it. With dragToScroll the body shows a
grab cursor, and grabbing with no text selection while pulled - what a
consumer otherwise had to style through reqore's class names.
…t landed on losing its capture

A touch pointer is captured by the element it lands on (implicit touch
capture). Taking the capture for the header moved it away, the cell
reported lostpointercapture, and the window listener ended the gesture:
a real swipe on a phone moved the table 5px. Only the capture the gesture
itself took ends it. Found driving the IDE's review on a phone with real
touch events; the dispatched-event tests could not see it.
… 0.78.4

From David's review of qorus#646: hovering an expression in reqraft's
builder and clicking its floating Remove did nothing - the click switched
the Visual / Text view underneath instead.

The actions float above the panel's top-right corner, so a pointer heading
for them leaves the panel through its top edge, usually left of the
actions, on a diagonal. The hover ended as it left the panel, the actions
went, and the click landed on what they had covered. While the pointer is
in the strip between the panel's top edge and the actions (across the
panel's width), the hover now stays; it ends when the pointer leaves the
strip for anything that is neither the panel nor the actions. Decided by
where the pointer is on each move, with no timer.

Test: panelFloatingActionsReachable (the crossing fails without the
change). Stacked on #710 (and #708).
@davidnich
davidnich requested a review from Foxhoundn as a code owner October 7, 2026 17:48
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

📦 Prerelease published for this PR:

@qoretechnologies/reqore@0.78.4-pr.713.gc701745

Install it in a consumer (updates package.json + lockfile):

yarn add @qoretechnologies/reqore@0.78.4-pr.713.gc701745

Published under the pr dist-tag — latest/beta are untouched. Repoint consumers to the released version once this PR merges.

@qlip-qore-technologies

Copy link
Copy Markdown

qlip visual regression

🔍 0 changed, 1 new of 1126 snapshots.
⏳ Awaiting review.

Review build 20261007-175702 in the dashboard →

davidnich added a commit that referenced this pull request Oct 7, 2026
davidnich added a commit that referenced this pull request Oct 7, 2026
0.78.0 is released; 0.78.1, 0.78.3 and 0.78.4 are claimed by the
prereleases of #708, #710 and #713, which this PR now carries.
@davidnich

Copy link
Copy Markdown
Member Author

Consolidated into #708 at 7c9c127 (merged in 21ee8c2, with no rebase of this branch's history). Further work goes to #708, which ships everything as 0.78.5. Closing without merging; the branch is kept.

@davidnich davidnich closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant