Add history to the search bar widget

This commit is contained in:
Kovid Goyal 2016-08-11 10:14:18 +05:30
parent 4815820f9b
commit 7852aa3eac
5 changed files with 57 additions and 31 deletions

View File

@ -3,6 +3,7 @@
from __python__ import hash_literals from __python__ import hash_literals
from ajax import ajax from ajax import ajax
from complete import create_search_bar
from dom import clear, set_css, build_rule, svgicon, add_extra_css from dom import clear, set_css, build_rule, svgicon, add_extra_css
from elementmaker import E from elementmaker import E
from gettext import gettext as _ from gettext import gettext as _
@ -56,15 +57,10 @@ class SearchPanel:
# Build search input # Build search input
search_container = div.firstChild search_container = div.firstChild
search_button = create_button(_('Search'), icon='search', action=self.execute_search.bind(self), tooltip=_('Do the search')) search_button = create_button(_('Search'), icon='search', tooltip=_('Do the search'))
search_container.appendChild(E.div(style="display: flex; width: 100%;", search_bar = create_search_bar(self.execute_search.bind(self), 'search-books', tooltip=_('Search for books'), placeholder=_('Enter the search query'), button=search_button)
E.input( set_css(search_bar, flex_grow='10', margin_right='0.5em')
type='search', name='search-books', search_container.appendChild(E.div(style="display: flex; width: 100%;", search_bar, search_button))
title=_('Search for books'), placeholder=_('Enter the search query'),
style="flex-grow: 10; margin-right: 0.5em", onkeydown=self.on_input_keydown.bind(self),
),
search_button
))
search_container.appendChild(E.ul(class_='search-items')) search_container.appendChild(E.ul(class_='search-items'))
# Build loading panel # Build loading panel
@ -80,12 +76,6 @@ class SearchPanel:
self.node_id_map = {} self.node_id_map = {}
self.active_nodes = {} self.active_nodes = {}
def on_input_keydown(self, event):
if event.keyCode is 13: # Enter
event.preventDefault(), event.stopPropagation()
event.target.nextSibling.focus()
self.execute_search()
def init(self): def init(self):
tb = self.search_control tb = self.search_control
# We dont focus the search box because on mobile that will cause the # We dont focus the search box because on mobile that will cause the

View File

@ -5,7 +5,9 @@ from __python__ import hash_literals, bound_methods
from dom import ensure_id from dom import ensure_id
from elementmaker import E from elementmaker import E
from keycodes import get_key from keycodes import get_key
from session import local_storage
from popups import CompletionPopup from popups import CompletionPopup
from utils import uniq
class EditWithComplete: class EditWithComplete:
@ -63,14 +65,30 @@ class EditWithComplete:
self.completion_popup.set_all_items(items) self.completion_popup.set_all_items(items)
def create_search_bar(action, name, tooltip=None, placeholder=None, button=None): def create_search_bar(action, name, tooltip=None, placeholder=None, button=None, history_size=100):
parent = E.div(style="display:flex")
parent = E.div()
ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder, input_type='search') ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder, input_type='search')
parent.lastChild.style.width = '100%'
history_name = 'search-bar-history-' + name
def update_completion_items():
items = local_storage().get(history_name)
if items?.length:
ewc.set_all_items(items)
update_completion_items()
def trigger(): def trigger():
text = ewc.text_input.value
ewc.hide_completion_popup() ewc.hide_completion_popup()
action() action(text)
if text and text.strip():
items = local_storage().get(history_name) or v'[]'
idx = items.indexOf(text)
if idx > -1:
items = items.splice(idx, 1)
items.unshift(text)
local_storage().set(history_name, uniq(items[:history_size]))
update_completion_items()
ewc.onenterkey = trigger ewc.onenterkey = trigger
if button: if button:
@ -81,6 +99,8 @@ def create_search_bar(action, name, tooltip=None, placeholder=None, button=None)
# }}} # }}}
def main(container): def main(container):
ewc = EditWithComplete(parent=container, placeholder='Testing edit with complete') container.appendChild(create_search_bar(print, 'test-search-bar', placeholder='Testing search bar'))
ewc.set_all_items('a a1 a11 a12 a13 b b1 b2 b3'.split(' ')) container.firstChild.lastChild.focus()
ewc.text_input.focus() # ewc = EditWithComplete(parent=container, placeholder='Testing edit with complete')
# ewc.set_all_items('a a1 a11 a12 a13 b b1 b2 b3'.split(' '))
# ewc.text_input.focus()

