diff --git a/ui/src/ui/components/tailwind/list/StackedList.scala b/ui/src/ui/components/tailwind/list/StackedList.scala index 47b8de5..0602afa 100644 --- a/ui/src/ui/components/tailwind/list/StackedList.scala +++ b/ui/src/ui/components/tailwind/list/StackedList.scala @@ -2,12 +2,13 @@ package list import com.raquo.laminar.api.L.{*, given} -import org.scalajs.dom.html.UList +import org.scalajs.dom import com.raquo.laminar.nodes.ReactiveHtmlElement +import works.iterative.ui.components.headless.Items class StackedList[Item: AsListRow]: import StackedList.* - def apply(items: List[Item]): ReactiveHtmlElement[UList] = + def apply(items: List[Item]): ReactiveHtmlElement[dom.html.UList] = ul( role := "list", cls := "divide-y divide-gray-200", @@ -16,7 +17,7 @@ def withMod( items: List[Item] - ): Modifier[HtmlElement] => ReactiveHtmlElement[UList] = mods => + ): Modifier[HtmlElement] => ReactiveHtmlElement[dom.html.UList] = mods => ul( role := "list", cls := "divide-y divide-gray-200", @@ -31,7 +32,7 @@ object StackedList: def withHeader(header: String)( - content: Modifier[HtmlElement] => ReactiveHtmlElement[UList] + content: Modifier[HtmlElement] => ReactiveHtmlElement[dom.html.UList] ): HtmlElement = div( cls("relative"), @@ -43,3 +44,75 @@ ), content(cls("relative")) ) + +object StackedListWithRightJustifiedSecondColumn: + opaque type Title = ReactiveHtmlElement[dom.html.Paragraph] + opaque type Tag = ReactiveHtmlElement[dom.html.Paragraph] + opaque type LeftProp = ReactiveHtmlElement[dom.html.Paragraph] + opaque type RightProp = Div + + case class Item( + title: Title, + tag: Tag, + leftProps: Seq[LeftProp] = Nil, + rightProp: Option[RightProp] = None + ) + + def title(text: String): Title = + p( + cls := "text-sm font-medium text-indigo-600 truncate", + text + ) + + def tag(text: String, color: Color): Tag = + p( + cls("px-2 inline-flex text-xs leading-5 font-semibold rounded-full"), + text + ) + + def leftProp(text: String, icon: Option[SvgElement] = None): LeftProp = + p( + cls( + "mt-2 flex items-center text-sm text-gray-500 sm:mt-0 sm:ml-6" + ), + icon, + text + ) + + def rightProp(text: String, icon: Option[SvgElement] = None): RightProp = + div( + cls("mt-2 flex items-center text-sm text-gray-500 sm:mt-0"), + icon, + text + ) + + def item(i: Item): Div = + div( + cls := "px-4 py-4 sm:px-6 items-center flex", + div( + cls := "min-w-0 flex-1 pr-4", + div( + cls := "flex items-center justify-between", + i.title, + div(cls := "ml-2 flex-shrink-0 flex", i.tag) + ), + div( + cls := "mt-2 sm:flex sm:justify-between", + div( + cls("sm:flex"), + i.leftProps + ), + i.rightProp + ) + ) + ) + + def frame: Modifier[HtmlElement] => Div = + el => + div( + cls("bg-white shadow overflow-hidden sm:rounded-md"), + ul(role("list"), cls("divide-y divide-gray-200"), el) + ) + + def apply[A](f: this.type => A => Item): Items[A] = + Items(frame, item).contramap(f(this))