History Section

Displays filtered records with date range and tag filtering.

Structure

  1. 408-date-range-picker - Preset buttons + From/To pickers
  2. 409-tag-filter - Tag toggle buttons in FlowLayout
  3. Total time display
  4. Share button (opens 410-share-export-sheet)
  5. Month rows (HistoryMonthRow) - Period > Day > Record hierarchy

Month rows (HistoryMonthRow)

HistorySection emits one List row per month (HistoryMonthRow). Because the enclosing List reuses cells natively, only on-screen months are built — the nested SwiftUI Grids below are not lazy, but each is bounded to a single month. Columns still align across months because every label column has a fixed width (HistoryColumnSizes).

Level 1: Month Row

  • One List row per year-month
  • Period label column: vertical text (rotated 90 degrees)
  • Contains Level 2 (Days Grid) for the month

Level 2: Days Grid

  • Rows grouped by day within each month
  • Day label column: large monospaced day number (01-31)
  • Contains Level 3 (Records Grid) for each day

Level 3: Records Grid

  • Individual record rows with columns:
    • Duration (compact format, monospaced, right-aligned)
    • Time range (start - end, monospaced)
    • Tag (color indicator + name) - always shown
  • Comment shown on separate row when present (spans all columns)

Smart Column Hiding (HistoryVisibility)

Columns are intelligently hidden based on filtered data:

  • Period column: Hidden if all records are in current month of current year
  • Year in period label: Shown only if multiple years or non-current year
  • Day column: Hidden if all records are on the same day

The tag column is always shown so records stay identifiable regardless of the active filter.

This reduces visual noise when filtering to specific time periods.

Sticky Labels

Period and day labels use sticky positioning:

  • Labels stay visible at top of viewport when scrolling through long lists
  • Offset is clamped to keep label within its cell bounds
  • Threshold: labels start sticking when cell Y goes below 100pt

Interactions

  • Tap row: Expand inline 406-record-editor (spans record columns)
  • Context menu: Delete option
  • Long press (iOS): Same as context menu

Filtering

Records filtered by:

  1. Date range (inclusive)
  2. Selected tags (if any)
  3. Untagged toggle

Running timers are excluded from history.

Export

Share button opens 410-share-export-sheet with:

  • Current date range
  • Current tag filters
  • All filtered records

Uses snapshot pattern to prevent sheet re-render issues.

Related