Finish up EditWithComplete

This commit is contained in:
Kovid Goyal 2016-07-28 10:43:36 +05:30
parent 7c0d90f7b4
commit a585b65f3b
2 changed files with 51 additions and 36 deletions

View File

@ -13,8 +13,7 @@ class EditWithComplete:
inpt = E.input(type=input_type, name=name, title=tooltip or '', placeholder=placeholder or '') inpt = E.input(type=input_type, name=name, title=tooltip or '', placeholder=placeholder or '')
self.input_id = ensure_id(inpt) self.input_id = ensure_id(inpt)
self.onenterkey = onenterkey self.onenterkey = onenterkey
self.completion_popup = CompletionPopup(parent=parent) self.completion_popup = CompletionPopup(parent=parent, onselect=self.apply_completion)
self.ignore_next_input = False
inpt.addEventListener('keydown', self.onkeydown) inpt.addEventListener('keydown', self.onkeydown)
inpt.addEventListener('input', self.oninput) inpt.addEventListener('input', self.oninput)
self.completion_popup.add_associated_widget(inpt) self.completion_popup.add_associated_widget(inpt)
@ -25,10 +24,12 @@ class EditWithComplete:
def text_input(self): def text_input(self):
return document.getElementById(self.input_id) return document.getElementById(self.input_id)
def apply_completion(self): def apply_completion(self, text):
self.ignore_next_input = True if text:
text = self.completion_popup.current_text ti = self.text_input
self.text_input.value = text ti.value = text
ti.focus()
return True
def onkeydown(self, event): def onkeydown(self, event):
k = get_key(event) k = get_key(event)
@ -38,19 +39,19 @@ class EditWithComplete:
if k is 'enter': if k is 'enter':
if self.onenterkey: if self.onenterkey:
event.preventDefault(), event.stopPropagation() event.preventDefault(), event.stopPropagation()
self.enter_pressed() self.onenterkey()
elif k is 'tab': elif k is 'tab':
if self.completion_popup.is_visible: if self.completion_popup.is_visible:
self.apply_completion() if self.apply_completion(self.completion_popup.current_text):
self.completion_popup.hide()
else:
self.completion_popup.move_highlight()
event.preventDefault(), event.stopPropagation() event.preventDefault(), event.stopPropagation()
def oninput(self, event): def oninput(self, event):
if self.ignore_next_input: ti = self.text_input
self.ignore_next_input = False self.completion_popup.set_query(ti.value or '')
else: self.completion_popup.popup(ti)
ti = self.text_input
self.completion_popup.set_query(ti.value or '')
self.completion_popup.popup(ti)
def hide_completion_popup(self): def hide_completion_popup(self):
self.completion_popup.hide() self.completion_popup.hide()
@ -65,7 +66,7 @@ class EditWithComplete:
def create_search_bar(action, name, tooltip=None, placeholder=None, button=None): def create_search_bar(action, name, tooltip=None, placeholder=None, button=None):
parent = E.div() parent = E.div()
ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder) ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder, input_type='search')
def trigger(): def trigger():
ewc.hide_completion_popup() ewc.hide_completion_popup()

View File

