/// Renders Timeline Geometry pieces for the FocusFlow Flutter timeline. library; import 'package:flutter/material.dart'; /// Converts timeline clock values into vertical pixel positions. class TimelineGeometry { /// Creates geometry for a visible time range. const TimelineGeometry({ required this.visibleStart, required this.visibleEnd, required this.pixelsPerMinute, this.minCardHeight = 72, }); /// First visible time on the timeline. final TimeOfDay visibleStart; /// Last visible time on the timeline. final TimeOfDay visibleEnd; /// Vertical scale used to convert minutes into pixels. final double pixelsPerMinute; /// Minimum rendered height for a timeline card. final double minCardHeight; /// First visible minute from midnight. int get startMinutes => visibleStart.hour * 60 + visibleStart.minute; /// Last visible minute from midnight. int get endMinutes => visibleEnd.hour * 60 + visibleEnd.minute; /// Total vertical height of the visible timeline. double get totalHeight => (endMinutes - startMinutes) * pixelsPerMinute; /// Returns the vertical offset for [minutes] since midnight. double yForMinutesSinceMidnight(int minutes) { return (minutes - startMinutes) * pixelsPerMinute; } /// Returns the rendered height for a duration in minutes. double heightForDuration(int minutes) { final height = minutes * pixelsPerMinute; return height < minCardHeight ? minCardHeight : height; } /// Generates timeline ticks at [stepMinutes] intervals. List ticks({int stepMinutes = 15}) { return [ for ( var minute = startMinutes; minute <= endMinutes; minute += stepMinutes ) TimelineTick( minutesSinceMidnight: minute, y: yForMinutesSinceMidnight(minute), label: _labelFor(minute), major: minute % 60 == 0, ), ]; } static String _labelFor(int minutes) { final hour24 = minutes ~/ 60; final minute = minutes % 60; final period = hour24 >= 12 ? 'PM' : 'AM'; final rawHour = hour24 % 12; final hour = rawHour == 0 ? 12 : rawHour; if (minute == 0) { return '$hour:00 $period'; } return '$hour:${minute.toString().padLeft(2, '0')}'; } } /// A labeled tick on the compact timeline axis. class TimelineTick { /// Creates a timeline tick. const TimelineTick({ required this.minutesSinceMidnight, required this.y, required this.label, required this.major, }); /// Minute from midnight represented by this tick. final int minutesSinceMidnight; /// Vertical pixel offset for this tick. final double y; /// Display label for this tick. final String label; /// Whether this tick represents a major interval. final bool major; }