aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/reading/component
diff options
context:
space:
mode:
authorJoris2017-02-25 22:25:03 +0100
committerJoris2017-02-25 22:25:03 +0100
commitbbe5788cdcfbb26358566bfc74426ec38029cc73 (patch)
tree27607f6b5a667c264f11aeb10708d6d1dadb0fde /src/main/scala/reading/component
parentf1de0dd7632eb29a40ea1f5cf136ab43ee945926 (diff)
downloadreading-bbe5788cdcfbb26358566bfc74426ec38029cc73.tar.gz
reading-bbe5788cdcfbb26358566bfc74426ec38029cc73.tar.bz2
reading-bbe5788cdcfbb26358566bfc74426ec38029cc73.zip
Add detailed book page instead of a modal.
Diffstat (limited to 'src/main/scala/reading/component')
-rw-r--r--src/main/scala/reading/component/Index.scala11
-rw-r--r--src/main/scala/reading/component/index/BookDetail.scala81
-rw-r--r--src/main/scala/reading/component/index/Books.scala94
-rw-r--r--src/main/scala/reading/component/index/FilterUtils.scala6
-rw-r--r--src/main/scala/reading/component/index/Filters.scala65
-rw-r--r--src/main/scala/reading/component/index/Header.scala45
-rw-r--r--src/main/scala/reading/component/index/Menu.scala104
-rw-r--r--src/main/scala/reading/component/index/style/BookDetail.scala65
-rw-r--r--src/main/scala/reading/component/index/style/Books.scala15
-rw-r--r--src/main/scala/reading/component/index/style/Filters.scala69
-rw-r--r--src/main/scala/reading/component/index/style/Header.scala61
-rw-r--r--src/main/scala/reading/component/index/style/Menu.scala52
-rw-r--r--src/main/scala/reading/component/style/Global.scala3
-rw-r--r--src/main/scala/reading/component/style/Index.scala4
-rw-r--r--src/main/scala/reading/component/widget/Animate.scala17
-rw-r--r--src/main/scala/reading/component/widget/AnimateMethod.scala29
-rw-r--r--src/main/scala/reading/component/widget/Input.scala23
-rw-r--r--src/main/scala/reading/component/widget/Modal.scala59
-rw-r--r--src/main/scala/reading/component/widget/style/Input.scala15
-rw-r--r--src/main/scala/reading/component/widget/style/Modal.scala65
20 files changed, 478 insertions, 405 deletions
diff --git a/src/main/scala/reading/component/Index.scala b/src/main/scala/reading/component/Index.scala
index 78890de..0105150 100644
--- a/src/main/scala/reading/component/Index.scala
+++ b/src/main/scala/reading/component/Index.scala
@@ -5,26 +5,25 @@ import scalacss.Defaults._
import scalacss.ScalatagsCss._
import scalatags.JsDom.all._
-import reading.component.index.{ Menu, Header, Books => BooksComponent }
+import reading.component.index.{ Menu, Books => BooksComponent }
import reading.component.style.{ Index => IndexStyle }
import reading.models.{ Book, Books, Filter }
object Index {
- def apply(initialFilters: Seq[Filter])(implicit ctx: Ctx.Owner): Frag = {
+ def apply(initialFilters: Seq[Filter], initialDetail: Option[Book])(implicit ctx: Ctx.Owner): Frag = {
val filters: Var[Seq[Filter]] = Var(initialFilters)
val books: Rx[Seq[Book]] = Rx(Filter.add(Books(), filters()))
val search: Var[String] = Var("")
val showFiltersMenu: Var[Boolean] = Var(false)
- val searchedBooks: Rx[Seq[Book]] = Rx(Book.filter(books(), search()))
+ val detail: Var[Option[Book]] = Var(initialDetail)
div(
IndexStyle.render,
IndexStyle.page,
- Menu(books, filters, search, showFiltersMenu),
+ Menu(books, filters, detail, search, showFiltersMenu),
div(
IndexStyle.main,
- Header(searchedBooks, filters, search, showFiltersMenu),
- BooksComponent(searchedBooks)
+ BooksComponent(books, filters, detail, search, showFiltersMenu)
)
)
}
diff --git a/src/main/scala/reading/component/index/BookDetail.scala b/src/main/scala/reading/component/index/BookDetail.scala
index c42029f..ed91211 100644
--- a/src/main/scala/reading/component/index/BookDetail.scala
+++ b/src/main/scala/reading/component/index/BookDetail.scala
@@ -1,17 +1,28 @@
package reading.component.index
-import scalatags.JsDom.all._
+import scala.util.Random
+
import scalacss.Defaults._
import scalacss.ScalatagsCss._
+import scalatags.JsDom.all._
import reading.component.index.style.{ BookDetail => BookStyle }
-import reading.models.{ Program, Book }
+import reading.component.widget.AnimateMethod
+import reading.models.{ Book, Program }
object BookDetail {
- def apply(book: Book): Frag =
+ val componentId = s"books${Random.nextInt}"
+
+ def apply(book: Book, parentId: String, onClose: => Unit): Frag = {
+ val titleParts = if (book.parts > 1) s", ${book.parts} volumes" else ""
+ val grades = book.programs.map(Program.grade(_)).distinct.sorted
+
+ AnimateMethod.fadeIn(componentId)
+
div(
BookStyle.render,
BookStyle.detail,
+ id := componentId,
img(
BookStyle.cover,
@@ -20,36 +31,48 @@ object BookDetail {
),
div(
- BookStyle.items,
+ BookStyle.presentation,
+ div(BookStyle.title, s"${book.title}$titleParts"),
+ div(BookStyle.author, book.author),
- if (book.programs.nonEmpty) {
- item("classe", book.programs.map(Program.grade(_).prettyPrint).distinct.sorted)
- },
- if (book.programs.nonEmpty) {
- item("programme", book.programs.map(p => "« " ++ p.prettyPrint ++ " »").sorted)
- },
- if (book.themes.nonEmpty) {
- item("thème", book.themes.sorted.map(_.prettyPrint))
+ book.resume match {
+ case Some(resume) =>
+ p(BookStyle.resume, raw(resume))
+ case _ =>
+ span("")
},
- if (book.genres.nonEmpty) {
- item("genre", book.genres.sorted.map(_.prettyPrint))
- },
- book.period.map { period =>
- item("période", period.prettyPrint)
- },
- item("niveau", book.level.prettyPrint)
- )
- )
- private def item(key: String, value: String): Frag = item(key, Seq(value))
+ dl(
+ BookStyle.definitions,
- private def item(key: String, values: Seq[String]): Frag =
- div(
- BookStyle.item,
- div(BookStyle.itemName, key),
- ul(
- BookStyle.itemValues,
- values.map(value => li(BookStyle.itemValue, value.capitalize))
+ grades.map { grade =>
+ val programs = book.programs.filter(p => Program.grade(p) == grade).sorted
+ val pp = grade.prettyPrint
+ definition(pp, pp, programs.map(p => s"« ${p.prettyPrint} »"))
+ },
+ if (book.themes.nonEmpty) {
+ definition("thème", "thèmes", book.themes.sorted.map(_.prettyPrint))
+ },
+ if (book.genres.nonEmpty) {
+ definition("genre", "genres", book.genres.sorted.map(_.prettyPrint))
+ },
+ definition("niveau", "niveaux", Seq(book.level.prettyPrint))
+ ),
+
+ button(
+ BookStyle.close,
+ onclick := (() => onClose),
+ "Fermer"
+ )
)
)
+ }
+
+ private def definition(key: String, pluralKey: String, values: Seq[String]): Seq[Frag] = {
+ val term = if (values.length > 1) pluralKey else key
+ Seq(
+ dt(BookStyle.definitionTerm, s"${term.capitalize} :"),
+ dd(BookStyle.definitionDescription, values.mkString(", "))
+ )
+ }
}
diff --git a/src/main/scala/reading/component/index/Books.scala b/src/main/scala/reading/component/index/Books.scala
index c22639f..b5e172b 100644
--- a/src/main/scala/reading/component/index/Books.scala
+++ b/src/main/scala/reading/component/index/Books.scala
@@ -1,49 +1,89 @@
package reading.component.index
-import rx._
+import scala.util.Random
-import scalatags.JsDom.all._
+import rx._
import scalacss.Defaults._
import scalacss.ScalatagsCss._
+import scalatags.JsDom.all._
import reading.component.index.style.{ Books => BooksStyle }
-import reading.component.widget.Modal
-import reading.models.{ Book }
+import reading.component.widget.AnimateMethod
+import reading.models.{ Book, Filter }
+import reading.Route
import reading.utils.RxUtils._
object Books {
- def apply(books: Rx[Seq[Book]])(implicit ctx: Ctx.Owner): Frag = {
- val focus: Var[Option[Book]] = Var(None)
+ val componentId = s"books${Random.nextInt}"
+
+ def apply(
+ books: Rx[Seq[Book]],
+ filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
+ search: Var[String],
+ showFiltersMenu: Var[Boolean]
+ )(
+ implicit
+ ctx: Ctx.Owner
+ ): Frag = {
+ val searchedBooks: Rx[Seq[Book]] = Rx(Book.filter(books(), search()))
+
+ if (detail.now.isEmpty) AnimateMethod.fadeIn(id = componentId)
div(
- BooksStyle.render,
+ BooksStyle.booksParent,
+
+ div(
+ id := componentId,
+ BooksStyle.render,
+ BooksStyle.books,
+
+ Header(searchedBooks, filters, detail, search, showFiltersMenu),
- Rx {
div(
- div(
- BooksStyle.books,
-
- books().sorted.map { book =>
- div(
- BooksStyle.book,
- img(
- BooksStyle.cover,
- src := s"cover/${book.title}.jpg",
- alt := s"${book.title}, ${book.author}",
- onclick := (() => focus() = Some(book))
- )
- )
- }
- ),
+ BooksStyle.listParent,
Rx {
- focus() match {
- case Some(book) => Modal(onClose = focus() = None)(BookDetail(book))
- case None => span("")
- }
+ div(
+ BooksStyle.list,
+
+ searchedBooks().sorted.map { book =>
+ div(
+ BooksStyle.book,
+ img(
+ BooksStyle.cover,
+ src := s"cover/${book.title}.jpg",
+ alt := s"${book.title}, ${book.author}",
+ onclick := (() => {
+ Route.push(Route.Books(filters.now, Some(book)))
+ AnimateMethod.fadeOut(
+ id = componentId,
+ onEnd = detail() = Some(book)
+ )
+ })
+ )
+ )
+ }
+ )
}
)
+ ),
+
+ Rx {
+ detail() match {
+ case Some(book) =>
+ BookDetail(book, componentId, onClose = closeDetail(filters, detail))
+ case None =>
+ span("")
+ }
}
)
}
+
+ def closeDetail(filters: Var[Seq[Filter]], detail: Var[Option[Book]]): Unit =
+ AnimateMethod.fadeOut(BookDetail.componentId, onEnd = {
+ detail() = None
+ Route.push(Route.Books(filters.now, None))
+ AnimateMethod.fadeIn(componentId)
+ })
}
diff --git a/src/main/scala/reading/component/index/FilterUtils.scala b/src/main/scala/reading/component/index/FilterUtils.scala
index d4b24e4..89f993a 100644
--- a/src/main/scala/reading/component/index/FilterUtils.scala
+++ b/src/main/scala/reading/component/index/FilterUtils.scala
@@ -8,31 +8,37 @@ import reading.Route
object FilterUtils {
def remove(
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
filter: Filter
): Unit = {
val newFilters = Filter.remove(filters.now, filter)
filters() = newFilters
+ if (detail.now.nonEmpty) Books.closeDetail(filters, detail)
search() = ""
Route.push(Route.Books(newFilters))
}
def removeAll(
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String]
): Unit = {
filters() = Nil
+ if (detail.now.nonEmpty) Books.closeDetail(filters, detail)
search() = ""
Route.push(Route.Books(Nil))
}
def add(
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
filter: Filter
): Unit = {
val newFilters = filter +: filters.now
filters() = newFilters
+ if (detail.now.nonEmpty) Books.closeDetail(filters, detail)
search() = ""
Route.push(Route.Books(newFilters))
}
diff --git a/src/main/scala/reading/component/index/Filters.scala b/src/main/scala/reading/component/index/Filters.scala
new file mode 100644
index 0000000..3aa26e8
--- /dev/null
+++ b/src/main/scala/reading/component/index/Filters.scala
@@ -0,0 +1,65 @@
+package reading.component.index
+
+import rx._
+
+import scalatags.JsDom.all._
+import scalacss.Defaults._
+import scalacss.ScalatagsCss._
+
+import reading.component.index.style.{ Filters => FiltersStyle }
+import reading.component.widget.Cross
+import reading.component.style.{ Color => C }
+import reading.models.{ Filter, Book }
+import reading.utils.RxUtils._
+
+object Filters {
+ def apply(
+ filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
+ search: Var[String],
+ showFiltersMenu: Var[Boolean]
+ )(
+ implicit
+ ctx: Ctx.Owner
+ ): Frag = {
+ val filtersCount: Rx[Int] = Rx(filters().length)
+
+ div(
+ FiltersStyle.render,
+ FiltersStyle.filters,
+
+ Rx {
+ div(
+ div(
+ FiltersStyle.showFiltersMenu,
+ onclick := (() => showFiltersMenu() = true),
+ "Filtrer",
+ if (filtersCount() > 0) span(FiltersStyle.filtersCount, filtersCount()) else span("")
+ ),
+
+ if (filters().isEmpty)
+ span("")
+ else
+ div(
+ FiltersStyle.values,
+
+ div(
+ FiltersStyle.clear,
+ onclick := (() => FilterUtils.removeAll(filters, detail, search)),
+ "Effacer les filtres"
+ ),
+
+ filters().sortBy(_.name).map { filter =>
+ div(
+ FiltersStyle.filter,
+ onclick := (() => FilterUtils.remove(filters, detail, search, filter)),
+ span(FiltersStyle.name, filter.name.capitalize),
+ Cross(15.px, C.gray.value)
+ )
+ }
+ )
+ )
+ }
+ )
+ }
+}
diff --git a/src/main/scala/reading/component/index/Header.scala b/src/main/scala/reading/component/index/Header.scala
index 50d520e..0809b0c 100644
--- a/src/main/scala/reading/component/index/Header.scala
+++ b/src/main/scala/reading/component/index/Header.scala
@@ -1,14 +1,12 @@
package reading.component.index
import rx._
-
-import scalatags.JsDom.all._
import scalacss.Defaults._
import scalacss.ScalatagsCss._
+import scalatags.JsDom.all._
import reading.component.index.style.{ Header => HeaderStyle }
-import reading.component.widget.{ Cross, Input }
-import reading.component.style.{ Color => C }
+import reading.component.widget.Input
import reading.models.{ Book, Filter }
import reading.utils.RxUtils._
@@ -16,58 +14,27 @@ object Header {
def apply(
books: Rx[Seq[Book]],
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
showFiltersMenu: Var[Boolean]
)(
implicit
ctx: Ctx.Owner
): Frag = {
- val filtersCount: Rx[Int] = Rx(filters().length)
val booksCount: Rx[Int] = books.map(_.length)
div(
HeaderStyle.render,
HeaderStyle.header,
- Rx {
- div(
- div(
- HeaderStyle.showFiltersMenu,
- onclick := (() => showFiltersMenu() = true),
- "Filtrer",
- if (filtersCount() > 0) span(HeaderStyle.filtersCount, filtersCount()) else span("")
- ),
-
- if (filters().isEmpty)
- span("")
- else
- div(
- HeaderStyle.filters,
-
- div(
- HeaderStyle.clear,
- onclick := (() => FilterUtils.removeAll(filters, search)),
- "Effacer les filtres"
- ),
-
- filters().sortBy(_.name).map { filter =>
- div(
- HeaderStyle.filter,
- onclick := (() => FilterUtils.remove(filters, search, filter)),
- span(HeaderStyle.name, filter.name.capitalize),
- Cross(15.px, C.black.value)
- )
- }
- )
- )
- },
+ Filters(filters, detail, search, showFiltersMenu),
div(
HeaderStyle.searchAndCount,
- Input(HeaderStyle.search, search, "Rechercher"),
+ Input(HeaderStyle.search, search, "Rechercher", maxLength = Some(25)),
Rx {
div(
- HeaderStyle.booksCount,
+ HeaderStyle.count,
span(s"${booksCount()} livre${if (booksCount() > 1) "s" else ""}")
)
}
diff --git a/src/main/scala/reading/component/index/Menu.scala b/src/main/scala/reading/component/index/Menu.scala
index 4c118bd..cfeb6d4 100644
--- a/src/main/scala/reading/component/index/Menu.scala
+++ b/src/main/scala/reading/component/index/Menu.scala
@@ -2,9 +2,9 @@ package reading.component.index
import rx._
-import scalatags.JsDom.all._
import scalacss.Defaults._
import scalacss.ScalatagsCss._
+import scalatags.JsDom.all._
import reading.component.index.style.{ Menu => MenuStyle }
import reading.models._
@@ -14,6 +14,7 @@ object Menu {
def apply(
books: Rx[Seq[Book]],
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
showFiltersMenu: Var[Boolean]
)(
@@ -25,40 +26,34 @@ object Menu {
Rx(if (showFiltersMenu()) MenuStyle.show else MenuStyle.empty),
MenuStyle.menu,
- div(MenuStyle.background),
+ Rx(header(showFiltersMenu, filters().length)),
div(
- MenuStyle.content,
-
- Rx(header(showFiltersMenu, filters().length)),
-
- div(
- MenuStyle.groups,
- Rx {
- filters().find(_.kind == FilterKind.Grade) match {
- case Some(grade) =>
- val programs = Program.values.filter(p => Program.grade(p).toString() == grade.nonFormattedName)
- group(books, filters, search, grade.name, programs.map(Filter.apply(_)), Some(grade))
- case None =>
- group(books, filters, search, "Classe", Grade.values.map(Filter.apply(_)))
- }
- },
- Rx {
- filters().find(_.kind == FilterKind.GroupedTheme) match {
- case Some(groupedTheme) =>
- val themes = Theme.values.filter(t => Theme.groupedTheme(t).toString() == groupedTheme.nonFormattedName)
- group(books, filters, search, groupedTheme.name, themes.map(Filter.apply(_)), Some(groupedTheme))
- case None =>
- group(books, filters, search, "Theme", GroupedTheme.values.map(Filter.apply(_)))
- }
- },
- group(books, filters, search, "Genre", Genre.values.sorted.map(Filter.apply(_))),
- group(books, filters, search, "Niveau", Level.values.map(Filter.apply(_))),
- group(books, filters, search, "Période", Period.values.map(Filter.apply(_)))
- ),
+ MenuStyle.groups,
+ Rx {
+ filters().find(_.kind == FilterKind.Grade) match {
+ case Some(grade) =>
+ val programs = Program.values.filter(p => Program.grade(p).toString() == grade.nonFormattedName).sorted
+ group(books, filters, detail, search, grade.name, grade.name, programs.map(Filter.apply(_)), Some(grade))
+ case None =>
+ group(books, filters, detail, search, "classe", "classes", Grade.values.sorted.map(Filter.apply(_)))
+ }
+ },
+ Rx {
+ filters().find(_.kind == FilterKind.GroupedTheme) match {
+ case Some(groupedTheme) =>
+ val themes = Theme.values.filter(t => Theme.grouped(t).toString() == groupedTheme.nonFormattedName).sorted
+ group(books, filters, detail, search, groupedTheme.name, groupedTheme.name, themes.map(Filter.apply(_)), Some(groupedTheme))
+ case None =>
+ group(books, filters, detail, search, "thème", "thèmes", GroupedTheme.values.sorted.map(Filter.apply(_)))
+ }
+ },
+ group(books, filters, detail, search, "genre", "genres", Genre.values.sorted.map(Filter.apply(_))),
+ group(books, filters, detail, search, "niveau", "niveaux", Level.values.sorted.map(Filter.apply(_))),
+ group(books, filters, detail, search, "période", "périodes", Period.values.sorted.map(Filter.apply(_)))
+ ),
- footer(books, filters, search, showFiltersMenu)
- )
+ footer(books, filters, detail, search, showFiltersMenu)
)
def header(showFiltersMenu: Var[Boolean], count: Int): Frag =
@@ -71,8 +66,10 @@ object Menu {
def group(
books: Rx[Seq[Book]],
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
name: String,
+ pluralName: String,
groupFilters: Seq[Filter],
parentFilter: Option[Filter] = None
)(
@@ -84,19 +81,20 @@ object Menu {
.map(filter => (filter, Filter.add(books(), filter).length))
.filter(_._2 > 0)
}
+ val filtersCount = filtersWithCount.map(_.length)
Rx {
- if (filtersWithCount().isEmpty)
+ if (filtersCount() == 0)
span("")
else
div(
div(
MenuStyle.filterTitle,
parentFilter.map { filter =>
- onclick := (() => FilterUtils.remove(filters, search, filter))
+ onclick := (() => FilterUtils.remove(filters, detail, search, filter))
}.getOrElse(""),
if (parentFilter.isDefined) MenuStyle.activeFilter else "",
- name,
+ if (filtersCount() > 1) pluralName else name,
Rx {
val count = filters().filter(f => groupFilters.exists(f == _)).length
if (count > 0) span(MenuStyle.filterTitleCount, count) else span("")
@@ -112,9 +110,9 @@ object Menu {
if (isActive) MenuStyle.activeFilter else "",
onclick := (() =>
if (isActive)
- FilterUtils.remove(filters, search, filter)
+ FilterUtils.remove(filters, detail, search, filter)
else
- FilterUtils.add(filters, search, filter)),
+ FilterUtils.add(filters, detail, search, filter)),
span(
span(filter.name.capitalize),
span(MenuStyle.filterCount, count)
@@ -130,26 +128,32 @@ object Menu {
def footer(
books: Rx[Seq[Book]],
filters: Var[Seq[Filter]],
+ detail: Var[Option[Book]],
search: Var[String],
showFiltersMenu: Var[Boolean]
)(
implicit
ctx: Ctx.Owner
): Frag =
- div(
- MenuStyle.footer,
- div(
- MenuStyle.clear,
- onclick := (() => FilterUtils.removeAll(filters, search)),
- "Effacer"
- ),
+ Rx {
div(
- MenuStyle.returnToBooks,
- onclick := (() => showFiltersMenu() = false),
- "Afficher",
- Rx {
+ MenuStyle.footer,
+
+ if (filters().nonEmpty)
+ div(
+ MenuStyle.clear,
+ onclick := (() => if (filters.now.nonEmpty) FilterUtils.removeAll(filters, detail, search)),
+ "Effacer"
+ )
+ else
+ span(""),
+
+ div(
+ MenuStyle.returnToBooks,
+ onclick := (() => showFiltersMenu() = false),
+ "Afficher",
span(MenuStyle.bookCount, books().length)
- }
+ )
)
- )
+ }
}
diff --git a/src/main/scala/reading/component/index/style/BookDetail.scala b/src/main/scala/reading/component/index/style/BookDetail.scala
index f432fda..2488a8f 100644
--- a/src/main/scala/reading/component/index/style/BookDetail.scala
+++ b/src/main/scala/reading/component/index/style/BookDetail.scala
@@ -2,15 +2,25 @@ package reading.component.index.style
import scalacss.Defaults._
-import reading.component.style.{ Color => C }
+import reading.component.style.{ Color => C, Button }
+import reading.Media
object BookDetail extends StyleSheet.Inline {
import dsl._
val detail = style(
+ position.fixed,
+ height(100.%%),
+ top(0.px),
+ right(0.px),
+ padding(30.px, 30.px, 0.px, 30.px),
+ overflowY.scroll,
display.flex,
flexWrap.wrap,
- justifyContent.spaceAround
+ justifyContent.spaceAround,
+ Media.desktop(width :=! "calc(100% - 280px)"),
+ Media.mobile(width(100.%%)),
+ opacity(0)
)
val cover = style(
@@ -18,32 +28,47 @@ object BookDetail extends StyleSheet.Inline {
marginBottom(30.px)
)
- val items = style(
- marginBottom(25.px)
+ val presentation = style(
+ Media.desktop(width(50.%%))
)
- val item = style(
- lineHeight(25.px),
- margin(0.px, 15.px, 15.px),
- &.lastChild(marginBottom(0.px))
+ val title = style(
+ color(C.congoBrown.value),
+ fontSize(26.px),
+ fontWeight.bold,
+ marginBottom(1.em),
+ lineHeight(1.4.em)
+ )
+
+ val author = style(
+ fontSize(20.px),
+ marginBottom(1.em)
+ )
+
+ val resume = style(
+ textAlign.justify,
+ lineHeight(1.4.em),
+ marginBottom(2.em)
+ )
+
+ val definitions = style(
+ marginBottom(2.5.em)
)
- val itemName = style(
+ val definitionTerm = style(
fontWeight.bold,
- textTransform.uppercase,
- marginBottom(10.px)
+ float.left,
+ marginRight(5.px),
+ lineHeight(1.4.em)
)
- val itemValues = style(
- marginLeft(15.px)
+ val definitionDescription = style(
+ marginBottom(1.em),
+ lineHeight(1.4.em)
)
- val itemValue = style(
- marginBottom(5.px),
- &.before(
- content := "\"•\"",
- color(C.stiletto.value),
- marginRight(10.px)
- )
+ val close = style(
+ Button.simple,
+ marginBottom(1.em)
)
}
diff --git a/src/main/scala/reading/component/index/style/Books.scala b/src/main/scala/reading/component/index/style/Books.scala
index ca52328..f7c1ffc 100644
--- a/src/main/scala/reading/component/index/style/Books.scala
+++ b/src/main/scala/reading/component/index/style/Books.scala
@@ -9,8 +9,23 @@ import reading.component.style.{ Color => C }
object Books extends StyleSheet.Inline {
import dsl._
+ val booksParent = style(
+ height(100.%%)
+ )
+
val books = style(
display.flex,
+ flexDirection.column,
+ height(100.%%),
+ opacity(0)
+ )
+
+ val listParent = style(
+ overflowY.scroll
+ )
+
+ val list = style(
+ display.flex,
flexWrap.wrap,
justifyContent.spaceAround
)
diff --git a/src/main/scala/reading/component/index/style/Filters.scala b/src/main/scala/reading/component/index/style/Filters.scala
new file mode 100644
index 0000000..e4346a2
--- /dev/null
+++ b/src/main/scala/reading/component/index/style/Filters.scala
@@ -0,0 +1,69 @@
+package reading.component.index.style
+
+import scalacss.Defaults._
+
+import reading.Media
+import reading.component.style.{ Color => C, Button, Count }
+
+object Filters extends StyleSheet.Inline {
+ import dsl._
+
+ val filters = style(
+ Media.mobile(margin(0.px, 30.px, 10.px))
+ )
+
+ val showFiltersMenu = style(
+ Media.desktop(display.none),
+ Media.mobile(
+ Button.simple,
+ marginBottom(20.px)
+ )
+ )
+
+ val filtersCount = style(
+ Count.major,
+ marginLeft(20.px)
+ )
+
+ val values = style(
+ display.flex,
+ flexWrap.wrap,
+ alignItems.center,
+ Media.desktop(margin(0.px, 40.px, 10.px)),
+ Media.mobile(display.none)
+ )
+
+ private val box = style(
+ display.flex,
+ alignItems.center,
+ height(50.px),
+ marginRight(20.px),
+ marginBottom(10.px),
+ padding(15.px),
+ borderRadius(2.px),
+ border(1.px, solid, C.gray.lighten(60).value),
+ fontSize(18.px),
+ &.hover(cursor.pointer)
+ )
+
+ val clear = style(
+ box,
+ backgroundColor(C.mickado.value),
+ color(C.white.value),
+ &.hover(backgroundColor(C.mickado.lighten(30).value))
+ )
+
+ val filter = style(
+ box,
+ &.hover(borderColor(C.gray.lighten(80).value))
+ )
+
+ val name = style(
+ marginRight(10.px)
+ )
+
+ val cross = style(
+ width(15.px),
+ height(15.px)
+ )
+}
diff --git a/src/main/scala/reading/component/index/style/Header.scala b/src/main/scala/reading/component/index/style/Header.scala
index 2eb6eb2..10ce059 100644
--- a/src/main/scala/reading/component/index/style/Header.scala
+++ b/src/main/scala/reading/component/index/style/Header.scala
@@ -3,73 +3,20 @@ package reading.component.index.style
import scalacss.Defaults._
import reading.Media
-import reading.component.style.{ Color => C, Button, Count }
+import reading.component.style.{ Color => C }
object Header extends StyleSheet.Inline {
import dsl._
val header = style(
- Media.desktop(margin(40.px)),
- Media.mobile(margin(30.px))
- )
-
- val showFiltersMenu = style(
- Media.desktop(display.none),
- Media.mobile(
- Button.simple,
- marginBottom(20.px)
- )
- )
-
- val filtersCount = style(
- Count.major,
- marginLeft(20.px)
- )
-
- val filters = style(
- display.flex,
- flexWrap.wrap,
- marginBottom(15.px),
- Media.mobile(display.none)
- )
-
- private val box = style(
- display.flex,
- alignItems.center,
- padding(15.px),
- marginRight(20.px),
- marginBottom(15.px),
- borderRadius(2.px),
- border(1.px, solid, C.gray.lighten(60).value),
- fontSize(18.px),
- &.hover(cursor.pointer)
- )
-
- val clear = style(
- box,
- backgroundColor(C.mickado.value),
- color(C.white.value),
- &.hover(backgroundColor(C.mickado.lighten(30).value))
- )
-
- val filter = style(
- box,
- &.hover(borderColor(C.gray.lighten(80).value))
- )
-
- val name = style(
- marginRight(10.px)
- )
-
- val cross = style(
- width(15.px),
- height(15.px)
+ margin(30.px, 0.px)
)
val searchAndCount = style(
display.flex,
flexWrap.wrap,
alignItems.center,
+ Media.desktop(paddingLeft(40.px)),
Media.mobile(justifyContent.center)
)
@@ -78,7 +25,7 @@ object Header extends StyleSheet.Inline {
Media.desktop(marginRight(30.px))
)
- val booksCount = style(
+ val count = style(
fontSize(20.px),
color(C.gray.value),
Media.mobile(textAlign.center)
diff --git a/src/main/scala/reading/component/index/style/Menu.scala b/src/main/scala/reading/component/index/style/Menu.scala
index dd74039..09f529c 100644
--- a/src/main/scala/reading/component/index/style/Menu.scala
+++ b/src/main/scala/reading/component/index/style/Menu.scala
@@ -9,28 +9,25 @@ object Menu extends StyleSheet.Inline {
import dsl._
val menu = style(
- Media.mobile(display.none),
+ height(100.%%),
+ zIndex(1),
Media.desktop(
+ backgroundColor(C.englishWalnut.value),
color(C.white.value),
position.relative,
width(280.px)
- )
- )
-
- val background = style(
- Media.desktop(
+ ),
+ Media.mobile(
+ backgroundColor(C.white.value),
+ color(C.black.value),
position.fixed,
- width(280.px),
- height(100.%%),
- backgroundColor(C.englishWalnut.value),
- boxShadow := "4px 0px 6px -1px rgba(0, 0, 0, 0.2)"
+ display.none,
+ width(100.%%)
)
)
- val content = style(
- position.relative,
- width(100.%%),
- height(100.%%)
+ val show = style(
+ Media.mobile(display.block)
)
val header = style(
@@ -44,7 +41,6 @@ object Menu extends StyleSheet.Inline {
textTransform.uppercase,
fontWeight.bold,
letterSpacing(1.px),
- Media.desktop(marginBottom(20.px)),
Media.mobile(boxShadow := "0px 3px 5px -1px rgba(0, 0, 0, 0.2)")
)
@@ -53,28 +49,13 @@ object Menu extends StyleSheet.Inline {
marginLeft(20.px)
)
- val show = style(
- Media.mobile(
- display.block,
- position.fixed,
- top(0.px),
- left(0.px),
- width(100.%%),
- height(100.%%),
- zIndex(1),
- overflowY.scroll,
- backgroundColor(C.white.value),
- color(C.black.value)
- )
- )
-
val empty = style()
val groups = style(
- Media.mobile(
- height :=! "calc(100% - 120px)",
- overflowY.auto
- )
+ overflowY.scroll,
+ paddingTop(20.px),
+ Media.desktop(height :=! "calc(100% - 60px)"),
+ Media.mobile(height :=! "calc(100% - 120px)")
)
val filterTitle = style(
@@ -155,9 +136,10 @@ object Commons extends StyleSheet.Inline {
val footerButton = style(
display.flex,
- width(50.%%),
+ flexGrow(1),
justifyContent.center,
alignItems.center,
+ height(50.px),
margin(5.px),
textTransform.uppercase,
fontSize(14.px),
diff --git a/src/main/scala/reading/component/style/Global.scala b/src/main/scala/reading/component/style/Global.scala
index 7501ec3..b7bf38c 100644
--- a/src/main/scala/reading/component/style/Global.scala
+++ b/src/main/scala/reading/component/style/Global.scala
@@ -14,8 +14,7 @@ object Global extends StyleSheet.Standalone {
"body" - (
position.absolute,
width(100.%%),
- height(100.%%),
- overflowY.hidden
+ height(100.%%)
)
"a" - (
diff --git a/src/main/scala/reading/component/style/Index.scala b/src/main/scala/reading/component/style/Index.scala
index e02ebd9..a2be884 100644
--- a/src/main/scala/reading/component/style/Index.scala
+++ b/src/main/scala/reading/component/style/Index.scala
@@ -9,12 +9,12 @@ object Index extends StyleSheet.Inline {
val page = style(
display.flex,
- overflowY.scroll,
height(100.%%)
)
val main = style(
Media.desktop(width :=! "calc(100% - 280px)"),
- Media.mobile(width(100.%%))
+ Media.mobile(width(100.%%)),
+ overflow.hidden
)
}
diff --git a/src/main/scala/reading/component/widget/Animate.scala b/src/main/scala/reading/component/widget/Animate.scala
index 0e848aa..6328177 100644
--- a/src/main/scala/reading/component/widget/Animate.scala
+++ b/src/main/scala/reading/component/widget/Animate.scala
@@ -14,16 +14,16 @@ object Animate {
transition: (Double, Double) => Double,
animate: (Double, HTMLElement) => Unit,
onEnd: => Unit = ()
- ): Unit = {
+ ): Unit =
animationFrames.get(id) match {
- case Some(animationFrame) => window.cancelAnimationFrame(animationFrame)
- case None => ()
+ case Some(animationFrame) =>
+ ()
+ case None =>
+ val animationFrame = window.requestAnimationFrame(ts =>
+ frame(id, ts, duration, transition, animate, onEnd)(ts))
+ animationFrames.put(id, animationFrame)
+ ()
}
- val animationFrame = window.requestAnimationFrame(ts =>
- frame(id, ts, duration, transition, animate, onEnd)(ts))
- animationFrames.put(id, animationFrame)
- ()
- }
private def frame(
id: String,
@@ -44,6 +44,7 @@ object Animate {
animationFrames.put(id, animationFrame)
} else {
animate(1, element)
+ animationFrames.remove(id)
onEnd
}
case _ =>
diff --git a/src/main/scala/reading/component/widget/AnimateMethod.scala b/src/main/scala/reading/component/widget/AnimateMethod.scala
new file mode 100644
index 0000000..dfe3e46
--- /dev/null
+++ b/src/main/scala/reading/component/widget/AnimateMethod.scala
@@ -0,0 +1,29 @@
+package reading.component.widget
+
+object AnimateMethod {
+ def fadeOut(id: String, onEnd: => Unit = ()): Unit =
+ Animate(
+ id = id,
+ duration = 100,
+ transition = Transition.linear,
+ animate =
+ (progress, element) => {
+ element.style.opacity = s"${1 - progress}"
+ element.style.transform = s"translateX(${20 * progress}px)"
+ },
+ onEnd = onEnd
+ )
+
+ def fadeIn(id: String, onEnd: => Unit = ()): Unit =
+ Animate(
+ id = id,
+ duration = 100,
+ transition = Transition.easeIn,
+ animate =
+ (progress, element) => {
+ element.style.opacity = s"${progress}"
+ element.style.transform = s"translateX(${20 * (1 - progress)}px)"
+ },
+ onEnd = onEnd
+ )
+}
diff --git a/src/main/scala/reading/component/widget/Input.scala b/src/main/scala/reading/component/widget/Input.scala
index 7dac47a..1a1157e 100644
--- a/src/main/scala/reading/component/widget/Input.scala
+++ b/src/main/scala/reading/component/widget/Input.scala
@@ -2,14 +2,15 @@ package reading.component.widget
import scalatags.JsDom.all._
-import org.scalajs.dom.KeyboardEvent
import org.scalajs.dom.html.Input
+import org.scalajs.dom.KeyboardEvent
import scalacss.Defaults._
import scalacss.ScalatagsCss._
import rx._
+import reading.component.style.{ Color => C }
import reading.component.widget.style.{ Input => InputStyle }
object Input {
@@ -17,28 +18,38 @@ object Input {
style: StyleA,
query: Var[String],
label: String = "",
- onEnter: => Unit = ()
+ onEnter: => Unit = (),
+ maxLength: Option[Int] = None
)(
implicit
ctx: Ctx.Owner
): Frag = {
val inputBox = input(
- InputStyle.render,
InputStyle.input,
- style,
placeholder := label,
onkeyup := { (e: KeyboardEvent) =>
val input = e.target.asInstanceOf[Input]
query() = input.value
input.value = input.value
if (e.keyCode == 13) onEnter
- }
+ },
+ maxlength := maxLength.map(_.toString).getOrElse("")
).render
query.trigger {
inputBox.value = query.now
}
- inputBox
+ div(
+ InputStyle.render,
+ InputStyle.parent,
+ style,
+ inputBox,
+ span(
+ InputStyle.clear,
+ onclick := (() => query() = ""),
+ Cross(15.px, C.gray.value)
+ )
+ )
}
}
diff --git a/src/main/scala/reading/component/widget/Modal.scala b/src/main/scala/reading/component/widget/Modal.scala
deleted file mode 100644
index db1f7e6..0000000
--- a/src/main/scala/reading/component/widget/Modal.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-package reading.component.widget
-
-import scala.util.Random
-
-import org.scalajs.dom.raw.HTMLElement
-import scalacss.Defaults._
-import scalacss.ScalatagsCss._
-import scalatags.JsDom.all._
-
-import reading.component.widget.style.{ Modal => ModalStyle }
-
-object Modal {
- def apply(onClose: => Unit)(content: Frag): Frag = {
- val modalId = s"modal${Random.nextInt}"
-
- Animate(
- id = modalId,
- duration = 250,
- transition = Transition.easeOut,
- animate =
- (progress, element) => {
- element.style.opacity = s"$progress"
- element.childNodes(2) match {
- case e: HTMLElement => e.style.transform = s"scale(${0.85 + 0.15 * progress})"
- }
- }
- )
-
- div(
- ModalStyle.render,
- ModalStyle.modal,
- id := modalId,
-
- div(
- ModalStyle.curtain,
- onclick := (() => close(modalId, onClose))
- ),
-
- div(
- ModalStyle.content,
- content,
- button(
- ModalStyle.close,
- onclick := (() => close(modalId, onClose)),
- "Fermer"
- )
- )
- )
- }
-
- private def close(modalId: String, onClose: => Unit): Unit =
- Animate(
- id = modalId,
- duration = 300,
- transition = Transition.linear,
- onEnd = onClose,
- animate = (progress, element) => element.style.opacity = s"${1 - progress}"
- )
-}
diff --git a/src/main/scala/reading/component/widget/style/Input.scala b/src/main/scala/reading/component/widget/style/Input.scala
index 967393b..9453640 100644
--- a/src/main/scala/reading/component/widget/style/Input.scala
+++ b/src/main/scala/reading/component/widget/style/Input.scala
@@ -7,10 +7,25 @@ import reading.component.style.{ Color => C }
object Input extends StyleSheet.Inline {
import dsl._
+ val parent = style(
+ position.relative
+ )
+
val input = style(
+ height(45.px),
border(1.px, solid, C.mickado.value),
borderRadius(2.px),
padding(10.px),
&.hover(borderColor(C.gray.value))
)
+
+ val clear = style(
+ position.absolute,
+ top(0.px),
+ right(10.px),
+ display.flex,
+ height(100.%%),
+ alignItems.center,
+ cursor.pointer
+ )
}
diff --git a/src/main/scala/reading/component/widget/style/Modal.scala b/src/main/scala/reading/component/widget/style/Modal.scala
deleted file mode 100644
index faf325d..0000000
--- a/src/main/scala/reading/component/widget/style/Modal.scala
+++ /dev/null
@@ -1,65 +0,0 @@
-package reading.component.widget.style
-
-import scalacss.Defaults._
-
-import reading.Media
-import reading.component.style.{ Color => C, Button }
-
-object Modal extends StyleSheet.Inline {
- import dsl._
-
- val modal = style(
- display.flex,
- justifyContent.center,
- position.fixed,
- width(100.%%),
- height(100.%%),
- top(0.px),
- right(0.px),
- bottom(0.px),
- left(0.px),
- opacity(0),
- overflowY.scroll
- )
-
- val curtain = style(
- Media.desktop(
- width(100.%%),
- height(100.%%),
- position.fixed,
- top(0.px),
- left(0.px),
- backgroundColor(C.black.value),
- opacity(0.7),
- cursor.pointer
- ),
- Media.mobile(
- display.none
- )
- )
-
- val content = style(
- position.relative,
- backgroundColor(C.white.value),
- margin.auto,
- Media.desktop(
- width(50.%%),
- borderRadius(5.px)
- ),
- Media.mobile(
- width(100.%%),
- height(100.%%),
- overflowY.auto
- ),
- padding(30.px, 30.px, 0.px, 30.px)
- )
-
- val close = style(
- Media.desktop(display.none),
- Media.mobile(
- Button.simple,
- marginTop(20.px),
- marginBottom(30.px)
- )
- )
-}