@ -32,11 +32,11 @@ def click_in_popup(event):
def filter_clicks(event): def filter_clicks(event):
if shown_popups.length: if shown_popups.length:
event.stopPropagation(), event.preventDefault()
if not click_in_popup(event): if not click_in_popup(event):
for popup_id in shown_popups: for popup_id in shown_popups:
hide_popup(popup_id) hide_popup(popup_id)
shown_popups.clear() shown_popups.clear()
event.stopPropagation(), event.preventDefault()
def install_event_filters(): def install_event_filters():
window.addEventListener('click', filter_clicks, True) window.addEventListener('click', filter_clicks, True)
@ -66,14 +66,15 @@ class CompletionPopup:
CLASS = 'popup-completion-items' CLASS = 'popup-completion-items'
CURRENT_ITEM_CLASS = 'popup-completion-current-item' CURRENT_ITEM_CLASS = 'popup-completion-current-item'
def __init__(self, parent=None, max_items=25): 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) self.container_id = create_popup(parent)
self.onselect = onselect
self.items = [] self.items = []
self.matches = [] self.matches = []
c = self.container c = self.container
set_css(c, user_select='none') set_css(c, user_select='none')
c.appendChild(E.div(class_=self.CLASS, onclick=self._item_clicked)) 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
self.applied_query = self.current_query self.applied_query = self.current_query
@ -120,38 +121,41 @@ class CompletionPopup:
def hide(self): def hide(self):
self.container.style.display = 'none' self.container.style.display = 'none'
c = self.current_item
def highlight_up(self): if c:
self.move_highlight(True) c.classList.remove(self.CURRENT_ITEM_CLASS)
def highlight_down(self):
self.move_highlight(False)
def handle_keydown(self, key): def handle_keydown(self, key):
if key is 'escape': if key is 'escape':
self.hide() self.hide()
return True return True
if key is 'up': if key is 'up':
self.highlight_up() self.move_highlight(True)
return True return True
if key is 'down': if key is 'down':
self.highlight_down() self.move_highlight(False)
return True return True
return False return False
@property @property
def current_item(self): def current_item(self):
c = self.container c = self.container
return c.querySelector('{} > div.{}'.format(self.CLASS, self.CURRENT_ITEM_CLASS)) return c.querySelector('div.{} > div.{}'.format(self.CLASS, self.CURRENT_ITEM_CLASS))
def move_highlight(self, up=True): @property
def current_text(self):
return self.current_item?.textContent
def move_highlight(self, up=None):
if up is None:
up = self.is_upwards
ans = None ans = None
div = self.current_item div = self.current_item
if div: if div:
div.classList.remove(self.CURRENT_ITEM_CLASS) div.classList.remove(self.CURRENT_ITEM_CLASS)
ans = div.previousSibling if up else div.nextSibling ans = div.previousSibling if up else div.nextSibling
if not ans: if not ans:
c = self.container c = self.container.firstChild
ans = c.lastChild if up else c.firstChild ans = c.lastChild if up else c.firstChild
if ans: if ans:
ans.classList.add(self.CURRENT_ITEM_CLASS) ans.classList.add(self.CURRENT_ITEM_CLASS)
@ -188,18 +192,28 @@ class CompletionPopup:
if self.is_upwards: if self.is_upwards:
items = reversed(items) items = reversed(items)
for m in items: for m in items:
c.firstChild.appendChild(E.div(m)) c.firstChild.appendChild(E.div(m, onmouseenter=self.onmouseenter, onclick=self.onclick))
def _item_clicked(self, ev): def onmouseenter(self, event):
self.hide() div = self.current_item
if div:
div.classList.remove(self.CURRENT_ITEM_CLASS)
event.currentTarget.classList.add(self.CURRENT_ITEM_CLASS)
def onclick(self, event):
self.onmouseenter(event)
try:
if self.onselect:
self.onselect(self.current_text)
finally:
self.hide()
add_extra_css(def(): add_extra_css(def():
sel = 'div.' + CompletionPopup.CLASS sel = 'div.' + CompletionPopup.CLASS
style = build_rule(sel, background_color=get_color('window-background'), style = build_rule(sel, overflow='hidden', background_color=get_color('window-background'), border='solid 1px ' + get_color('window-foreground'))
border='solid 1px ' + get_color('window-foreground'))
sel += ' > div' sel += ' > div'
style += build_rule(sel, cursor='pointer', margin='1ex 1rem') style += build_rule(sel, cursor='pointer', padding='1ex 1rem', white_space='nowrap', text_overflow='ellipsis', overflow='hidden')
sel += ' > div.' + CompletionPopup.CURRENT_ITEM_CLASS sel += '.' + CompletionPopup.CURRENT_ITEM_CLASS
style += build_rule(sel, color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background')) style += build_rule(sel, color=get_color('list-hover-foreground'), background_color=get_color('list-hover-background'))
return style return style
) )