diff --git a/ui/src/services/files/components/tailwind/FilePicker.scala b/ui/src/services/files/components/tailwind/FilePicker.scala index 0dabf8d..74029cb 100644 --- a/ui/src/services/files/components/tailwind/FilePicker.scala +++ b/ui/src/services/files/components/tailwind/FilePicker.scala @@ -12,7 +12,8 @@ def apply( currentFiles: Signal[List[File]], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val (updatesStream, updatesObserver) = EventStream.withObserver[Event] val selectorOpen = Var[Boolean](false) @@ -52,7 +53,7 @@ overlay, browserCenteringModalTrick, child <-- currentFiles.map( - FileSelector(_, availableFiles)(updatesObserver) + FileSelector(_, availableFiles, headerActions)(updatesObserver) ) ) ) diff --git a/ui/src/services/files/components/tailwind/FilePicker.scala b/ui/src/services/files/components/tailwind/FilePicker.scala index 0dabf8d..74029cb 100644 --- a/ui/src/services/files/components/tailwind/FilePicker.scala +++ b/ui/src/services/files/components/tailwind/FilePicker.scala @@ -12,7 +12,8 @@ def apply( currentFiles: Signal[List[File]], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val (updatesStream, updatesObserver) = EventStream.withObserver[Event] val selectorOpen = Var[Boolean](false) @@ -52,7 +53,7 @@ overlay, browserCenteringModalTrick, child <-- currentFiles.map( - FileSelector(_, availableFiles)(updatesObserver) + FileSelector(_, availableFiles, headerActions)(updatesObserver) ) ) ) diff --git a/ui/src/services/files/components/tailwind/FileSelector.scala b/ui/src/services/files/components/tailwind/FileSelector.scala index 3226356..cc4ee36 100644 --- a/ui/src/services/files/components/tailwind/FileSelector.scala +++ b/ui/src/services/files/components/tailwind/FileSelector.scala @@ -13,7 +13,8 @@ def apply( initialFiles: List[File], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val selectedFiles = Var[Set[File]](initialFiles.to(Set)) div( @@ -26,7 +27,7 @@ div( cls("bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"), div( - cls("sm:flex sm:items-start"), + cls("sm:flex sm:items-start sm:justify-between"), div( cls("mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"), h3( @@ -34,9 +35,10 @@ idAttr("modal-headline"), "Výběr souborů" ) - ) + ), + headerActions ), - FileTable(availableFiles, selectedFiles) + FileTable(availableFiles, Some(selectedFiles)) ), div( cls("bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6"), diff --git a/ui/src/services/files/components/tailwind/FilePicker.scala b/ui/src/services/files/components/tailwind/FilePicker.scala index 0dabf8d..74029cb 100644 --- a/ui/src/services/files/components/tailwind/FilePicker.scala +++ b/ui/src/services/files/components/tailwind/FilePicker.scala @@ -12,7 +12,8 @@ def apply( currentFiles: Signal[List[File]], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val (updatesStream, updatesObserver) = EventStream.withObserver[Event] val selectorOpen = Var[Boolean](false) @@ -52,7 +53,7 @@ overlay, browserCenteringModalTrick, child <-- currentFiles.map( - FileSelector(_, availableFiles)(updatesObserver) + FileSelector(_, availableFiles, headerActions)(updatesObserver) ) ) ) diff --git a/ui/src/services/files/components/tailwind/FileSelector.scala b/ui/src/services/files/components/tailwind/FileSelector.scala index 3226356..cc4ee36 100644 --- a/ui/src/services/files/components/tailwind/FileSelector.scala +++ b/ui/src/services/files/components/tailwind/FileSelector.scala @@ -13,7 +13,8 @@ def apply( initialFiles: List[File], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val selectedFiles = Var[Set[File]](initialFiles.to(Set)) div( @@ -26,7 +27,7 @@ div( cls("bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"), div( - cls("sm:flex sm:items-start"), + cls("sm:flex sm:items-start sm:justify-between"), div( cls("mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"), h3( @@ -34,9 +35,10 @@ idAttr("modal-headline"), "Výběr souborů" ) - ) + ), + headerActions ), - FileTable(availableFiles, selectedFiles) + FileTable(availableFiles, Some(selectedFiles)) ), div( cls("bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6"), diff --git a/ui/src/services/files/components/tailwind/FileTable.scala b/ui/src/services/files/components/tailwind/FileTable.scala index 247ad2d..8143b9d 100644 --- a/ui/src/services/files/components/tailwind/FileTable.scala +++ b/ui/src/services/files/components/tailwind/FileTable.scala @@ -12,9 +12,10 @@ def FileTable( files: Signal[List[File]], - selectedFiles: Var[Set[File]] + maybeSelection: Option[Var[Set[File]]] = None ): HtmlElement = val scope = customHtmlAttr("scope", StringAsIsCodec) + val selectedFiles = maybeSelection.getOrElse(Var(Set.empty)) def headerRow: HtmlElement = val col = scope("col") @@ -23,7 +24,7 @@ "text-left text-xs font-medium text-gray-500 uppercase tracking-wider" ) tr( - th(baseM, span(cls("sr-only"), "Vybrat")), + maybeSelection.map(_ => th(baseM, span(cls("sr-only"), "Vybrat"))), th(baseM, textH, "Soubor"), th(baseM, textH, "Kategorie"), th(baseM, textH, "Vytvořen"), @@ -38,12 +39,14 @@ val baseC = cls("px-6 py-4 whitespace-nowrap text-sm") tr( cls(if idx % 2 == 0 then "bg-gray-50" else "bg-white"), - td( - cls("font-medium cursor-pointer"), - onClick.mapTo(()) --> toggleSelection, - cls(if selected then "text-green-900" else "text-gray-200"), - Icons.outline.`check-circle`("w-6 h-6 mx-auto"), - span(cls("sr-only"), if selected then "Vybráno" else "Nevybráno") + maybeSelection.map(_ => + td( + cls("font-medium cursor-pointer"), + onClick.mapTo(()) --> toggleSelection, + cls(if selected then "text-green-900" else "text-gray-200"), + Icons.outline.`check-circle`("w-6 h-6 mx-auto"), + span(cls("sr-only"), if selected then "Vybráno" else "Nevybráno") + ) ), td( baseC, diff --git a/ui/src/services/files/components/tailwind/FilePicker.scala b/ui/src/services/files/components/tailwind/FilePicker.scala index 0dabf8d..74029cb 100644 --- a/ui/src/services/files/components/tailwind/FilePicker.scala +++ b/ui/src/services/files/components/tailwind/FilePicker.scala @@ -12,7 +12,8 @@ def apply( currentFiles: Signal[List[File]], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val (updatesStream, updatesObserver) = EventStream.withObserver[Event] val selectorOpen = Var[Boolean](false) @@ -52,7 +53,7 @@ overlay, browserCenteringModalTrick, child <-- currentFiles.map( - FileSelector(_, availableFiles)(updatesObserver) + FileSelector(_, availableFiles, headerActions)(updatesObserver) ) ) ) diff --git a/ui/src/services/files/components/tailwind/FileSelector.scala b/ui/src/services/files/components/tailwind/FileSelector.scala index 3226356..cc4ee36 100644 --- a/ui/src/services/files/components/tailwind/FileSelector.scala +++ b/ui/src/services/files/components/tailwind/FileSelector.scala @@ -13,7 +13,8 @@ def apply( initialFiles: List[File], - availableFiles: Signal[List[File]] + availableFiles: Signal[List[File]], + headerActions: Option[HtmlElement] = None )(selectionUpdates: Observer[Event]): HtmlElement = val selectedFiles = Var[Set[File]](initialFiles.to(Set)) div( @@ -26,7 +27,7 @@ div( cls("bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"), div( - cls("sm:flex sm:items-start"), + cls("sm:flex sm:items-start sm:justify-between"), div( cls("mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"), h3( @@ -34,9 +35,10 @@ idAttr("modal-headline"), "Výběr souborů" ) - ) + ), + headerActions ), - FileTable(availableFiles, selectedFiles) + FileTable(availableFiles, Some(selectedFiles)) ), div( cls("bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6"), diff --git a/ui/src/services/files/components/tailwind/FileTable.scala b/ui/src/services/files/components/tailwind/FileTable.scala index 247ad2d..8143b9d 100644 --- a/ui/src/services/files/components/tailwind/FileTable.scala +++ b/ui/src/services/files/components/tailwind/FileTable.scala @@ -12,9 +12,10 @@ def FileTable( files: Signal[List[File]], - selectedFiles: Var[Set[File]] + maybeSelection: Option[Var[Set[File]]] = None ): HtmlElement = val scope = customHtmlAttr("scope", StringAsIsCodec) + val selectedFiles = maybeSelection.getOrElse(Var(Set.empty)) def headerRow: HtmlElement = val col = scope("col") @@ -23,7 +24,7 @@ "text-left text-xs font-medium text-gray-500 uppercase tracking-wider" ) tr( - th(baseM, span(cls("sr-only"), "Vybrat")), + maybeSelection.map(_ => th(baseM, span(cls("sr-only"), "Vybrat"))), th(baseM, textH, "Soubor"), th(baseM, textH, "Kategorie"), th(baseM, textH, "Vytvořen"), @@ -38,12 +39,14 @@ val baseC = cls("px-6 py-4 whitespace-nowrap text-sm") tr( cls(if idx % 2 == 0 then "bg-gray-50" else "bg-white"), - td( - cls("font-medium cursor-pointer"), - onClick.mapTo(()) --> toggleSelection, - cls(if selected then "text-green-900" else "text-gray-200"), - Icons.outline.`check-circle`("w-6 h-6 mx-auto"), - span(cls("sr-only"), if selected then "Vybráno" else "Nevybráno") + maybeSelection.map(_ => + td( + cls("font-medium cursor-pointer"), + onClick.mapTo(()) --> toggleSelection, + cls(if selected then "text-green-900" else "text-gray-200"), + Icons.outline.`check-circle`("w-6 h-6 mx-auto"), + span(cls("sr-only"), if selected then "Vybráno" else "Nevybráno") + ) ), td( baseC, diff --git a/ui/src/services/files/components/tailwind/UploadButton.scala b/ui/src/services/files/components/tailwind/UploadButton.scala new file mode 100644 index 0000000..cff5cc5 --- /dev/null +++ b/ui/src/services/files/components/tailwind/UploadButton.scala @@ -0,0 +1,41 @@ +package services.files.components.tailwind + +import com.raquo.laminar.api.L.{*, given} +import works.iterative.ui.components.tailwind.Component +import com.raquo.laminar.nodes.ReactiveHtmlElement +import works.iterative.ui.{*, given} +import org.scalajs.dom.FileList + +class UploadButton(upload: Observer[FileList]) + extends Component[org.scalajs.dom.html.Div]: + + override def element: ReactiveHtmlElement[org.scalajs.dom.html.Div] = + div( + cls := "mt-4 sm:mt-0 sm:ml-16 sm:flex-none", + label( + cls("block w-full"), + div( + cls( + "inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto" + ), { + import svg.* + svg( + cls("w-6 h-6"), + fill := "#FFF", + viewBox := "0 0 24 24", + xmlns := "http://www.w3.org/2000/svg", + path(d := "M0 0h24v24H0z", fill := "none"), + path(d := "M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z") + ) + }, + span(cls := "ml-2", "Přidat soubory".ui) + ), + input( + cls := "cursor-pointer hidden", + tpe := "file", + name := "files", + multiple := true, + inContext(thisNode => onInput.mapTo(thisNode.ref.files) --> upload) + ) + ) + )