Cover grid view for the new server

This commit is contained in:
Kovid Goyal 2015-10-23 09:22:28 +05:30
parent eae610e465
commit 12d1d5c8c2
4 changed files with 132 additions and 0 deletions

View File

@ -573,6 +573,7 @@ def interface_data(ctx, rd, library_id):
session = rd.session
ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
ans['library_map'], ans['default_library'] = ctx.library_map
ans['library_id'] = library_id or ans['default_library']
sorts, orders = [], []
for x in ans['session_data']['sort'].split(','):
s, o = x.partition(':')[::2]

View File

@ -3,8 +3,10 @@
from book_list.theme import get_color
from book_list.top_bar import TopBar
from book_list.views import BooksView
from dom import set_css
from gettext import gettext as _
from utils import debounce
class BarState:
@ -27,4 +29,9 @@ class BookList:
self.states.append(ibs)
self.top_bar = TopBar()
self.top_bar.apply_state(ibs.left_state, ibs.buttons)
self.books_view = BooksView(interface_data)
ibs.left_state.run_animation = False
window.addEventListener('resize', debounce(bind(self.on_resize, self), 250))
def on_resize(self):
self.books_view.on_resize()

102
src/pyj/book_list/views.pyj Normal file
View File

@ -0,0 +1,102 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from dom import set_css
from elementmaker import E
bv_counter = 0
THUMBNAIL_MAX_WIDTH = 300
THUMBNAIL_MAX_HEIGHT = 400
class BooksView:
def __init__(self, interface_data):
nonlocal bv_counter
bv_counter += 1
self.interface_data = interface_data
self.search_result = interface_data['search_result']
self.metadata_map = interface_data['metadata']
self.container_id = 'books-view-' + bv_counter
div = E.div(
id=self.container_id, style='display:none',
E.style(),
E.div(),
E.div(id='get-more-books')
)
document.body.appendChild(div)
self.render_book = bind(self.cover_grid_item, self)
self.init_grid = bind(self.init_cover_grid, self)
self.resize_grid = bind(self.resize_cover_grid, self)
self.mode = 'cover_grid'
self.init_grid()
self.render_ids()
self.resize_grid()
self.is_visible = True
@property
def container(self):
return document.getElementById(self.container_id)
@property
def grid(self):
return self.container.lastChild.previousSibling
@property
def is_visible(self):
self.container.style.display == 'block'
@is_visible.setter
def is_visible(self, val):
self.container.style.display = 'block' if val else 'none'
def clear(self):
# We replace the div entirely so that any styles associated with it are
# also removed
c = self.container
c.removeChild(self.grid)
c.insertBefore(E.div(), c.lastChild)
self.init_grid()
def init_cover_grid(self):
div = self.grid
set_css(div, display='flex', flex_wrap='wrap', justify_content='flex-start', align_items='flex-end', align_content='flex-start')
self.cover_grid_width = self.cover_grid_height = -1
def resize_cover_grid(self):
w, h = window.innerWidth, window.innerHeight
if w <= h:
# Portrait
MAX_WIDTH, MIN_WIDTH = THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_WIDTH // 2
no_wider_than = (w - 50) // 2
width = min(no_wider_than, MAX_WIDTH)
width = max(width, MIN_WIDTH)
height = int((4 / 3) * width)
else:
# Landscape
MAX_HEIGHT, MIN_HEIGHT = THUMBNAIL_MAX_HEIGHT, THUMBNAIL_MAX_HEIGHT // 2
no_taller_than = (h - 75) // 2
height = min(no_taller_than, MAX_HEIGHT)
height = max(height, MIN_HEIGHT)
width = int((3 / 4) * height)
width, height = width + 'px', height + 'px'
for child in self.grid.childNodes:
set_css(child, width=width, height=height)
def cover_grid_item(self, book_id):
cover_url = str.format('get/thumb/{}/{}?sz={}x{}', book_id, self.interface_data['library_id'], THUMBNAIL_MAX_WIDTH, THUMBNAIL_MAX_HEIGHT)
return E.div(
style='margin: 10px; display: flex; align-content: flex-end; align-items: flex-end; justify-content: space-around',
data_book_id=str(book_id),
E.img(src=cover_url, style='max-width: 100%; max-height: 100%; display: block; cursor: pointer; width:auto; height:auto')
)
def render_ids(self, book_ids=None):
if book_ids is None:
book_ids = self.search_result['book_ids']
div = self.grid
for book_id in book_ids:
div.appendChild(self.render_book(book_id))
def on_resize(self):
if self.resize_grid:
self.resize_grid()

22
src/pyj/utils.pyj Normal file
View File

@ -0,0 +1,22 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
def debounce(func, wait, immediate=False):
# Returns a function, that, as long as it continues to be invoked, will not
# be triggered. The function will be called after it stops being called for
# N milliseconds. If `immediate` is True, trigger the function on the
# leading edge, instead of the trailing.
timeout = None
return def debounce_inner(): # noqa: unused-local
nonlocal timeout
context, args = this, arguments
def later():
nonlocal timeout
timeout = None
if not immediate:
func.apply(context, args)
call_now = immediate and not timeout
window.clearTimeout(timeout)
timeout = window.setTimeout(later, wait)
if call_now:
func.apply(context, args)