diff --git a/src/components/measurements/list/measurement-card-list.tsx b/src/components/measurements/list/measurement-card-list.tsx index 78da09634..ce5f90d09 100644 --- a/src/components/measurements/list/measurement-card-list.tsx +++ b/src/components/measurements/list/measurement-card-list.tsx @@ -16,60 +16,31 @@ import { } from "../measurement-list-meta"; import { DayDrillDown, SleepNightCaption } from "./day-drill-down"; -import { MeasurementRowActions, typeLabel } from "./row-actions"; +import { + MeasurementRowActions, + rowState, + typeLabel, + type MeasurementRowContext, +} from "./row-actions"; import { formatMeasurementSource, - isGroupedRow, sourceBadgeClass, truncateComment, type Measurement, } from "./list-model"; -import type { MeasurementListData } from "./use-measurement-list-data"; -import type { MeasurementListFilters } from "./use-measurement-list-filters"; -import type { MeasurementRowDisplay } from "./use-measurement-row-display"; /** The phone list: one card per reading, day or night. */ export function MeasurementCardList({ measurements, - canManage, - filters, - list, - display, - expandedDayKeys, - toggleExpand, - navigateForType, - startEdit, + rows, }: { measurements: Measurement[]; - canManage: boolean; - filters: MeasurementListFilters; - list: MeasurementListData; - display: MeasurementRowDisplay; - expandedDayKeys: ReadonlySet; - toggleExpand: (dayKey: string) => void; - navigateForType: (type: string) => void; - startEdit: (m: Measurement) => void; + rows: MeasurementRowContext; }) { - const { selectedIds } = filters; - const { onToggleRow, deleteMutation } = list; return (
{measurements.map((m) => ( - deleteMutation.mutate(id)} - display={display} - toggleExpand={toggleExpand} - navigateForType={navigateForType} - startEdit={startEdit} - /> + ))}
); @@ -78,32 +49,17 @@ export function MeasurementCardList({ /** One card, and under a grouped card its open drill-down. */ function MeasurementCard({ m, - canManage, - isSelected, - isExpanded, - onToggleRow, - onDelete, - display, - toggleExpand, - navigateForType, - startEdit, + rows, }: { m: Measurement; - canManage: boolean; - isSelected: boolean; - isExpanded: boolean; - onToggleRow: (id: string) => void; - onDelete: (id: string) => void; - display: MeasurementRowDisplay; - toggleExpand: (dayKey: string) => void; - navigateForType: (type: string) => void; - startEdit: (m: Measurement) => void; + rows: MeasurementRowContext; }) { + const { canManage, onToggleRow, navigateForType, display } = rows; + const { isGrouped, isSelected, isExpanded } = rowState(m, rows); const { t } = useTranslations(); const fmt = useFormatters(); const { rowDisplay, rowIdentityAttrs } = display; const Icon = TYPE_ICONS[m.type]; - const isGrouped = isGroupedRow(m); const isSleep = m.type === "SLEEP_DURATION"; const drilldownId = `drilldown-mobile-${m.dayKey ?? m.id}`; const label = typeLabel(m.type, t); @@ -229,14 +185,11 @@ function MeasurementCard({
diff --git a/src/components/measurements/list/measurement-table.tsx b/src/components/measurements/list/measurement-table.tsx index 1ba980ccd..b90e33fc2 100644 --- a/src/components/measurements/list/measurement-table.tsx +++ b/src/components/measurements/list/measurement-table.tsx @@ -21,44 +21,37 @@ import { measurementUnitLabel } from "@/lib/measurements/unit-label"; import { MEASUREMENT_TYPE_COLORS as TYPE_COLORS } from "../measurement-list-meta"; import { DayDrillDown, SleepNightCaption } from "./day-drill-down"; -import { MeasurementRowActions, typeLabel } from "./row-actions"; +import { + MeasurementRowActions, + rowState, + typeLabel, + type MeasurementRowContext, +} from "./row-actions"; import { formatMeasurementSource, - isGroupedRow, sourceBadgeClass, truncateComment, type Measurement, } from "./list-model"; import type { MeasurementListData } from "./use-measurement-list-data"; import type { MeasurementListFilters } from "./use-measurement-list-filters"; -import type { MeasurementRowDisplay } from "./use-measurement-row-display"; /** The desktop table: one row per reading, day or night, sortable columns. */ export function MeasurementTable({ measurements, - canManage, + rows, filters, list, - display, - expandedDayKeys, - toggleExpand, - navigateForType, - startEdit, }: { measurements: Measurement[]; - canManage: boolean; + rows: MeasurementRowContext; filters: MeasurementListFilters; list: MeasurementListData; - display: MeasurementRowDisplay; - expandedDayKeys: ReadonlySet; - toggleExpand: (dayKey: string) => void; - navigateForType: (type: string) => void; - startEdit: (m: Measurement) => void; }) { const { t } = useTranslations(); - const { sortBy, sortDir, toggleSort, selectedIds } = filters; - const { selectAll, pageIds, onToggleSelectAll, onToggleRow, deleteMutation } = - list; + const { canManage } = rows; + const { sortBy, sortDir, toggleSort } = filters; + const { selectAll, pageIds, onToggleSelectAll } = list; return (
{measurements.map((m) => ( - deleteMutation.mutate(id)} - display={display} - toggleExpand={toggleExpand} - navigateForType={navigateForType} - startEdit={startEdit} - /> + ))} @@ -149,31 +126,16 @@ export function MeasurementTable({ /** One table row, and under a grouped row its open drill-down. */ function MeasurementTableRow({ m, - canManage, - isSelected, - isExpanded, - onToggleRow, - onDelete, - display, - toggleExpand, - navigateForType, - startEdit, + rows, }: { m: Measurement; - canManage: boolean; - isSelected: boolean; - isExpanded: boolean; - onToggleRow: (id: string) => void; - onDelete: (id: string) => void; - display: MeasurementRowDisplay; - toggleExpand: (dayKey: string) => void; - navigateForType: (type: string) => void; - startEdit: (m: Measurement) => void; + rows: MeasurementRowContext; }) { + const { canManage, onToggleRow, navigateForType, display } = rows; + const { isGrouped, isSelected, isExpanded } = rowState(m, rows); const { t } = useTranslations(); const fmt = useFormatters(); const { rowDisplay, rowIdentityAttrs } = display; - const isGrouped = isGroupedRow(m); // A sleep night is grouped (its chevron opens the stage segments) but // reads as a duration with a nap caption, not as a daily total. const isSleep = m.type === "SLEEP_DURATION"; @@ -289,14 +251,11 @@ function MeasurementTableRow({
diff --git a/src/components/measurements/list/row-actions.tsx b/src/components/measurements/list/row-actions.tsx index 143d65562..92a0c0244 100644 --- a/src/components/measurements/list/row-actions.tsx +++ b/src/components/measurements/list/row-actions.tsx @@ -8,7 +8,8 @@ import { useTranslations } from "@/lib/i18n/context"; import { MEASUREMENT_TYPE_LABEL_KEYS as TYPE_LABEL_KEYS } from "../measurement-list-meta"; -import type { Measurement } from "./list-model"; +import { isGroupedRow, type Measurement } from "./list-model"; +import type { MeasurementRowDisplay } from "./use-measurement-row-display"; type Translate = ReturnType["t"]; @@ -17,6 +18,31 @@ export function typeLabel(type: string, t: Translate): string { return TYPE_LABEL_KEYS[type] ? t(TYPE_LABEL_KEYS[type]) : type; } +/** What every row of either layout needs from the list around it. */ +export interface MeasurementRowContext { + canManage: boolean; + selectedIds: ReadonlySet; + expandedDayKeys: ReadonlySet; + toggleExpand: (dayKey: string) => void; + navigateForType: (type: string) => void; + startEdit: (m: Measurement) => void; + onToggleRow: (id: string) => void; + onDelete: (id: string) => void; + display: MeasurementRowDisplay; +} + +/** A row's grouping, selection and drill-down state. */ +export function rowState(m: Measurement, rows: MeasurementRowContext) { + const isGrouped = isGroupedRow(m); + return { + isGrouped, + isSelected: rows.selectedIds.has(m.id), + isExpanded: isGrouped + ? rows.expandedDayKeys.has(m.dayKey as string) + : false, + }; +} + /** * A row's controls: the chevron into a day's or night's samples for a * grouped row; edit (owner only) and delete for a reading. The table's @@ -24,25 +50,20 @@ export function typeLabel(type: string, t: Translate): string { */ export function MeasurementRowActions({ m, + rows, isGrouped, isExpanded, drilldownId, - canManage, layout, - toggleExpand, - startEdit, - onDelete, }: { m: Measurement; + rows: MeasurementRowContext; isGrouped: boolean; isExpanded: boolean; drilldownId: string; - canManage: boolean; layout: "desktop" | "mobile"; - toggleExpand: (dayKey: string) => void; - startEdit: (m: Measurement) => void; - onDelete: (id: string) => void; }) { + const { canManage, toggleExpand, startEdit, onDelete } = rows; const { t } = useTranslations(); const desktop = layout === "desktop"; const size = desktop ? "icon" : "icon-lg"; diff --git a/src/components/measurements/measurement-list.tsx b/src/components/measurements/measurement-list.tsx index ac65e359e..9004bbc9e 100644 --- a/src/components/measurements/measurement-list.tsx +++ b/src/components/measurements/measurement-list.tsx @@ -24,6 +24,7 @@ import { MeasurementEditSheet } from "./list/measurement-edit-sheet"; import { MeasurementFilterRail } from "./list/measurement-filter-rail"; import { MeasurementTable } from "./list/measurement-table"; import type { Measurement } from "./list/list-model"; +import type { MeasurementRowContext } from "./list/row-actions"; import { useMeasurementEdit } from "./list/use-measurement-edit"; import { useMeasurementListData } from "./list/use-measurement-list-data"; import { useMeasurementListFilters } from "./list/use-measurement-list-filters"; @@ -149,15 +150,16 @@ export function MeasurementList({ ); } - const rowProps = { + const rows: MeasurementRowContext = { canManage, - filters, - list, - display, + selectedIds: filters.selectedIds, expandedDayKeys, toggleExpand, navigateForType, - startEdit: (m: Measurement) => edit.startEdit(m), + startEdit: edit.startEdit, + onToggleRow: list.onToggleRow, + onDelete: (id) => list.deleteMutation.mutate(id), + display, }; return ( @@ -202,13 +204,15 @@ export function MeasurementList({ {!isMobile && ( )} {isMobile && ( )}