From d359c4e6022cafaedc6ca8874db25267e80e292c Mon Sep 17 00:00:00 2001 From: riyadhafraa <49757212-riyadhafraa@users.noreply.replit.com> Date: Tue, 19 May 2026 08:12:56 +0000 Subject: [PATCH] #607: make inline edit toolbar usable on mobile (wrap + viewport clamp) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: On phone-width viewports (~375-430px), the floating FormattingToolbar in EditableCell rendered as a single row with 17+ controls (B/I/U + 7 color swatches + 3 align buttons + font + size + Save/Cancel). The row was wider than the screen, so users only saw the edges (X, ✓, two "default" dropdowns) and could not reach Bold/Italic/Underline, color swatches, alignment, or font controls. The horizontal clamp also relied on the table's scroll-container bounds, which on mobile extend beyond the viewport because the table is wider than the screen — so the clamp could park the toolbar partially off-screen. Fix in artifacts/tx-os/src/components/editable-cell.tsx: - Toolbar container: `flex` → `flex flex-wrap` with `gap-x-1 gap-y-1` and `max-w-[calc(100vw-16px)]` so it wraps onto multiple rows whenever a single row would exceed the viewport. Bumped `py` to `py-1` for breathing room between wrapped rows. - Horizontal clamp: in addition to the scroll-container bounds, also clamp against the viewport (`[8, window.innerWidth - tbWidth - 8]`) so the toolbar always lands fully on-screen even when the scroll container is wider than the screen. Scope: visual / positioning only. No changes to TipTap config, toolbar buttons, or save/cancel logic. Above-vs-below placement (#581 iOS keyboard handling) is preserved. --- .../tx-os/src/components/editable-cell.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/artifacts/tx-os/src/components/editable-cell.tsx b/artifacts/tx-os/src/components/editable-cell.tsx index b76105f5..5a40098f 100644 --- a/artifacts/tx-os/src/components/editable-cell.tsx +++ b/artifacts/tx-os/src/components/editable-cell.tsx @@ -606,12 +606,22 @@ function FormattingToolbar({ // Clamp horizontally to the table's scroll container if we can // find one; fall back to the viewport otherwise. This keeps the // toolbar visually anchored inside the table. + // #607: also clamp against the viewport in case the table's + // scroll container is wider than the screen (mobile/iPad), so + // the toolbar never lands off-screen and stays usable when it + // wraps to multiple rows. const container = findScrollContainer(anchor); const containerRect = container?.getBoundingClientRect(); - const minLeft = containerRect ? containerRect.left + 4 : 8; - const maxLeft = containerRect - ? containerRect.right - tbWidth - 4 - : window.innerWidth - tbWidth - 8; + const vpMinLeft = 8; + const vpMaxLeft = window.innerWidth - tbWidth - 8; + const minLeft = Math.max( + vpMinLeft, + containerRect ? containerRect.left + 4 : vpMinLeft, + ); + const maxLeft = Math.min( + vpMaxLeft, + containerRect ? containerRect.right - tbWidth - 4 : vpMaxLeft, + ); let left = rect.left; if (left > maxLeft) left = maxLeft; if (left < minLeft) left = minLeft; @@ -645,7 +655,7 @@ function FormattingToolbar({ zIndex: 1000, visibility: pos ? "visible" : "hidden", }} - className="flex items-center gap-1 bg-white border border-gray-200 rounded shadow-md px-1 py-0.5" + className="flex flex-wrap items-center gap-x-1 gap-y-1 bg-white border border-gray-200 rounded shadow-md px-1 py-1 max-w-[calc(100vw-16px)]" onMouseDown={(e) => e.preventDefault()} data-testid="em-edit-toolbar" >