mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-01-06 04:00:20 -05:00
209 lines
7.6 KiB
Plaintext
209 lines
7.6 KiB
Plaintext
# vim:fileencoding=utf-8
|
|
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
|
from __python__ import hash_literals
|
|
|
|
from dom import build_rule, clear, svgicon, create_keyframes, set_css, change_icon_image, add_extra_css, ensure_id
|
|
from elementmaker import E
|
|
|
|
from book_list.theme import get_color
|
|
|
|
# Button {{{
|
|
|
|
def create_button(text, icon=None, action=None, tooltip=None, highlight=False):
|
|
ic = ''
|
|
if icon:
|
|
ic = svgicon(icon)
|
|
text = '\xa0' + text
|
|
ans = E.a(ic, E.span(text), class_='calibre-push-button', href='javascript: void(0)', title=tooltip or '')
|
|
if action is not None:
|
|
ans.addEventListener('click', def(event): event.preventDefault(), action(event);)
|
|
if highlight:
|
|
set_css(ans, font_size='larger', font_weight='bold')
|
|
return ans
|
|
|
|
create_button.style = build_rule('a.calibre-push-button',
|
|
border_radius='1em', background_clip='padding-box', background_color=get_color('button-start'),
|
|
background_image='linear-gradient(to bottom, {}, {})'.format(get_color('button-start'), get_color('button-end')),
|
|
padding='0.5ex 1em', color=get_color('button-text'), cursor='pointer', font_size='inherit', display='inline-flex', align_items='center',
|
|
box_shadow='0px 2px 1px rgba(50, 50, 50, 0.75)',
|
|
)
|
|
create_button.style += build_rule('a.calibre-push-button:hover', transform='scale(1.2)')
|
|
create_button.style += build_rule('a.calibre-push-button:active', transform='scale(2)')
|
|
# }}}
|
|
|
|
# Spinner {{{
|
|
def create_spinner(height, width):
|
|
ans = svgicon('cog', height, width)
|
|
ans.classList.add('spin')
|
|
return ans
|
|
|
|
create_spinner.style = build_rule('.spin', animation='spin 2s infinite linear')
|
|
create_spinner.style += create_keyframes('spin', 'from { transform: rotate(0deg); } to { transform: rotate(359deg);}')
|
|
|
|
# }}}
|
|
|
|
# Breadcrumbs {{{
|
|
|
|
class Breadcrumbs:
|
|
|
|
STYLE_RULES = build_rule(
|
|
'.calibre-breadcrumbs',
|
|
user_select='none', white_space='nowrap', background_color=get_color('window-background2'),
|
|
z_index='-1', border_radius='10px', margin='1ex 1em', margin_bottom='0'
|
|
)
|
|
|
|
STYLE_RULES += build_rule(
|
|
'.calibre-breadcrumbs > li',
|
|
cursor='pointer', display='inline-block', line_height='26px', margin='0 9px 0 -10px',
|
|
padding='0.5ex 1rem', position='relative'
|
|
)
|
|
|
|
STYLE_RULES += build_rule('.calibre-breadcrumbs > li:hover', color='red')
|
|
|
|
STYLE_RULES += build_rule('.calibre-breadcrumbs > li:active', color='red', transform='scale(1.5)')
|
|
|
|
STYLE_RULES += build_rule(
|
|
'.calibre-breadcrumbs > li:before, .calibre-breadcrumbs > li:after',
|
|
border_right='2px solid currentColor', content='""', display='block', height='50%',
|
|
position='absolute', left='0', top='0', right='0', transform='skewX(45deg)'
|
|
)
|
|
|
|
STYLE_RULES += build_rule(
|
|
'.calibre-breadcrumbs > li:after',
|
|
bottom='0', top='auto', transform='skewX(-45deg)'
|
|
)
|
|
|
|
STYLE_RULES += build_rule(
|
|
'.calibre-breadcrumbs > li:last-of-type:before, .calibre-breadcrumbs > li:last-of-type:after',
|
|
display='none')
|
|
|
|
|
|
def __init__(self, container):
|
|
self.container_id = ensure_id(container, 'calibre-breadcrumbs-')
|
|
container.classList.add('calibre-breadcrumbs')
|
|
clear(container)
|
|
|
|
@property
|
|
def container(self):
|
|
return document.getElementById(self.container_id)
|
|
|
|
def reset(self):
|
|
clear(self.container)
|
|
|
|
def add_crumb(self, callback):
|
|
li = E.li()
|
|
if callback:
|
|
li.addEventListener('click', callback)
|
|
self.container.appendChild(li)
|
|
return li
|
|
# }}}
|
|
|
|
# Simple Tree {{{
|
|
|
|
def create_tree(root, populate_data, onclick):
|
|
container = E.div(class_='simple-tree')
|
|
set_css(container, overflow='auto')
|
|
|
|
def toggle_node(li):
|
|
if li.dataset.treeState is 'closed':
|
|
li.dataset.treeState = 'open'
|
|
li.lastChild.style.display = 'block'
|
|
change_icon_image(li.firstChild.firstChild, 'caret-down')
|
|
else:
|
|
li.dataset.treeState = 'closed'
|
|
li.lastChild.style.display = 'none'
|
|
change_icon_image(li.firstChild.firstChild, 'caret-right')
|
|
|
|
def process_node(node, parent_container, level):
|
|
if node.children?.length:
|
|
ul = E.div()
|
|
parent_container.appendChild(ul)
|
|
for child in node.children:
|
|
icon = 'caret-right' if child.children?.length else None
|
|
li = E.div(style='display:flex; flex-direction:column; margin: 1ex 1em; margin-left: {}em'.format(level+1),
|
|
E.div(style='display:flex; align-items: center',
|
|
svgicon(icon),
|
|
E.span('\xa0'),
|
|
E.a(
|
|
href='javascript: void(0)',
|
|
class_='simple-link tree-item-title',
|
|
onclick=def (event):
|
|
if onclick:
|
|
if event.button is 0:
|
|
event.preventDefault(), event.stopPropagation()
|
|
onclick(event, event.currentTarget.parentNode.parentNode)
|
|
),
|
|
),
|
|
E.div(style='display:none', data_tree_subtree_container='1'),
|
|
data_tree_state='closed',
|
|
)
|
|
ul.appendChild(li)
|
|
populate_data(child, li, li.firstChild.lastChild)
|
|
if icon:
|
|
set_css(li.firstChild.firstChild, cursor='pointer')
|
|
li.firstChild.firstChild.addEventListener('click', def(event):
|
|
toggle_node(event.currentTarget.parentNode.parentNode)
|
|
)
|
|
process_node(child, li.lastChild, level + 1)
|
|
|
|
if root:
|
|
process_node(root, container, 0)
|
|
return container
|
|
|
|
def find_text_in_tree(container, q):
|
|
q = q.lower()
|
|
last_match = container.querySelector('a[data-tree-last-match]')
|
|
if last_match:
|
|
last_match.parentNode.style.backgroundColor = 'transparent'
|
|
last_match.parentNode.style.borderRadius = '0'
|
|
lm = last_match.getAttribute('data-tree-last-match')
|
|
last_match.removeAttribute('data-tree-last-match')
|
|
if lm is not q:
|
|
last_match = None
|
|
if not q:
|
|
return
|
|
before = []
|
|
seen = False
|
|
ans = None
|
|
|
|
for a in container.querySelectorAll('a.tree-item-title'):
|
|
if a is last_match:
|
|
seen = True
|
|
else:
|
|
if a.textContent.lower().indexOf(q) != -1:
|
|
if seen:
|
|
ans = a
|
|
break
|
|
if last_match is None:
|
|
ans = a
|
|
break
|
|
before.push(a)
|
|
if not ans and before.length:
|
|
ans = before[0]
|
|
ans = ans or last_match
|
|
if ans:
|
|
ans.dataset.treeLastMatch = q
|
|
if ans:
|
|
ans.parentNode.style.backgroundColor = get_color('tree-highlight-item')
|
|
ans.parentNode.style.borderRadius = '5px'
|
|
ans = ans.parentNode.parentNode
|
|
return ans
|
|
|
|
def scroll_tree_item_into_view(item):
|
|
p = item.parentNode?.parentNode
|
|
while p and p.getAttribute('data-tree-subtree-container'):
|
|
p.style.display = 'block'
|
|
p = p.parentNode?.parentNode?.parentNode
|
|
item.scrollIntoView()
|
|
|
|
# }}}
|
|
|
|
add_extra_css(def():
|
|
ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n'
|
|
ans += '.simple-link { cursor: pointer } .simple-link:hover { color: red } .simple-link:active { transform: scale(1.5) }\n'
|
|
ans += create_button.style + '\n'
|
|
ans += create_spinner.style + '\n'
|
|
ans += Breadcrumbs.STYLE_RULES + '\n'
|
|
return ans
|
|
)
|