v4: auto-sized text editors no longer scroll, and on a phone an action row with a destructive button stacks — an autoResize editor set its height to scrollHeight, which leaves out the borders that a border-box height counts, so the text always overflowed by a couple of pixels: a playlist's description, a .form textarea since cf89c18, showed a scrollbar that ran under the emoji button. The height now adds the borders. On a phone, Delete, Cancel and Save didn't fit one line in French or in a wide face, and wrapped into a staircase: Delete alone on the left, Cancel and Save under it on the right. A .form-actions row with a .btn-danger now stacks under 512px, with Cancel and Save across the top (Save taking the room left) and the destructive button full width under them; the playlist editor and DumpEdit's footer both take it. 669 frontend tests pass, and tsc and deno fmt are clean; checked in screenshots in Kanagawa (French, 385px, with a long description) and DumpEdit's footer in Smooth at 390px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
khannurien
2026-10-08 21:51:38 +00:00
parent cf89c18eb9
commit a0c8b1d509
2 changed files with 24 additions and 3 deletions

View File

@@ -3438,8 +3438,6 @@ body.has-player .chat-fab {
.form-actions {
display: flex;
/* A destructive action, Cancel and Save don't fit a phone side by side in
every face; the right group then drops to its own line, still right. */
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
@@ -3456,6 +3454,25 @@ body.has-player .chat-fab {
margin-left: auto;
}
/* A destructive action, Cancel and Save don't fit a phone side by side in
every face, and wrapped they would make a staircase. On a phone they stack:
Cancel and Save across the top, Save taking the room left, the destructive
action the full width under them. */
@media (max-width: 512px) {
.form-actions:has(> .btn-danger) {
flex-direction: column-reverse;
align-items: stretch;
}
.form-actions:has(> .btn-danger) > .form-actions-right {
margin-left: 0;
}
.form-actions:has(> .btn-danger) > .form-actions-right > :last-child {
flex: 1;
}
}
/* Subtle text action. Resets the global <button> chrome so it looks the same
whether rendered as a <button> (modal/page forms) or a <Link> (DumpEdit). */
.form-cancel {

View File

@@ -278,7 +278,11 @@ export const TextEditor: React.ForwardRefExoticComponent<
const el = textareaRef.current;
if (!el) return;
el.style.height = "auto";
el.style.height = `${el.scrollHeight}px`;
// A border-box height counts the borders, which scrollHeight leaves
// out; without them the text overflows by a few pixels and scrolls.
el.style.height = `${
el.scrollHeight + el.offsetHeight - el.clientHeight
}px`;
}, [value, autoResize]);
// The textarea is read-only while images upload, so the spot they go to