Skip to content

Commit

Permalink
Add a word counter for abstract and indicate if the abstract is too long
Browse files Browse the repository at this point in the history
  • Loading branch information
cquiroz committed Dec 19, 2024
1 parent 32f837d commit 8a2ce03
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 7 deletions.
2 changes: 1 addition & 1 deletion common/src/main/scala/explore/components/Tile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import lucuma.ui.syntax.all.given
*/
case class Tile[A](
id: Tile.TileId,
title: String,
title: VdomNode,
initialState: A = (),
back: Option[VdomNode] = None,
canMinimize: Boolean = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ object ExploreStyles:
val ProposalTab: Css = Css("explore-proposal-tab")
val ProposalDetailsGrid: Css = Css("explore-proposal-details-grid")
val ProposalAbstract: Css = Css("explore-proposal-abstract")
val AbstractTitleTooLong: Css = Css("explore-abstract-too-long")
val ProposalSubmissionBar: Css = Css("explore-proposal-submission-line")
val ProposalDeadline: Css = Css("explore-proposal-deadline")
val ProposalAttachmentsTile: Css = Css("explore-proposal-attachments-tile")
Expand Down
5 changes: 5 additions & 0 deletions common/src/main/webapp/sass/explore.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2014,6 +2014,11 @@ svg.fa-triangle-exclamation.explore-error-icon {
// ------
// Proposals and Partner Splits
// ------

.explore-abstract-too-long {
color: var(--error-background-color);
}

.partner-splits-grid {
--gap: 0.5em;

Expand Down
42 changes: 37 additions & 5 deletions explore/src/main/scala/explore/proposal/ProposalEditor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package explore.proposal

import cats.data.NonEmptySet
import cats.effect.IO
import cats.syntax.option.*
import cats.syntax.all.*
import clue.*
import clue.data.Input
import clue.data.syntax.*
Expand Down Expand Up @@ -69,6 +69,16 @@ case class ProposalEditor(
object ProposalEditor:
private type Props = ProposalEditor

private val BaseWordLimit = 200
private val HardWordLimit = 2 * BaseWordLimit

extension (s: String)
inline def wordCount: Int =
val trim = s.trim
if (trim.isEmpty) 0
else
trim.split("\\s+", HardWordLimit + 1).length // add a limit to restrict the performance hit

private val component =
ScalaFnComponent
.withHooks[Props]
Expand Down Expand Up @@ -99,8 +109,14 @@ object ProposalEditor:
// setClass >> setType >> setHours >> setPct2
// }
// )
.useStateBy((props, _) => props.proposal.get.abstrakt.map(_.value).foldMap(_.wordCount))
.useEffectWithDepsBy((props, _, abstractCounter) => props.proposal.get.abstrakt.map(_.value))(
(_, _, abstractCounter) =>
case Some(t) => abstractCounter.setState(t.wordCount)
case None => abstractCounter.setState(0)
)
.useResizeDetector()
.render: (props, ctx, resize) =>
.render: (props, ctx, abstractCounter, resize) =>
import ctx.given

val undoCtx: UndoContext[Proposal] = UndoContext(props.undoStacks, props.proposal)
Expand All @@ -118,7 +134,8 @@ object ProposalEditor:
val abstractAligner: Aligner[Option[NonEmptyString], Input[NonEmptyString]] =
aligner.zoom(Proposal.abstrakt, ProposalPropertiesInput.`abstract`.modify)

val abstractView: View[Option[NonEmptyString]] = abstractAligner.view(_.orUnassign)
val abstractView: View[Option[NonEmptyString]] = abstractAligner
.view(_.orUnassign)

val defaultLayouts = ExploreGridLayouts.sectionLayout(GridLayoutSection.ProposalLayout)

Expand Down Expand Up @@ -159,15 +176,30 @@ object ProposalEditor:
.orEmpty
)

val absTitle: VdomNode =
if (abstractCounter.value < 1) "Abstract"
else if (abstractCounter.value >= HardWordLimit)
React.Fragment(
"Abstract ",
<.span(ExploreStyles.AbstractTitleTooLong, s"($HardWordLimit or more words)")
)
else if (abstractCounter.value >= BaseWordLimit)
React.Fragment(
"Abstract ",
<.span(ExploreStyles.AbstractTitleTooLong, s"(${abstractCounter.value} words)")
)
else s"Abstract (${abstractCounter.value} words)"

val abstractTile =
Tile(
ProposalTabTileIds.AbstractId.id,
"Abstract",
absTitle,
bodyClass = ExploreStyles.ProposalAbstract
)(_ =>
FormInputTextAreaView(
id = "abstract".refined,
value = abstractView.as(OptionNonEmptyStringIso)
value = abstractView.as(OptionNonEmptyStringIso),
onTextChange = t => abstractCounter.setState(t.wordCount).rateLimitMs(1000).void
)(^.disabled := props.readonly,
^.cls := ExploreStyles.WarningInput.when_(abstractView.get.isEmpty).htmlClass
)
Expand Down
2 changes: 1 addition & 1 deletion project/Versions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object Versions {
val lucumaSchemas = "0.110.1"
val lucumaOdbSchema = "0.17.2"
val lucumaSSO = "0.7.1"
val lucumaUI = "0.124.7"
val lucumaUI = "0.125.0"
val monocle = "3.3.0"
val mouse = "1.3.2"
val mUnit = "1.0.3"
Expand Down

0 comments on commit 8a2ce03

Please sign in to comment.