diff options
-rw-r--r-- | public/main.css | 107 | ||||
-rw-r--r-- | src/book.ts | 2 | ||||
-rw-r--r-- | src/main.ts | 10 | ||||
-rw-r--r-- | src/view/books.ts | 61 | ||||
-rw-r--r-- | src/view/components/modal.ts | 26 |
5 files changed, 196 insertions, 10 deletions
diff --git a/public/main.css b/public/main.css index 09c344f..e5d3bcf 100644 --- a/public/main.css +++ b/public/main.css @@ -1,3 +1,19 @@ +/* Variables */ + +:root { + --color-focus: #888833; + + --width-close-button: 4rem; + + --font-size-dog: 1.5rem; + + --spacing-beetle: 0.25rem; + --spacing-mouse: 0.5rem; + --spacing-cat: 1rem; +} + +/* Top level */ + html { height: 100%; } @@ -9,6 +25,51 @@ body { font-family: sans-serif; } +/* Modal */ + +.g-Modal { + width: 100vw; + height: 100vh; + position: fixed; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.4); + z-index: 1; +} + +.g-Modal__Close { + position: absolute; + top: 2rem; + right: 2rem; + cursor: pointer; + font-weight: bold; + border-radius: 50%; + border: 1px solid #EEE; + background-color: transparent; + width: var(--width-close-button); + height: var(--width-close-button); + font-size: 2rem; + font-family: monospace; +} + +.g-Modal__Close:hover, .g-Modal__Close:focus { + background-color: #EEE; +} + +.g-Modal__Content { + position: relative; + background-color: white; + width: 50%; + height: 50%; + border-radius: 0.2rem; + border: 1px solid #EEE; + padding: 2rem; + padding-right: calc(2rem + var(--width-close-button) + 2rem); +} + /* Filters */ aside { @@ -50,7 +111,7 @@ ul { } .g-Filter button:hover { - background-color: #888833; + background-color: var(--color-focus); } /* Books */ @@ -78,7 +139,49 @@ header { } .g-Book { - width: 13rem; align-self: center; border: 1px solid #DDDDDD; + padding: 0; + width: fit-content; +} + +.g-Book:hover, .g-Book:focus { + cursor: pointer; + outline: none; +} + +.g-Book:hover .g-Book__Image, .g-Book:focus .g-Book__Image { + transform: scale(105%); + opacity: 0.9; +} + +.g-Book__Image { + display: flex; + width: 13rem; + transition: all 0.2s ease-in-out; +} + +/* Book detail */ + +.g-BookDetail { + display: flex; + justify-content: space-between; +} + +.g-BookDetail img { + width: 10rem; +} + +.g-BookDetail__Title { + font-size: var(--font-size-dog); + margin-bottom: var(--spacing-beetle); +} + +.g-BookDetail__Subtitle { + font-style: italic; + margin-bottom: var(--spacing-beetle); +} + +.g-BookDetail__Genres { + margin-top: var(--spacing-cat); } diff --git a/src/book.ts b/src/book.ts index a5ddb75..328f8e1 100644 --- a/src/book.ts +++ b/src/book.ts @@ -1,6 +1,6 @@ export interface Book { title: string - subtitle: string + subtitle?: string authors: Array<string> authorsSort: string genres: Array<string> diff --git a/src/main.ts b/src/main.ts index 21bf215..5885871 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,9 @@ -import { h, withVar, mount } from 'lib/rx' +import { h, withVar, mount, Html } from 'lib/rx' import * as Search from 'lib/search' import * as Functions from 'lib/functions' import * as I18n from 'lib/i18n' import * as Filters from 'view/filters' +import * as Books from 'view/books' import * as Book from 'book' // @ts-ignore @@ -34,12 +35,7 @@ mount(withVar<Filters.Model>({}, (filters, updateFilters) => ), filteredBooks.map(fb => I18n.unit(fb.length, 'livre', 'livres')) ), - h('div', - { className: 'g-Books' }, - filteredBooks.map(fb => - fb.map(book => h('img', { className: 'g-Book', src: book.cover })) - ) - ) + Books.view(filteredBooks) ) ] }) diff --git a/src/view/books.ts b/src/view/books.ts new file mode 100644 index 0000000..6ea0c12 --- /dev/null +++ b/src/view/books.ts @@ -0,0 +1,61 @@ +import { h, withVar, mount, Html, Rx } from 'lib/rx' +import * as Book from 'book' +import * as Modal from 'view/components/modal' + +export function view(books: Rx<Array<Book.Book>>): Html { + return h('div', + { className: 'g-Books' }, + withVar<Book.Book | undefined>(undefined, (focusBook, updateFocusBook) => [ + focusBook.map(book => book && bookDetailModal({ + book, + onClose: () => updateFocusBook(_ => undefined) + })), + books.map(bs => bs.map(book => viewBook({ + book, + onSelect: (book) => updateFocusBook(_ => book) + }))) + ]) + ) +} + +interface ViewBookParams { + book: Book.Book + onSelect: (book: Book.Book) => void +} + +function viewBook({ book, onSelect }: ViewBookParams): Html { + return h('button', + { className: 'g-Book' }, + h('img', + { src: book.cover, + className: 'g-Book__Image', + onclick: () => onSelect(book) + } + ) + ) +} + +interface BookDetailModalParams { + book: Book.Book + onClose: () => void +} + +function bookDetailModal({ book, onClose }: BookDetailModalParams): Html { + return Modal.view({ + content: h('div', + { className: 'g-BookDetail' }, + h('div', + h('div', { className: 'g-BookDetail__Title' }, `${book.title}, ${book.date}`), + book.subtitle && h('div', { className: 'g-BookDetail__Subtitle' }, book.subtitle), + book.authors.join(', '), + h('div', + { className: 'g-BookDetail__Genres' }, + book.genres.length > 1 ? 'Genres : ' : 'Genre : ', + book.genres.join(', ') + ) + ), + h('img', { src: book.cover }) + ), + onClose + }) +} diff --git a/src/view/components/modal.ts b/src/view/components/modal.ts new file mode 100644 index 0000000..fe08272 --- /dev/null +++ b/src/view/components/modal.ts @@ -0,0 +1,26 @@ +import { h, Html } from 'lib/rx' + +interface Params { + content: Html, + onClose: () => void +} + +export function view({ content, onClose }: Params): Html { + return h('div', + { className: 'g-Modal', + onclick: () => onClose() + }, + h('div', + { className: 'g-Modal__Content', + onclick: (e: Event) => e.stopPropagation() + }, + h('button', + { className: 'g-Modal__Close', + onclick: () => onClose() + }, + '✖' + ), + content + ) + ) +} |