mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Cover grid view for the new server
This commit is contained in:
parent
eae610e465
commit
12d1d5c8c2
@ -573,6 +573,7 @@ def interface_data(ctx, rd, library_id):
|
|||||||
session = rd.session
|
session = rd.session
|
||||||
ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
|
ans = {'session_data': {k:session[k] for k in defaults.iterkeys()}}
|
||||||
ans['library_map'], ans['default_library'] = ctx.library_map
|
ans['library_map'], ans['default_library'] = ctx.library_map
|
||||||
|
ans['library_id'] = library_id or ans['default_library']
|
||||||
sorts, orders = [], []
|
sorts, orders = [], []
|
||||||
for x in ans['session_data']['sort'].split(','):
|
for x in ans['session_data']['sort'].split(','):
|
||||||
s, o = x.partition(':')[::2]
|
s, o = x.partition(':')[::2]
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
from book_list.theme import get_color
|
from book_list.theme import get_color
|
||||||
from book_list.top_bar import TopBar
|
from book_list.top_bar import TopBar
|
||||||
|
from book_list.views import BooksView
|
||||||
from dom import set_css
|
from dom import set_css
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
from utils import debounce
|
||||||
|
|
||||||
class BarState:
|
class BarState:
|
||||||
|
|
||||||
@ -27,4 +29,9 @@ class BookList:
|
|||||||
self.states.append(ibs)
|
self.states.append(ibs)
|
||||||
self.top_bar = TopBar()
|
self.top_bar = TopBar()
|
||||||
self.top_bar.apply_state(ibs.left_state, ibs.buttons)
|
self.top_bar.apply_state(ibs.left_state, ibs.buttons)
|
||||||
|
self.books_view = BooksView(interface_data)
|
||||||
ibs.left_state.run_animation = False
|
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
102
src/pyj/book_list/views.pyj
Normal 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
22
src/pyj/utils.pyj
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user