diff --git a/build.sbt b/build.sbt index 3b17e7d4a..94f13c96d 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ import org.scalajs.linker.interface.ModuleSplitStyle -ThisBuild / tlBaseVersion := "0.109" +ThisBuild / tlBaseVersion := "0.110" ThisBuild / tlCiReleaseBranches := Seq("master") val Versions = new { diff --git a/modules/tests/src/test/scala/lucuma/ui/syntax/SyntaxSuite.scala b/modules/tests/src/test/scala/lucuma/ui/syntax/SyntaxSuite.scala index df44b380a..99dc82f42 100644 --- a/modules/tests/src/test/scala/lucuma/ui/syntax/SyntaxSuite.scala +++ b/modules/tests/src/test/scala/lucuma/ui/syntax/SyntaxSuite.scala @@ -4,6 +4,7 @@ package lucuma.ui.syntax import lucuma.core.util.TimeSpan +import lucuma.ui.TimeUnitsFormat import lucuma.ui.syntax.time.* import java.time.Duration @@ -12,21 +13,44 @@ class SyntaxSuite extends munit.FunSuite: test("toHoursMinutes should format a TimeSpan"): assertEquals(TimeSpan.Zero.toHoursMinutes, "0mins") + assertEquals(TimeSpan.Zero.toHoursMinutes(TimeUnitsFormat.Letter), "0m") assertEquals(TimeSpan.unsafeFromDuration(Duration.ofHours(1)).toHoursMinutes, "1hrs") + assertEquals( + TimeSpan.unsafeFromDuration(Duration.ofHours(1)).toHoursMinutes(TimeUnitsFormat.Letter), + "1h" + ) assertEquals(TimeSpan.unsafeFromDuration(Duration.ofMinutes(30)).toHoursMinutes, "30mins") - assertEquals(TimeSpan.unsafeFromDuration(Duration.ofHours(1).plusMinutes(30)).toHoursMinutes, - "1hrs 30mins" + assertEquals( + TimeSpan + .unsafeFromDuration(Duration.ofHours(1).plusMinutes(30)) + .toHoursMinutes(TimeUnitsFormat.Abbreviation), + "1hrs 30mins" + ) + assertEquals( + TimeSpan + .unsafeFromDuration(Duration.ofHours(1).plusMinutes(30)) + .toHoursMinutes(TimeUnitsFormat.Letter), + "1h 30m" ) test("toHoursMinutes should round to the nearest minute"): - assertEquals(TimeSpan.unsafeFromDuration(Duration.ofMinutes(30).plusSeconds(30)).toHoursMinutes, - "31mins" + assertEquals( + TimeSpan.unsafeFromDuration(Duration.ofMinutes(30).plusSeconds(30)).toHoursMinutes, + "31mins" + ) + assertEquals( + TimeSpan.unsafeFromDuration(Duration.ofMinutes(30).plusSeconds(29)).toHoursMinutes, + "30mins" ) - assertEquals(TimeSpan.unsafeFromDuration(Duration.ofMinutes(30).plusSeconds(29)).toHoursMinutes, - "30mins" + assertEquals( + TimeSpan + .unsafeFromDuration(Duration.ofHours(1).plusMinutes(30).plusSeconds(30)) + .toHoursMinutes, + "1hrs 31mins" ) - assertEquals(TimeSpan - .unsafeFromDuration(Duration.ofHours(1).plusMinutes(30).plusSeconds(30)) - .toHoursMinutes, - "1hrs 31mins" + assertEquals( + TimeSpan + .unsafeFromDuration(Duration.ofHours(1).plusMinutes(30).plusSeconds(30)) + .toHoursMinutes(TimeUnitsFormat.Letter), + "1h 31m" ) diff --git a/modules/ui/src/main/scala/lucuma/ui/TimeUnitsFormat.scala b/modules/ui/src/main/scala/lucuma/ui/TimeUnitsFormat.scala new file mode 100644 index 000000000..e34f9fe0b --- /dev/null +++ b/modules/ui/src/main/scala/lucuma/ui/TimeUnitsFormat.scala @@ -0,0 +1,8 @@ +// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA) +// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause + +package lucuma.ui + +enum TimeUnitsFormat(val hours: String, val minutes: String): + case Letter extends TimeUnitsFormat("h", "m") + case Abbreviation extends TimeUnitsFormat("hrs", "mins") diff --git a/modules/ui/src/main/scala/lucuma/ui/components/TimeSpanView.scala b/modules/ui/src/main/scala/lucuma/ui/components/TimeSpanView.scala index c8ff35772..5b3288755 100644 --- a/modules/ui/src/main/scala/lucuma/ui/components/TimeSpanView.scala +++ b/modules/ui/src/main/scala/lucuma/ui/components/TimeSpanView.scala @@ -7,6 +7,7 @@ import japgolly.scalajs.react.* import japgolly.scalajs.react.vdom.html_<^.* import lucuma.core.util.TimeSpan import lucuma.react.common.ReactFnProps +import lucuma.ui.TimeUnitsFormat import lucuma.ui.syntax.time.* /** @@ -14,18 +15,23 @@ import lucuma.ui.syntax.time.* * * Hovering over the view will show the full (unrounded) TimeSpan in hours, minutes, and seconds. */ -case class TimeSpanView(timespan: TimeSpan, modifiers: Seq[TagMod] = Seq.empty) - extends ReactFnProps(TimeSpanView.component): +case class TimeSpanView( + timespan: TimeSpan, + unitsFormat: TimeUnitsFormat = TimeUnitsFormat.Abbreviation, + modifiers: Seq[TagMod] = Seq.empty +) extends ReactFnProps(TimeSpanView.component): def addModifiers(modifiers: Seq[TagMod]) = copy(modifiers = this.modifiers ++ modifiers) def withMods(mods: TagMod*) = addModifiers(mods) object TimeSpanView: + private type Props = TimeSpanView - def component = ScalaFnComponent[TimeSpanView]: props => + private val component = ScalaFnComponent[Props]: props => val ts = props.timespan <.span( props.modifiers.toTagMod, - ^.title := s"${ts.toHoursPart} hours, ${ts.toMinutesPart} minutes, ${ts.toSecondsPart} seconds", - ts.toHoursMinutes + ^.title := s"${ts.toHoursPart} hours, ${ts.toMinutesPart} minutes, ${ts.toSecondsPart} seconds" + )( + ts.toHoursMinutes(props.unitsFormat) ) diff --git a/modules/ui/src/main/scala/lucuma/ui/syntax/time.scala b/modules/ui/src/main/scala/lucuma/ui/syntax/time.scala index f0e149007..fb0200f4d 100644 --- a/modules/ui/src/main/scala/lucuma/ui/syntax/time.scala +++ b/modules/ui/src/main/scala/lucuma/ui/syntax/time.scala @@ -5,6 +5,7 @@ package lucuma.ui.syntax import cats.syntax.eq.* import lucuma.core.util.TimeSpan +import lucuma.ui.TimeUnitsFormat trait time: extension (timespan: TimeSpan) @@ -12,12 +13,20 @@ trait time: * Format a timespan in the format `${hh}hrs ${mm}mins` */ def toHoursMinutes: String = - val hours = timespan.toHoursPart + toHoursMinutes(TimeUnitsFormat.Abbreviation) + + /** + * Format a timespan in the format `${hh}hrs ${mm}mins` or `${hh}h ${mm}m` + */ + def toHoursMinutes(unitsFormat: TimeUnitsFormat): String = + val hours = timespan.toHoursPart + def hourStr = s"$hours${unitsFormat.hours}" // Remaining minutes, rounded to the nearest minute - val minutes = timespan.toMinutes.setScale(0, BigDecimal.RoundingMode.HALF_UP) % 60 + val minutes = timespan.toMinutes.setScale(0, BigDecimal.RoundingMode.HALF_UP) % 60 + def minuteStr = s"$minutes${unitsFormat.minutes}" - if hours === 0 then s"${minutes}mins" - else if minutes === 0 then s"${hours}hrs" - else s"${hours}hrs ${minutes}mins" + if hours === 0 then minuteStr + else if minutes === 0 then hourStr + else s"$hourStr $minuteStr" object time extends time