def _update_positions(*args): if _scrolling: # not sure why this fires on scroll but it does on chrome return _S(".popover").addClass("PopNoTransition").popover("show").removeClass( "PopNoTransition" )
def _get_jquery_for_component(component): if isinstance(component, _anvil.Button): return _S(_js.get_dom_node(component).firstElementChild) elif isinstance(component, _anvil.FileLoader): return _S(_js.get_dom_node(component)).find('form') else: return _S(_js.get_dom_node(component))
def _sticky_leave(e): popper_element = None popover_id = _S(e.currentTarget).attr("popover_id") if popover_id in _sticky_popovers and not _S( "[popover_id='{}']:hover".format(popover_id) ): popper_element = _visible_popovers.get(popover_id) if popper_element is not None: _popper_execute(popper_element, "hide")
def _get_jquery_for_component(component): if isinstance(component, _anvil.Button): return _S(_js.get_dom_node(component).firstElementChild) elif isinstance(component, _anvil.FileLoader): return _S(_js.get_dom_node(component)).find("form") elif isinstance(component, (_anvil.CheckBox, _anvil.RadioButton)): return _S(_js.get_dom_node(component)).find("input") else: return _S(_js.get_dom_node(component))
def _sticky_leave(e): popper_element = None popover_id = _S(e.currentTarget).attr('popover_id') if popover_id in _sticky_popovers and not _S( '[popover_id={}]:hover'.format(popover_id)): popper_element = _visible_popovers.get(popover_id) if popper_element is not None: popper_element.data( 'bs.popover' ).inState.click = False # see bug https://github.com/twbs/bootstrap/issues/16732 popper_element.popover('hide')
def _get_jquery_popper_element(popper): if isinstance(popper, _anvil.Button): # use the button node not the div node so that point is in the right place element = _anvil.js.get_dom_node(popper).firstElementChild else: element = _anvil.js.get_dom_node(popper) return _S(element) # return the jquery element
def __init__(self, **properties): # Set Form properties and Data Bindings. self._init = False self._dom_node = _js.get_dom_node(self) _S_dom_node = _S(self._dom_node) self._el = _S_dom_node.find("select") _S_dom_node.html("").append(self._el) # remove all the script tags before they load into the dom self._values = {} self._invalid = [] # Any code you write here will run when the form opens self._props = props = _defaults | properties props["items"] = props["items"] or [] selected = props.pop("selected", ()) self.init_components(**props) self._el.selectpicker() self._el.on("changed.bs.select", self.change) self._el.on("shown.bs.select", self._opened) self._el.on("hidden.bs.select", self._closed) self.set_event_handler("x-popover-init", self._mk_popover) if selected: self.selected = selected self._user_selected_all(False) menu = self._el.data("selectpicker")["$menu"] menu.find(".bs-actionsbox").on("click", self._user_selected_all) self._init = True
def __init__(self, margin_top=0, border="1px solid grey", **properties): dom_node = _S(anvil.js.get_dom_node(self)) self.margin_node = dom_node.find(".margin-element") self.break_container = dom_node.find(".break-container") self.margin_top = margin_top self.border = border self.init_components(**properties)
def _hide_popovers_on_outside_click(e): target = e.target if target.classList.contains('anvil-popover'): nearest_id = target.getAttribute('popover_id') else: nearest_id = _S(target).closest('.anvil-popover').attr('popover_id') visible_popovers = _visible_popovers.copy() for popover_id in visible_popovers: # use copy since we don't want the dict to change size if nearest_id is not popover_id: _hide(popover_id, visible_popovers)
def _hide_popovers_on_outside_click(e): target = e.target if target.classList.contains("anvil-popover"): nearest_id = target.getAttribute("popover_id") else: nearest_id = _S(target).closest(".anvil-popover").attr("popover_id") # use copy since the dict changes size during iteration for popover_id, popper_element in _visible_popovers.copy().items(): if nearest_id == popover_id: continue if _get_data(popper_element, "autoDismiss", True): _popper_execute(popper_element, "hide")
def _clean_items(items): options = [] value_dict = {} for idx, item in enumerate(items): if isinstance(item, str): option, value = _option_from_str(item, idx) elif isinstance(item, (tuple, list)): option, value = _option_from_tuple(item, idx) elif isinstance(item, dict): option, value = _option_from_dict(item, idx) else: raise TypeError( f"Invalid item at index {idx} (got type {type(item)})") # use strings since the value from jquery is always a string value_dict[str(idx)] = value options.append(option) return _S("\n".join(options)), value_dict
def _on_hide(self, **e_args): """This method is called when the TextBox is removed from the screen""" _document.body.removeChild(self._lp_node) _S(_window).off("resize", self._reset_position)
def _on_show(self, **e_args): """This method is called when the TextBox is shown on the screen""" _document.body.appendChild(self._lp_node) _S(_window).on("resize", self._reset_position)
# SPDX-License-Identifier: MIT # # Copyright (c) 2021 The Anvil Extras project team members listed at # https://github.com/anvilistas/anvil-extras/graphs/contributors # # This software is published at https://github.com/anvilistas/anvil-extras __version__ = "2.0.1" from anvil.js.window import jQuery as _S alert_modal = _S("#alert-modal") def handle_alert_unload() -> bool: """ if there is an active alert which is not dismissible then navigation is prevented return value indicates whether this function took control of the on_navigation """ data = alert_modal.data("bs.modal") if data is None: return False elif not data.isShown: return False elif data.options and data.options.backdrop != "static": # bootstrap alerts have a backdrom of static when not dismissible alert_modal.modal("hide") return False from . import _navigation _navigation.stopUnload()
def sticky_leave(e): sleep(0.1) # small delay to allow the mouse to move to the element if not _S("[popover_id='{}']:hover".format(popper_id)): pop(self, "hide")
def popover(self, content, title='', placement='right', trigger='click', animation=True, delay={ "show": 100, "hide": 100 }, max_width=None, auto_dismiss=True): """should be called by a button or link content - either text or an anvil component or Form placement - right, left, top, bottom (for left/right best to have links and buttons inside flow panels) trigger - manual, focus, hover, click (can be a combination of two e.g. 'hover focus') animation - True or False delay - {'show': 100, 'hide': 100} max_width - bootstrap default is 276px you might want this wider if the content is a form then the form will have an attribute self.popper added """ html = not isinstance(content, str) if html: content.popper = self # add the popper to the content form content = _anvil.js.get_dom_node(content) # get the dom node max_width = _default_max_width if max_width is None else max_width # can effect the title of the popover so temporarily set it to '' tooltip, self.tooltip = self.tooltip, '' popper_id = _get_random_string(5) popper_element = _get_jquery_popper_element(self) if trigger is 'stickyhover': trigger = 'manual' popper_element.on('mouseenter', lambda e: None if pop(self, 'is_visible') else pop(self, 'show'))\ .on('mouseleave', lambda e: None if _S('[popover_id={}]:hover'.format(popper_id)) else pop(self, 'hide')) _set_sticky_hover() _sticky_popovers.add(popper_id) popper_element.popover({ 'content': content, 'title': title, 'placement': placement, 'trigger': trigger, 'animation': animation, 'delay': delay, 'html': html, 'template': _template.format(popper_id, max_width), 'container': 'body' }) if tooltip: self.tooltip = tooltip # otherwise the tooltip doesn't work for Buttons popper_element.attr('title', tooltip) popper_element.on("show.bs.popover", lambda e: _visible_popovers.update({popper_id: popper_element}) if auto_dismiss else None)\ .on("hide.bs.popover", lambda e: _visible_popovers.pop(popper_id, None))\ .addClass('anvil-popover')\ .attr('popover_id', popper_id)
def _set_sticky_hover(): if not _sticky_popovers: _S("body").on("mouseleave", ".popover", _sticky_leave)
def _update_positions(*args): _S('.popover').addClass("PopNoTransition")\ .popover('show')\ .removeClass("PopNoTransition")
def _set_sticky_hover(): if not _sticky_popovers: _S('body').on('mouseleave', '.popover', _sticky_leave)
def _get_random_string(_len): return ''.join(_random_choice(_letters) for _ in range(_len)) _visible_popovers = {} _template = '<div class="popover anvil-popover" role="tooltip" popover_id={} style="max-width:{}; "><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' # temp style for updating popovers without transition animations _S("""<style> .PopNoTransition { -moz-transition: none !important; -webkit-transition: none !important; -o-transition: none !important; transition: none !important; } </style> """).appendTo(_S('head')) if __name__ == "__main__": _ = _anvil.ColumnPanel() _.set_event_handler( 'show', lambda **e: _anvil.Notification('oops, popover is a dependency', style='danger', timeout=None).show()) _anvil.open_form(_) _ = None
def _make_popover_element(dom_node): _S(dom_node).addClass("anvil-popover").attr("popover_id", id)
def selected(self): return [ self._values[e.value] for e in _S("option:selected", self._el) if e.value != "" ]
def off_dd_click(e): # see bug #271 if not e.target.closest(".bootstrap-select"): _S(_document).trigger("click.bs.dropdown.data-api")