From 4e2339ddd1d95c07e7b54dee8565cd07e9e7dc34 Mon Sep 17 00:00:00 2001 From: Joris Date: Thu, 9 Feb 2023 10:03:11 +0100 Subject: Improve filtering on read status --- src/main.ts | 132 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/main.ts b/src/main.ts index 7033690..0a3fd30 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,6 @@ -import { h, withVar, mount } from 'lib/rx' +import { h, withVar, mount, Rx } from 'lib/rx' + +// Model interface Book { title: string @@ -6,43 +8,121 @@ interface Book { authorsSort: string genres: Array date: string - read: boolean + read: ReadStatus, cover: string } +type ReadStatus = 'Read' | 'Unread' | 'Reading' | 'Stopped' + +const readStatuses: Array = ['Read', 'Unread', 'Reading', 'Stopped' ] + +// Books + // @ts-ignore -const sortedBooks: Array = (books 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) -enum Filter { - All, - Read, - Unread +// Filters + +interface Filters { + read?: ReadStatus } -const view = withVar(Filter.All, (filter, updateFilter) => [ - h('aside', - { className: 'g-Aside' }, - h('select', - { name: 'filter', - id: 'filter', - onchange: (event: Event) => updateFilter(_ => (event.target as HTMLSelectElement).value as any) - }, - h('option', { value: Filter.All }, 'Tous les livres'), - h('option', { value: Filter.Read }, 'Livres lus'), - h('option', { value: Filter.Unread }, 'Livres non lus') +// View + +const view = withVar({}, (filters, updateFilters) => { + const filtredBooks = filters.map(f => + sortedBookLibrary.filter(book => + f.read === undefined || book.read === f.read ) - ), - h('main', - { className: 'g-Main' }, - filter.map(f => - sortedBooks - .filter(b => f == Filter.All || b.read == (f == Filter.Read)) - .map(book => h('img', { className: 'g-Book', src: book.cover })) + ) + + 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 })) + ) + ) ) + ] +}) + +// 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) + ) + ) + } 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' + } +} mount(view) -- cgit v1.2.3