diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala index 919669e..34f3eab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala @@ -133,5 +133,33 @@ clipRule := "evenodd" ) ) + + def search = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + clipRule := "evenodd" + ) + ) + + def filter = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z", + clipRule := "evenodd" + ) + ) end solid end Icons diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala index 919669e..34f3eab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala @@ -133,5 +133,33 @@ clipRule := "evenodd" ) ) + + def search = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + clipRule := "evenodd" + ) + ) + + def filter = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z", + clipRule := "evenodd" + ) + ) end solid end Icons diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala index 51ab5e9..172d5b5 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala @@ -18,12 +18,7 @@ ) def MainSection(mods: Modifier[HtmlElement]*): HtmlElement = - main( - div( - cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", - mods - ) - ) + main(mods) def Layout( logo: Navigation.Logo, diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala index 919669e..34f3eab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala @@ -133,5 +133,33 @@ clipRule := "evenodd" ) ) + + def search = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + clipRule := "evenodd" + ) + ) + + def filter = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z", + clipRule := "evenodd" + ) + ) end solid end Icons diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala index 51ab5e9..172d5b5 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala @@ -18,12 +18,7 @@ ) def MainSection(mods: Modifier[HtmlElement]*): HtmlElement = - main( - div( - cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", - mods - ) - ) + main(mods) def Layout( logo: Navigation.Logo, diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala index 32f62a7..e518682 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala @@ -4,28 +4,7 @@ import com.raquo.laminar.api.L.{*, given} import cz.e_bs.cmi.mdr.pdb.app.{Page, UserProfile} import com.raquo.waypoint.Router -import org.scalajs.dom - -def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = - Binder { el => - - val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] - - if (isLinkElement) { - el.amend(href(router.absoluteUrlForPage(page))) - } - - // If element is a link and user is holding a modifier while clicking: - // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key - // Otherwise: - // - Perform regular pushState transition - (onClick - .filter(ev => - !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) - ) - .preventDefault - --> (_ => router.pushState(page))).bind(el) - } +import cz.e_bs.cmi.mdr.pdb.app.Routes.navigateTo object Navigation: diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala index 919669e..34f3eab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala @@ -133,5 +133,33 @@ clipRule := "evenodd" ) ) + + def search = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + clipRule := "evenodd" + ) + ) + + def filter = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z", + clipRule := "evenodd" + ) + ) end solid end Icons diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala index 51ab5e9..172d5b5 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala @@ -18,12 +18,7 @@ ) def MainSection(mods: Modifier[HtmlElement]*): HtmlElement = - main( - div( - cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", - mods - ) - ) + main(mods) def Layout( logo: Navigation.Logo, diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala index 32f62a7..e518682 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala @@ -4,28 +4,7 @@ import com.raquo.laminar.api.L.{*, given} import cz.e_bs.cmi.mdr.pdb.app.{Page, UserProfile} import com.raquo.waypoint.Router -import org.scalajs.dom - -def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = - Binder { el => - - val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] - - if (isLinkElement) { - el.amend(href(router.absoluteUrlForPage(page))) - } - - // If element is a link and user is holding a modifier while clicking: - // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key - // Otherwise: - // - Perform regular pushState transition - (onClick - .filter(ev => - !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) - ) - .preventDefault - --> (_ => router.pushState(page))).bind(el) - } +import cz.e_bs.cmi.mdr.pdb.app.Routes.navigateTo object Navigation: diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala index 147e17b..bc8f32b 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala @@ -7,10 +7,11 @@ import cz.e_bs.cmi.mdr.pdb.app.components.Avatar import cz.e_bs.cmi.mdr.pdb.app.Page import cz.e_bs.cmi.mdr.pdb.app.services.DataFetcher +import com.raquo.airstream.core.EventStream val datetime = customHtmlAttr("datetime", StringAsIsCodec) -def DetailPage(fetch: DataFetcher[String, Osoba])( +def DetailPage(fetch: String => EventStream[Osoba])( $page: Signal[Page.Detail] ): HtmlElement = // TODO: proper loader @@ -25,9 +26,8 @@ val data = Var[Option[Osoba]](None) val maybeOsobaSignal = data.signal.split(_ => ())((_, _, s) => OsobaView(s)) div( - $page --> ((d: Page.Detail) => - fetch.fetch(d.osobniCislo, data.writer.contramapSome) - ), + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + $page.flatMap(pd => fetch(pd.osobniCislo)) --> data.writer.contramapSome, child <-- maybeOsobaSignal.map(_.getOrElse(loading)) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index 4465cf6..76fe6ac 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -43,15 +43,16 @@ def renderPage(using router: Router[Page]): HtmlElement = val pageSplitter = SplitRender[Page, HtmlElement](router.$currentPage) .collectSignal[Page.Detail]( - pages.DetailPage( - new DataFetcher[String, Osoba] { - override def fetch(osobniCislo: String, o: Observer[Osoba]): Unit = - o.delay(1000).onNext(ExampleData.persons.jmeistrova) - } + pages.DetailPage(osc => + EventStream.fromValue(ExampleData.persons.jmeistrova).delay(1000) ) ) .collectStatic(Page.Dashboard)(pages.DashboardPage) - .collectStatic(Page.Directory)(pages.DirectoryPage) + .collectStatic(Page.Directory)( + pages.DirectoryPage( + EventStream.fromValue(List(ExampleData.persons.jmeistrova)) + ) + ) components.MainSection(child <-- pageSplitter.$view) // TODO: pages by logged in user diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala index 0bd271d..c5c49d7 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Routes.scala @@ -4,6 +4,7 @@ import com.raquo.waypoint.* import zio.json.{*, given} import scala.scalajs.js +import org.scalajs.dom // enum is not working with Waypoints' SplitRender collectStatic sealed abstract class Page(val title: String) @@ -47,3 +48,25 @@ $popStateEvent = windowEvents.onPopState, owner = unsafeWindowOwner ) + + // TODO: evaluate dangers of a global router in a SPA + def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = + Binder { el => + + val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] + + if (isLinkElement) { + el.amend(href(router.absoluteUrlForPage(page))) + } + + // If element is a link and user is holding a modifier while clicking: + // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key + // Otherwise: + // - Perform regular pushState transition + (onClick + .filter(ev => + !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) + ) + .preventDefault + --> (_ => router.pushState(page))).bind(el) + } diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala index 919669e..34f3eab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Icons.scala @@ -133,5 +133,33 @@ clipRule := "evenodd" ) ) + + def search = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", + clipRule := "evenodd" + ) + ) + + def filter = + svg( + cls := "h-5 w-5 text-gray-400", + xmlns := "http://www.w3.org/2000/svg", + viewBox := "0 0 20 20", + fill := "currentColor", + aria.hidden := true, + path( + fillRule := "evenodd", + d := "M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V3z", + clipRule := "evenodd" + ) + ) end solid end Icons diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala index 51ab5e9..172d5b5 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Layout.scala @@ -18,12 +18,7 @@ ) def MainSection(mods: Modifier[HtmlElement]*): HtmlElement = - main( - div( - cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", - mods - ) - ) + main(mods) def Layout( logo: Navigation.Logo, diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala index 32f62a7..e518682 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/components/Navigation.scala @@ -4,28 +4,7 @@ import com.raquo.laminar.api.L.{*, given} import cz.e_bs.cmi.mdr.pdb.app.{Page, UserProfile} import com.raquo.waypoint.Router -import org.scalajs.dom - -def navigateTo(page: Page)(using router: Router[Page]): Binder[HtmlElement] = - Binder { el => - - val isLinkElement = el.ref.isInstanceOf[dom.html.Anchor] - - if (isLinkElement) { - el.amend(href(router.absoluteUrlForPage(page))) - } - - // If element is a link and user is holding a modifier while clicking: - // - Do nothing, browser will open the URL in new tab / window / etc. depending on the modifier key - // Otherwise: - // - Perform regular pushState transition - (onClick - .filter(ev => - !(isLinkElement && (ev.ctrlKey || ev.metaKey || ev.shiftKey || ev.altKey)) - ) - .preventDefault - --> (_ => router.pushState(page))).bind(el) - } +import cz.e_bs.cmi.mdr.pdb.app.Routes.navigateTo object Navigation: diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala index 147e17b..bc8f32b 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DetailPage.scala @@ -7,10 +7,11 @@ import cz.e_bs.cmi.mdr.pdb.app.components.Avatar import cz.e_bs.cmi.mdr.pdb.app.Page import cz.e_bs.cmi.mdr.pdb.app.services.DataFetcher +import com.raquo.airstream.core.EventStream val datetime = customHtmlAttr("datetime", StringAsIsCodec) -def DetailPage(fetch: DataFetcher[String, Osoba])( +def DetailPage(fetch: String => EventStream[Osoba])( $page: Signal[Page.Detail] ): HtmlElement = // TODO: proper loader @@ -25,9 +26,8 @@ val data = Var[Option[Osoba]](None) val maybeOsobaSignal = data.signal.split(_ => ())((_, _, s) => OsobaView(s)) div( - $page --> ((d: Page.Detail) => - fetch.fetch(d.osobniCislo, data.writer.contramapSome) - ), + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + $page.flatMap(pd => fetch(pd.osobniCislo)) --> data.writer.contramapSome, child <-- maybeOsobaSignal.map(_.getOrElse(loading)) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DirectoryPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DirectoryPage.scala index 368bc0a..0b2eb22 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DirectoryPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/DirectoryPage.scala @@ -1,6 +1,106 @@ package cz.e_bs.cmi.mdr.pdb.app.pages import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.app.components.Icons +import cz.e_bs.cmi.mdr.pdb.app.Osoba +import cz.e_bs.cmi.mdr.pdb.app.Routes.navigateTo +import cz.e_bs.cmi.mdr.pdb.app.Page +import com.raquo.waypoint.Router -def DirectoryPage: HtmlElement = - div("Directory page") +def DirectoryPage(data: EventStream[List[Osoba]])(using + router: Router[Page] +): HtmlElement = + div( + cls := "max-w-7xl mx-auto", + //cls := "xl:order-first xl:flex xl:flex-col flex-shrink-0 w-96 border-r border-gray-200", + form( + cls := "p-4 mt-6 flex space-x-4", + action := "#", + div( + cls := "flex-1 min-w-0", + label( + forId := "search", + cls := "sr-only", + """Search""" + ), + div( + cls := "relative rounded-md shadow-sm", + div( + cls := "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", + Icons.solid.search + ), + input( + tpe := "search", + name := "search", + idAttr := "search", + cls := "focus:ring-pink-500 focus:border-pink-500 block w-full pl-10 sm:text-sm border-gray-300 rounded-md", + placeholder := "Search" + ) + ) + ), + button( + tpe := "submit", + cls := "inline-flex justify-center px-3.5 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500", + Icons.solid.filter, + span( + cls := "sr-only", + """Search""" + ) + ) + ), + nav( + cls := "flex-1 min-h-0 overflow-y-auto", + aria.label := "Directory", + div( + cls := "relative", + // TODO: group by surname + div( + cls := "z-10 sticky top-0 border-t border-b border-gray-200 bg-gray-50 px-6 py-1 text-sm font-medium text-gray-500", + h3( + """A""" + ) + ), + ul( + role := "list", + cls := "relative z-0 divide-y divide-gray-200", + // TODO: zero / loading page + children <-- data.map(_.map({ o => + val page = Page.Detail(o.osobniCislo) + li( + div( + cls := "relative px-6 py-5 flex items-center space-x-3 hover:bg-gray-50 focus-within:ring-2 focus-within:ring-inset focus-within:ring-pink-500", + div( + cls := "flex-shrink-0", + img( + cls := "h-10 w-10 rounded-full", + src := "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", + alt := "" + ) + ), + div( + cls := "flex-1 min-w-0", + a( + href := router.absoluteUrlForPage(page), + navigateTo(page), + cls := "focus:outline-none", + span( + cls := "absolute inset-0", + aria.hidden := true + ), + p( + cls := "text-sm font-medium text-gray-900", + o.jmeno + ), + p( + cls := "text-sm text-gray-500 truncate", + o.hlavniFunkce.nazev + ) + ) + ) + ) + ) + })) + ) + ) + ) + )