From 0baea139a30bb23d9766841753564208580f6811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 15 May 2026 21:11:39 +0200 Subject: [PATCH] refactor(forms): migrate mood and measurement row-edit dialogs to ResponsiveSheet The mood-list and measurement-list edit dialogs were raw mounts, so on phones the user faced a centred dialog with the keyboard pushing the form off-screen instead of the bottom-sheet branch the rest of the app uses. Both row-edit surfaces now mount , with the Save / Cancel row portalled into the footer slot so the bottom-sheet branch can sticky-pin the action row. The submit button keeps its
association via the HTML form attribute so submit-on-Enter and native validation continue to work. --- .../measurements/measurement-list.tsx | 142 +++++++++-------- src/components/mood/mood-list.tsx | 146 ++++++++++-------- 2 files changed, 158 insertions(+), 130 deletions(-) diff --git a/src/components/measurements/measurement-list.tsx b/src/components/measurements/measurement-list.tsx index 31223f71f..c83336dd4 100644 --- a/src/components/measurements/measurement-list.tsx +++ b/src/components/measurements/measurement-list.tsx @@ -23,12 +23,7 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; +import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; import { DropdownMenu, DropdownMenuContent, @@ -59,6 +54,7 @@ import { MoreHorizontal, } from "lucide-react"; import { useId, useState } from "react"; +import { createPortal } from "react-dom"; import { formatDateTime } from "@/lib/format"; import { useTranslations, useFormatters } from "@/lib/i18n/context"; import { invalidateKeys, measurementDependentKeys } from "@/lib/query-keys"; @@ -155,6 +151,11 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) { // failure when the user submits an invalid value or timestamp. const editErrorId = useId(); const editErrorDescriptor = editError ? editErrorId : undefined; + // v1.4.27 R4 RC2 — Sheet-branch sticky-pinned footer slot. + const editFormId = useId(); + const [editFooterEl, setEditFooterEl] = useState( + null, + ); const setTypeFilter = (value: string) => { setTypeFilterRaw(value); @@ -604,13 +605,18 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) { )} - !open && closeEdit()}> - - - {t("measurements.editMeasurement")} - + !open && closeEdit()} + title={t("measurements.editMeasurement")} + footer={
} + > {editing && ( - +
@@ -683,57 +689,66 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) {
)} -
- - - - - - setEditDeleteDialogOpen(true)} - > - - {t("common.delete")} - - - + {editFooterEl + ? createPortal( +
+ + + + + + setEditDeleteDialogOpen(true)} + > + + {t("common.delete")} + + + -
- - -
-
+
+ + +
+
, + editFooterEl, + ) + : null} )} - -
+
); } diff --git a/src/components/mood/mood-list.tsx b/src/components/mood/mood-list.tsx index 6b89994ba..4af1e68b0 100644 --- a/src/components/mood/mood-list.tsx +++ b/src/components/mood/mood-list.tsx @@ -23,12 +23,7 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; +import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; import { DropdownMenu, DropdownMenuContent, @@ -59,7 +54,8 @@ import { ArrowUpDown, MoreHorizontal, } from "lucide-react"; -import { useState } from "react"; +import { useId, useState } from "react"; +import { createPortal } from "react-dom"; import { formatDateTime } from "@/lib/format"; import { useTranslations, useFormatters } from "@/lib/i18n/context"; import { @@ -126,6 +122,11 @@ export function MoodList({ onAddFirst }: MoodListProps = {}) { const [editMoodLoggedAt, setEditMoodLoggedAt] = useState(""); const [editError, setEditError] = useState(null); const [editDeleteDialogOpen, setEditDeleteDialogOpen] = useState(false); + // v1.4.27 R4 RC2 — Sheet-branch sticky-pinned footer slot. + const editFormId = useId(); + const [editFooterEl, setEditFooterEl] = useState( + null, + ); const setMoodFilter = (value: string) => { setMoodFilterRaw(value); @@ -517,14 +518,19 @@ export function MoodList({ onAddFirst }: MoodListProps = {}) { )} - {/* Edit dialog */} - !open && closeEdit()}> - - - {t("mood.editEntry")} - + {/* Edit sheet — bottom-sheet on ` !open && closeEdit()} + title={t("mood.editEntry")} + footer={
} + > {editing && ( -
+
)} -
- - - - - - setEditDeleteDialogOpen(true)} - > - - {t("common.delete")} - - - + {editFooterEl + ? createPortal( +
+ + + + + + setEditDeleteDialogOpen(true)} + > + + {t("common.delete")} + + + -
- - -
-
+
+ + +
+
, + editFooterEl, + ) + : null} )} - -
+ ); }