aboutsummaryrefslogtreecommitdiff
path: root/library/client/view
diff options
context:
space:
mode:
authorJoris Guyonvarch2025-12-26 18:41:26 +0100
committerJoris Guyonvarch2025-12-27 20:41:44 +0100
commita110c200e86d2325af07167531fac0f61d9681a0 (patch)
tree90e843f915a2e153ba735849afd83710d90560bf /library/client/view
parenta26d92ad5055fa057647158eb79511e7b1841162 (diff)
Switch to GUI to manage the library
Allow to regroup the CLI and the view into one unique tool.
Diffstat (limited to 'library/client/view')
-rw-r--r--library/client/view/books.ts116
-rw-r--r--library/client/view/components/modal.ts38
-rw-r--r--library/client/view/filters.ts90
3 files changed, 0 insertions, 244 deletions
diff --git a/library/client/view/books.ts b/library/client/view/books.ts
deleted file mode 100644
index aba55c1..0000000
--- a/library/client/view/books.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-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) => [
- books.map(bs => [
- focusBook.map(book => {
- if (book !== undefined) {
- let onKeyup = keyupHandler({
- books: bs,
- book,
- onUpdate: (book: Book.Book) => updateFocusBook(_ => book)
- })
-
- return bookDetailModal({
- book,
- onClose: () => updateFocusBook(_ => undefined),
- onmount: () => addEventListener('keyup', onKeyup),
- onunmount: () => removeEventListener('keyup', onKeyup)
- })
- }
- }),
- bs.map(book => viewBook({
- book,
- onSelect: (book) => updateFocusBook(_ => book)
- }))
- ])
- ])
- )
-}
-
-interface KeyupHandlerParams {
- books: Array<Book.Book>
- book: Book.Book
- onUpdate: (book: Book.Book) => void
-}
-
-function keyupHandler({ books, book, onUpdate }: KeyupHandlerParams): ((e: KeyboardEvent) => void) {
- return (e: KeyboardEvent) => {
- if (e.key === 'ArrowLeft') {
- const indexedBooks = books.map((b, i) => ({ b, i }))
- const focus = indexedBooks.find(({ b }) => b == book)
- if (focus !== undefined && focus.i > 0) {
- onUpdate(books[focus.i - 1])
- }
- } else if (e.key === 'ArrowRight') {
- const indexedBooks = books.map((b, i) => ({ b, i }))
- const focus = indexedBooks.find(({ b }) => b == book)
- if (focus !== undefined && focus.i < books.length - 1) {
- onUpdate(books[focus.i + 1])
- }
- }
- }
-}
-
-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,
- alt: book.title,
- className: 'g-Book__Image',
- onclick: () => onSelect(book)
- }
- )
- )
-}
-
-interface BookDetailModalParams {
- book: Book.Book
- onClose: () => void
- onmount: () => void
- onunmount: () => void
-}
-
-function bookDetailModal({ book, onClose, onmount, onunmount }: BookDetailModalParams): Html {
- return Modal.view({
- header: h('div',
- h('div', { className: 'g-BookDetail__Title' }, `${book.title}, ${book.date}`),
- book.subtitle && h('div', { className: 'g-BookDetail__Subtitle' }, book.subtitle)
- ),
- body: h('div',
- { className: 'g-BookDetail' },
- h('img', { src: book.cover }),
- h('div',
- h('dl',
- metadata('Auteur', book.authors),
- metadata('Genre', book.genres)
- ),
- book.summary && book.summary
- .split('\n')
- .map(str => str.trim())
- .filter(str => str != '')
- .map(str => h('p', str))
- )
- ),
- onClose,
- onmount,
- onunmount
- })
-}
-
-function metadata(term: string, descriptions: Array<string>): Html {
- return h('div',
- h('dt', term, descriptions.length > 1 && 's', ' :'),
- h('dd', ' ', descriptions.join(', '))
- )
-}
diff --git a/library/client/view/components/modal.ts b/library/client/view/components/modal.ts
deleted file mode 100644
index 5e845e1..0000000
--- a/library/client/view/components/modal.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { h, Html } from 'lib/rx'
-
-interface Params {
- header: Html
- body: Html
- onClose: () => void
- onmount?: (element: Element) => void
- onunmount?: (element: Element) => void
-}
-
-export function view({ header, body, onClose, onmount, onunmount }: Params): Html {
- return h('div',
- { className: 'g-Modal',
- onclick: () => onClose(),
- onmount: (element: Element) => onmount && onmount(element),
- onunmount: (element: Element) => onunmount && onunmount(element)
- },
- h('div',
- { className: 'g-Modal__Content',
- onclick: (e: Event) => e.stopPropagation()
- },
- h('div',
- { className: 'g-Modal__Header' },
- header,
- h('button',
- { className: 'g-Modal__Close',
- onclick: () => onClose()
- },
- '✕'
- )
- ),
- h('div',
- { className: 'g-Modal__Body' },
- body
- )
- )
- )
-}
diff --git a/library/client/view/filters.ts b/library/client/view/filters.ts
deleted file mode 100644
index efe4115..0000000
--- a/library/client/view/filters.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import { h, Rx, Html } from 'lib/rx'
-import * as Book from 'book'
-import * as I18n from 'lib/i18n'
-
-// Model
-
-export interface Model {
- read?: Book.ReadStatus
-}
-
-const init: Model = {}
-
-// View
-
-interface ViewFiltersParams {
- filteredBooks: Rx<Array<Book.Book>>
- filters: Rx<Model>
- updateFilters: (f: (filters: Model) => Model) => void
-}
-
-export function view({ filteredBooks, filters, updateFilters }: ViewFiltersParams): Html {
- return h('ul',
- h('li', [
- h('div', { className: 'g-FilterTitle' }, 'Lecture'),
- readFilter({
- filteredBooks,
- readStatus: filters.map(fs => fs.read),
- update: (status?: Book.ReadStatus) => updateFilters(fs => {
- fs.read = status
- return fs
- })
- })
- ])
- )
-}
-
-interface ReadFilterParams {
- filteredBooks: Rx<Array<Book.Book>>
- readStatus: Rx<Book.ReadStatus | undefined>
- update: (status?: Book.ReadStatus) => void
-}
-
-function readFilter({ filteredBooks, readStatus, update }: ReadFilterParams): Html {
- return h('ul',
- { className: 'g-Filters' },
- readStatus.map(currentStatus => {
- if (currentStatus !== undefined) {
- return h('li',
- { className: 'g-Filter g-Filter--Selected' },
- h('button',
- { onclick: () => update(undefined) },
- filteredBooks.map(xs => unit(xs.length, readStatusLabels(currentStatus)))
- )
- )
- } else {
- return Book.readStatuses.map(status =>
- filteredBooks.map(xs => {
- const count = xs.filter(b => b.read === status).length
-
- return count !== 0
- ? h('li',
- { className: 'g-Filter g-Filter--Unselected' },
- h('button',
- { onclick: () => update(status) },
- unit(count, readStatusLabels(status))
- )
- )
- : undefined
- })
- )
- }
- })
- )
-}
-
-function unit(n: number, labels: Array<string>): string {
- return I18n.unit(n, labels[0], labels[1], (n, str) => `${str} (${n})`)
-}
-
-function readStatusLabels(status: Book.ReadStatus): Array<string> {
- if (status === 'Read') {
- return ['lu', 'lus']
- } else if (status === 'Unread') {
- return ['non lu', 'non lus']
- } else if (status === 'Reading') {
- return ['en cours', 'en cours']
- } else {
- return ['arrêté', 'arrêtés']
- }
-}