#607: make inline edit toolbar usable on mobile (wrap + viewport clamp)
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.
This commit is contained in:
@@ -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"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user