View File

@ -48,7 +48,7 @@ def create_popup(parent, idprefix):
div = E.div(id=pid, style='display: none; position: absolute; z-index: {}'.format(POPUP_Z_INDEX)) div = E.div(id=pid, style='display: none; position: absolute; z-index: {}'.format(POPUP_Z_INDEX))
parent = parent or document.body parent = parent or document.body
parent.appendChild(div) parent.appendChild(div)
return pid return div
def show_popup(popup_id, associated_widget_ids=None): def show_popup(popup_id, associated_widget_ids=None):
elem = document.getElementById(popup_id) elem = document.getElementById(popup_id)
@ -68,12 +68,12 @@ class CompletionPopup:
def __init__(self, parent=None, max_items=25, onselect=None): def __init__(self, parent=None, max_items=25, onselect=None):
self.max_items = max_items self.max_items = max_items
self.container_id = create_popup(parent) c = create_popup(parent)
set_css(c, user_select='none')
self.container_id = c.getAttribute('id')
self.onselect = onselect self.onselect = onselect
self.items = [] self.items = []
self.matches = [] self.matches = []
c = self.container
set_css(c, user_select='none')
c.appendChild(E.div(class_=self.CLASS)) c.appendChild(E.div(class_=self.CLASS))
self.associated_widget_ids = set() self.associated_widget_ids = set()
self.current_query, self.is_upwards = '', False self.current_query, self.is_upwards = '', False
@ -88,7 +88,7 @@ class CompletionPopup:
return self.container.style.display is not 'none' return self.container.style.display is not 'none'
def set_all_items(self, items): def set_all_items(self, items):
self.items = items self.items = list(items)
self.matches = [] self.matches = []
self.applied_query = '' self.applied_query = ''
@ -210,7 +210,7 @@ class CompletionPopup:
add_extra_css(def(): add_extra_css(def():
sel = 'div.' + CompletionPopup.CLASS sel = 'div.' + CompletionPopup.CLASS
style = build_rule(sel, overflow='hidden', background_color=get_color('window-background'), border='solid 1px ' + get_color('window-foreground')) style = build_rule(sel, overflow='hidden', text_align='left', background_color=get_color('window-background'), border='solid 1px ' + get_color('window-foreground'))
sel += ' > div' sel += ' > div'
style += build_rule(sel, cursor='pointer', padding='1ex 1rem', white_space='nowrap', text_overflow='ellipsis', overflow='hidden') style += build_rule(sel, cursor='pointer', padding='1ex 1rem', white_space='nowrap', text_overflow='ellipsis', overflow='hidden')
sel += '.' + CompletionPopup.CURRENT_ITEM_CLASS sel += '.' + CompletionPopup.CURRENT_ITEM_CLASS

View File

@ -72,9 +72,8 @@ def get_session_storage():
class SessionData: class SessionData:
global_prefix = 'calibre-' def __init__(self, global_prefix=None):
self.global_prefix = global_prefix or 'calibre-session-'
def __init__(self):
self.storage = get_session_storage() self.storage = get_session_storage()
self.overflow_storage = {} self.overflow_storage = {}
self.has_overflow = False self.has_overflow = False
@ -115,6 +114,13 @@ class SessionData:
self.overflow_storage = {} self.overflow_storage = {}
self.has_overflow = False self.has_overflow = False
_local_storage = None
def local_storage():
nonlocal _local_storage
if not _local_storage:
_local_storage = SessionData('calibre-local-')
return _local_storage
class UserSessionData(SessionData): class UserSessionData(SessionData):
def __init__(self, username, saved_data): def __init__(self, username, saved_data):

View File

@ -131,6 +131,16 @@ def viewport_to_document(x, y, doc):
def username_key(username): def username_key(username):
return ('u' if username else 'n') + username return ('u' if username else 'n') + username
def uniq(vals):
# Remove all duplicates from vals, while preserving order
ans = v'[]'
seen = {}
for x in vals:
if not seen[x]:
seen[x] = True
ans.push(x)
return ans
if __name__ is '__main__': if __name__ is '__main__':
print(fmt_sidx(10), fmt_sidx(1.2)) print(fmt_sidx(10), fmt_sidx(1.2))
print(list(map(human_readable, [1, 1024.0, 1025, 1024*1024*2.3]))) print(list(map(human_readable, [1, 1024.0, 1025, 1024*1024*2.3])))