From 25ecb1cb75b7b5b584ad223e12d74c3e3ee7da89 Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 12 Feb 2023 13:05:25 +0100 Subject: Add textual search on title, subtitle and authors --- src/main.ts | 156 +++++++++++++++--------------------------------------------- 1 file changed, 39 insertions(+), 117 deletions(-) (limited to 'src/main.ts') diff --git a/src/main.ts b/src/main.ts index 0a3fd30..21bf215 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,128 +1,50 @@ -import { h, withVar, mount, Rx } from 'lib/rx' - -// Model - -interface Book { - title: string - authors: Array - authorsSort: string - genres: Array - date: string - read: ReadStatus, - cover: string -} - -type ReadStatus = 'Read' | 'Unread' | 'Reading' | 'Stopped' - -const readStatuses: Array = ['Read', 'Unread', 'Reading', 'Stopped' ] - -// Books +import { h, withVar, mount } 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 Book from 'book' // @ts-ignore -const sortedBookLibrary: Array = (bookLibrary as Array).sort((a, b) => +const sortedBookLibrary: Array = (bookLibrary as Array).sort((a, b) => a.authorsSort == b.authorsSort ? a.date > b.date : a.authorsSort > b.authorsSort) -// Filters - -interface Filters { - read?: ReadStatus -} - -// View - -const view = withVar({}, (filters, updateFilters) => { - const filtredBooks = filters.map(f => - sortedBookLibrary.filter(book => - f.read === undefined || book.read === f.read - ) - ) - - return [ - h('aside', viewFilters({ filtredBooks, filters, updateFilters })), - h('main', - h('header', filtredBooks.map(fb => `${fb.length} livres`)), - h('div', - { className: 'g-Books' }, - filtredBooks.map(fb => - fb.map(book => h('img', { className: 'g-Book', src: book.cover })) - ) +mount(withVar({}, (filters, updateFilters) => + withVar('', (search, updateSearch) => { + const filteredBooks = filters.flatMap(f => search.map(s => + sortedBookLibrary.filter(book => + (f.read === undefined || book.read === f.read) + && (s === '' || matchSearch(book, s)) ) - ) - ] -}) - -// Filters view - -interface ViewFiltersParams { - filtredBooks: Rx> - filters: Rx - updateFilters: (f: (filters: Filters) => Filters) => void -} - -function viewFilters({ filtredBooks, filters, updateFilters }: ViewFiltersParams) { - return h('ul', - {}, - h('li', [ - h('div', { className: 'g-FilterTitle' }, 'Lecture'), - readFilter({ - filtredBooks, - readStatus: filters.map(fs => fs.read), - update: (status?: ReadStatus) => updateFilters(fs => { - fs.read = status - return fs - }) - }) - ]) - ) -} - -interface ReadFilterParams { - filtredBooks: Rx> - readStatus: Rx - update: (status?: ReadStatus) => void -} - -function readFilter({ filtredBooks, readStatus, update }: ReadFilterParams) { - 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) }, - readStatusLabel(currentStatus) + )) + + return [ + h('aside', Filters.view({ filteredBooks, filters, updateFilters })), + h('main', + h('header', + h('input', + { className: 'g-Search', + oninput: Functions.debounce( + (event: Event) => updateSearch(_ => (event.target as HTMLInputElement).value), + 500 + ) + } + ), + 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 })) ) ) - } else { - return readStatuses.map(status => { - const count = filtredBooks.map(xs => xs.filter(b => b.read === status).length) - - return h('li', - { className: 'g-Filter g-Filter--Unselected' }, - h('button', - { onclick: () => update(status) }, - count.map(c => `${readStatusLabel(status)} (${c})`) - ) - ) - }) - } - }) - ) -} + ) + ] + }) +)) -function readStatusLabel(status: ReadStatus): string { - if (status === 'Read') { - return 'lus' - } else if (status === 'Unread') { - return 'non lus' - } else if (status === 'Reading') { - return 'en cours' - } else { - return 'arrêtés' - } +function matchSearch(book: Book.Book, search: string): boolean { + return Search.match(search, book.title, book.subtitle, ...book.authors) } - -mount(view) -- cgit v1.2.3