def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, **attributes, ): inputelement_attrs = inputelement_attrs or {} super().__init__( label, hg.DIV( self.get_input_element(inputelement_attrs, errors), hg.DIV( hg.BUTTON( Icon("caret--up", size=16), _class="bx--number__control-btn up-icon", type="button", ), hg.BUTTON( Icon("caret--down", size=16), _class="bx--number__control-btn down-icon", type="button", ), _class="bx--number__controls", ), _class="bx--number__input-wrapper", ), errors, help_text, data_numberinput=True, data_invalid=hg.If(errors.condition, True), **hg.merge_html_attrs(attributes, {"_class": "bx--number"}), )
def __init__(self, size="xl", widgetattributes=None, **kwargs): kwargs["_class"] = kwargs.get("_class", "") + f" bx--search bx--search--{size}" kwargs["data-search"] = True kwargs["role"] = "search" attributes = { "id": "search__" + hg.html_id(self), "_class": "bx--search-input", "type": "text", "placeholder": _("Search"), **(widgetattributes or {}), } super().__init__( hg.LABEL(_("Search"), _class="bx--label", _for=attributes["id"]), hg.INPUT(**attributes), Icon("search", size=16, _class="bx--search-magnifier", aria_hidden="true"), hg.BUTTON( Icon("close", size=20, _class="bx--search-clear"), _class="bx--search-close bx--search-close--hidden", title=_("Clear search input"), aria_label=_("Clear search input"), type="button", ), **kwargs, )
def __init__(self, *labels, selected=0, **wrapperkwargs): """ labels: tuples in the form (label, button-attributes) button-attributes will be applied to the generated button for a context label data_target can be a CSS-selector of a panel for the according context """ wrapperkwargs["_class"] = (wrapperkwargs.get("_class", "") + " bx--content-switcher") super().__init__( *[ hg.BUTTON( hg.SPAN(label, _class="bx--content-switcher__label"), _class="bx--content-switcher-btn" + (" bx--content-switcher--selected" if i == selected else ""), role="tab", type="button", **kwargs, ) for i, (label, kwargs) in enumerate(labels) ], data_content_switcher=True, role="tablist", **wrapperkwargs, )
def __init__( self, title, subtitle, kind="info", lowcontrast=False, hideclosebutton=False, hidetimestamp=False, **attributes, ): """ kind: can be one of "error" "info", "info-square", "success", "warning", "warning-alt" """ assert ( kind in KIND_ICON_MAPPING ), f"kind '{kind}' does not exists, must be one of {KIND_ICON_MAPPING.keys()}" self.hidetimestamp = hidetimestamp attributes["data-notification"] = True attributes["_class"] = ( attributes.get("_class", "") + f" bx--toast-notification bx--toast-notification--{kind}" ) if lowcontrast: attributes["_class"] += " bx--toast-notification--low-contrast" attributes["role"] = "alert" timestampelem = ( [ htmlgenerator.P( _("Time stamp "), _class="bx--toast-notification__caption" ) ] if not hidetimestamp else [] ) children = [ Icon( KIND_ICON_MAPPING[kind], size=20, _class="bx--toast-notification__icon", ), htmlgenerator.DIV( htmlgenerator.H3(title, _class="bx--toast-notification__title"), htmlgenerator.P(subtitle, _class="bx--toast-notification__subtitle"), *timestampelem, _class="bx--toast-notification__details", ), ] if not hideclosebutton: children.append( htmlgenerator.BUTTON( Icon("close", size=20, _class="bx--toast-notification__close-icon"), data_notification_btn=True, _class="bx--toast-notification__close-button", aria_label="close", ) ) super().__init__(*children, **attributes)
def __init__( self, links, menuiconname="overflow-menu--vertical", menuname=None, direction="bottom", flip=False, item_attributes={}, **attributes, ): attributes["data-overflow-menu"] = True attributes["_class"] = attributes.get("_class", "") + " bx--overflow-menu" item_attributes["_class"] = (item_attributes.get("_class", "") + " bx--overflow-menu-options__option") menuid = hg.F(lambda c: OverflowMenu.MENUID_TEMPLATE % hg.html_id( c.get("row", self))) triggerid = hg.F(lambda c: (OverflowMenu.MENUID_TEMPLATE % hg.html_id( c.get("row", self))) + "-trigger") super().__init__( hg.BUTTON( Icon(menuiconname, size=16), _class="bx--overflow-menu__trigger" + (" bx--tooltip__trigger bx--tooltip--a11y bx--tooltip--right bx--tooltip--align-start" if menuname is not None else ""), aria_haspopup="true", aria_expanded="false", aria_controls=menuid, type="button", id=triggerid, ), hg.DIV( hg.UL( hg.Iterator( links, "link", hg.LI( hg.F(asoverflowbutton), **item_attributes, ), ), _class="bx--overflow-menu-options__content", ), _class="bx--overflow-menu-options" + (" bx--overflow-menu--flip" if flip else ""), tabindex="-1", role="menu", aria_labelledby=triggerid, data_floating_menu_direction=direction, id=menuid, ), **attributes, ) if menuname is not None: self[0].insert(0, hg.SPAN(menuname, _class="bx--assistive-text"))
def __init__( self, item_iterator, iteratorclass=htmlgenerator.Iterator, menuname=None, direction="bottom", flip=False, item_attributes={}, **attributes, ): # making the class inline seems better, I think we can enforce scoping the type to this instance of OverflowMenu class MenuItemValueProvider(htmlgenerator.ValueProvider): attributename = "item" """item_iterator: an iterable which contains bread.menu.Action objects where the onclick value is what will be passed to the onclick attribute of the menu-item (and therefore should be javascript, e.g. "window.location.href='/home'"). All three item_iterator in the tuple can be lazy objects iteratorclass: If the Iterator needs additional values in order to generate item_iterator it can be customized and passed here""" attributes["data-overflow-menu"] = True attributes["_class"] = attributes.get("_class", "") + " bx--overflow-menu" menuid = f"overflow-menu-{hash(id(self))}" super().__init__( htmlgenerator.BUTTON( Icon("overflow-menu--vertical", size=16), _class="bx--overflow-menu__trigger" + (" bx--tooltip__trigger bx--tooltip--a11y bx--tooltip--right bx--tooltip--align-start" if menuname is not None else ""), aria_haspopup="true", aria_expanded="false", aria_controls=menuid, _id=f"{menuid}-trigger", ), htmlgenerator.DIV( htmlgenerator.UL( iteratorclass( item_iterator, MenuItemValueProvider.Binding(OverflowMenuItem)( MenuItemValueProvider, **item_attributes), MenuItemValueProvider, ), _class="bx--overflow-menu-options__content", ), _class="bx--overflow-menu-options" + (" bx--overflow-menu--flip" if flip else ""), tabindex="-1", role="menu", aria_labelledby=f"{menuid}-trigger", data_floating_menu_direction=direction, id=menuid, ), **attributes, ) if menuname is not None: self[0].insert( 0, htmlgenerator.SPAN(menuname, _class="bx--assistive-text"))
def __init__(self, platform, company, searchbar, actions=(), *args, **kwargs): super().__init__( hg.If( HasBreadCookieValue("sidenav-hidden", "true"), variable_size_header_part(hg.BaseElement(), company, searchbar, "5rem"), variable_size_header_part( hg.SPAN(platform, _class="bx--header__name--prefix"), company, searchbar, "18rem", ), ), hg.DIV( hg.If( hg.F(lambda c: c["request"].user.is_authenticated), hg.A( hg.SPAN( hg.C("request.user.get_username"), _class="bx--header__name--prefix", ), _class="bx--header__name", href=reverse("userprofile"), title=hg.C("request.user.get_username"), style="padding: 0; margin-right: 1rem", ), ), hg.If( hg.F(lambda c: c["request"].user.is_authenticated), hg.BUTTON( Icon( "logout", size=20, _class="bx--navigation-menu-panel-expand-icon", aria_hidden="true", ), Icon( "logout", size=20, _class="bx--navigation-menu-panel-collapse-icon", aria_hidden="true", ), _class="bx--header__menu-trigger bx--header__action", title=_("Logout"), data_navigation_menu_panel_label_expand=_("Logout"), data_navigation_menu_panel_label_collapse=_("Close"), onclick=f"document.location = '{reverse('logout')}'", ), ), _class="bx--header__global", ), _class="bx--header", data_header=True, )
def _close_button(resultcontainerid, widgetattributes): kwargs = { "_class": hg.BaseElement( "bx--search-close", hg.If(widgetattributes.get("value"), None, " bx--search-close--hidden"), ), "title": _("Clear search input"), "aria_label": _("Clear search input"), "type": "button", } if resultcontainerid is not None: kwargs["onclick"] = hg.format( "document.getElementById('{}').innerHTML = '';", resultcontainerid ) return hg.BUTTON(Icon("close", size=20, _class="bx--search-clear"), **kwargs)
def __init__(self, itemvalueproviderclass, **attributes): attributes["_class"] = (attributes.get("_class", "") + " bx--overflow-menu-options__option") super().__init__( htmlgenerator.BUTTON( itemvalueproviderclass.Binding(htmlgenerator.DIV)( htmlgenerator.F(lambda e, c: htmlgenerator.BaseElement( Icon(e.item.icon, size=16), e.item.label) if e.item.icon else e.item.label), _class="bx--overflow-menu-options__option-content", ), _class="bx--overflow-menu-options__btn", role="menuitem", ), **attributes, )
def as_header_cell(self, orderingurlparameter="ordering"): headcontent = hg.DIV(self.header, _class="bx--table-header-label") if self.sortingname: headcontent = hg.BUTTON( headcontent, Icon("arrow--down", _class="bx--table-sort__icon", size=16), Icon( "arrows--vertical", _class="bx--table-sort__icon-unsorted", size=16, ), _class=hg.BaseElement( "bx--table-sort ", sortingclass_for_column(orderingurlparameter, self.sortingname), ), data_event="sort", **sortinglink_for_column(orderingurlparameter, self.sortingname), ) return hg.TH(headcontent, lazy_attributes=self.th_attributes)
def __init__(self, header: Any, content: Any, **attributes): """ Parameters ---------- header : Any the header of the tile that is not hidden under the fold content : Any the hidden content that only visible when user click to unfold the tile **attributes : optional keyword arguments representing the specific HTML attributes for the tile """ super().__init__( hg.BUTTON(Icon("chevron--down", size="16"), _class="bx--tile__chevron"), hg.DIV( hg.SPAN( header, data_tile_atf=True, _class="bx--tile-content__above-the-fold", ), hg.SPAN( content, _class="bx--tile-content__below-the-fold", onclick="event.stopPropagation();", style="cursor: initial", ), _class="bx--tile-content", ), **hg.merge_html_attrs( attributes, { "_class": "bx--tile bx--tile--expandable", "data_tile": "expandable", "tabindex": "0", }, ))
def __init__( self, title, subtitle, action=None, kind="info", lowcontrast=False, hideclosebutton=False, **attributes, ): """ action: typle with (action_name, javascript_onclick), e.g. ("Open Google", "windows.location='https://google.com'") kind: can be one of "error" "info", "info-square", "success", "warning", "warning-alt" """ assert ( kind in KIND_ICON_MAPPING ), f"kind '{kind}' does not exists, must be one of {KIND_ICON_MAPPING.keys()}" assert action is None or ( len(action) == 2 ), "action must be a tuple with: (action_name, javascript_onclick)" attributes["data-notification"] = True attributes["_class"] = ( attributes.get("_class", "") + f" bx--inline-notification bx--inline-notification--{kind}" ) if lowcontrast: attributes["_class"] += " bx--inline-notification--low-contrast" attributes["role"] = "alert" children = [ htmlgenerator.DIV( Icon( KIND_ICON_MAPPING[kind], size=20, _class="bx--inline-notification__icon", ), htmlgenerator.DIV( htmlgenerator.P(title, _class="bx--inline-notification__title"), htmlgenerator.P( subtitle, _class="bx--inline-notification__subtitle" ), _class="bx--inline-notification__text-wrapper", ), _class="bx--inline-notification__details", ), ] if action is not None: children.append( Button( action[0], onclick=action[1], type="ghost", small=True, _class="bx--inline-notification__action-button", ) ) if not hideclosebutton: children.append( htmlgenerator.BUTTON( Icon( "close", size=20, _class="bx--inline-notification__close-icon" ), data_notification_btn=True, _class="bx--inline-notification__close-button", aria_label="close", ) ) super().__init__(*children, **attributes)
def __init__(self, menu: "bread.menu.Menu", **kwargs): kwargs["_class"] = hg.BaseElement( kwargs.get("_class", ""), " bx--side-nav bx--side-nav--rail", hg.If( HasBreadCookieValue("sidenav-hidden", "true"), "", " bx--side-nav--expanded", ), ) kwargs["data_side_nav"] = True super().__init__( hg.NAV( hg.UL( hg.Iterator( hg.F(lambda c: (i for i in sorted( hg.resolve_lazy(menu, c)._registry.values()) if i.has_permission(c["request"]))), "menugroup", hg.LI( hg.If( hg.F(lambda c: len(c["menugroup"].items) > 1 or c["menugroup"].force_show), hg.BaseElement( hg.BUTTON( hg.DIV( Icon(hg.C("menugroup.iconname"), size=16), _class="bx--side-nav__icon", ), hg.SPAN( hg.C("menugroup.label"), _class= "bx--side-nav__submenu-title", ), hg.DIV( Icon("chevron--down", size=16), _class= "bx--side-nav__icon bx--side-nav__submenu-chevron", ), _class="bx--side-nav__submenu", type="button", aria_haspopup="true", aria_expanded=hg.If( isactive("menugroup"), "true"), ), hg.UL( hg.Iterator( hg.F(lambda c: (i for i in sorted( c["menugroup"].items) if i. has_permission(c[ "request"]))), "menuitem", hg.LI( hg.A( hg.SPAN( hg. C("menuitem.link.label" ), _class= "bx--side-nav__link-text", ), _class=hg.BaseElement( "bx--side-nav__link", hg.If( isactive( "menuitem"), " bx--side-nav__link--current", ), ), href=hg.C( "menuitem.link.href"), ), _class=hg.BaseElement( "bx--side-nav__menu-item", hg.If( isactive("menuitem"), " bx--side-nav__menu-item--current", ), ), ), ), _class="bx--side-nav__menu", ), ), hg.A( hg.DIV( Icon( hg. C("menugroup.items.0.link.iconname" ), size=16, ), _class="bx--side-nav__icon", ), hg.SPAN( hg.C("menugroup.items.0.link.label"), _class="bx--side-nav__link-text", ), _class=hg.BaseElement( "bx--side-nav__link", hg.If( isactive("menugroup"), " bx--side-nav__link--current", ), ), href=hg.C("menugroup.items.0.link.href"), ), ), _class=hg.BaseElement( "bx--side-nav__item", hg.If(isactive("menugroup"), " bx--side-nav__item--active"), ), ), ), _class="bx--side-nav__items", ), hg.FOOTER( hg.BUTTON( hg.DIV( Icon( "close", size=20, _class= "bx--side-nav__icon--collapse bx--side-nav-collapse-icon", aria_hidden="true", ), Icon( "chevron--right", size=20, _class= "bx--side-nav__icon--expand bx--side-nav-expand-icon", aria_hidden="true", ), _class="bx--side-nav__icon", ), hg.SPAN( "Toggle the expansion state of the navigation", _class="bx--assistive-text", ), _class="bx--side-nav__toggle", onclick= "setBreadCookie('sidenav-hidden', getBreadCookie('sidenav-hidden', 'false') != 'true');", ), _class="bx--side-nav__footer", ), _class="bx--side-nav__navigation", ), **kwargs, )
def __init__( self, message, details, kind="info", lowcontrast=False, hideclosebutton=False, hidetimestamp=False, autoremove=4.0, **attributes, ): """ kind: can be one of "error" "info", "info-square", "success", "warning", "warning-alt" autoremove: remove notification after ``autoremove`` seconds """ self.hidetimestamp = hidetimestamp attributes["data-notification"] = True attributes["_class"] = hg.BaseElement( attributes.get("_class", ""), " bx--toast-notification bx--toast-notification--", kind, hg.If(lowcontrast, " bx--toast-notification--low-contrast"), ) attributes["role"] = "alert" attributes["style"] = hg.BaseElement( attributes.get("style", ""), ";opacity: 0; animation: ", hg.F(lambda c: autoremove * (c["message_index"] + 1)), "s ease-in-out notification", ) attributes["onload"] = hg.BaseElement( attributes.get("onload", ""), ";setTimeout(() => this.style.display = 'None', ", hg.F(lambda c: (autoremove * 1000 * (c["message_index"] + 1))), ")", ) timestampelem = ([ hg.P( _("Time stamp"), " ", _class="bx--toast-notification__caption") ] if not hidetimestamp else []) children = [ Icon( hg.F(lambda c: KIND_ICON_MAPPING[hg.resolve_lazy(kind, c)]), size=20, _class="bx--toast-notification__icon", ), hg.DIV( hg.DIV(message, _class="bx--toast-notification__title"), hg.DIV(details, _class="bx--toast-notification__subtitle"), *timestampelem, _class="bx--toast-notification__details", ), ] children.append( hg.If( hideclosebutton, None, hg.BUTTON( Icon("close", size=20, _class="bx--toast-notification__close-icon"), data_notification_btn=True, _class="bx--toast-notification__close-button", aria_label="close", ), )) super().__init__(*children, **attributes)
def __init__( self, message, details, action=None, kind="info", lowcontrast=False, hideclosebutton=False, **attributes, ): """ action: typle with (action_name, javascript_onclick), e.g. ("Open Google", "windows.location='https://google.com'") kind: can be one of "error" "info", "info-square", "success", "warning", "warning-alt" """ if action is not None and (len(action) != 2): raise ValueError( "action must be a tuple with: (action_name, javascript_onclick)" ) attributes["data-notification"] = True attributes["_class"] = hg.BaseElement( attributes.get("_class", ""), " bx--inline-notification bx--inline-notification--", kind, hg.If(lowcontrast, " bx--inline-notification--low-contrast"), ) attributes["role"] = "alert" children = [ hg.DIV( Icon( hg.F( lambda c: KIND_ICON_MAPPING[hg.resolve_lazy(kind, c)]), size=20, _class="bx--inline-notification__icon", ), hg.DIV( hg.P(message, _class="bx--inline-notification__title"), hg.P(details, _class="bx--inline-notification__subtitle"), _class="bx--inline-notification__text-wrapper", ), _class="bx--inline-notification__details", ), ] if action is not None: children.append( Button( action[0], onclick=action[1], buttontype="ghost", small=True, _class="bx--inline-notification__action-button", )) children.append( hg.If( hideclosebutton, None, hg.BUTTON( Icon("close", size=20, _class="bx--inline-notification__close-icon"), data_notification_btn=True, _class="bx--inline-notification__close-button", aria_label="close", ), )) super().__init__(*children, **attributes)
def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, **attributes, ): inputelement_attrs = inputelement_attrs or {} uploadbutton = hg.LABEL( hg.SPAN(_("Select file"), role="button"), tabindex=0, _class=hg.BaseElement( "bx--btn bx--btn--tertiary", hg.If(inputelement_attrs.get("disabled"), " bx--btn--disabled"), ), data_file_drop_container=True, disabled=inputelement_attrs.get("disabled"), data_invalid=hg.If(getattr(errors, "condition", False), True), _for=inputelement_attrs.get("id"), ) input = self.get_input_element( inputelement_attrs, errors, onload=""" that = this; document.addEventListener('change', (e) => { that.parentElement.querySelector('[data-file-container]').innerHTML = ''; var widget = new CarbonComponents.FileUploader(that.parentElement); widget._displayFilenames(); widget.setState('edit'); }); """, ) # we can only clear the field if it originates form a django field # otherwise it has no use clearbox = None if boundfield: checkbox_name = hg.F( lambda c: hg.resolve_lazy(boundfield, c).field.widget. clear_checkbox_name(hg.resolve_lazy(boundfield, c).html_name)) checkbox_id = hg.F( lambda c: hg.resolve_lazy(boundfield, c).field.widget. clear_checkbox_id(hg.resolve_lazy(checkbox_name, c))) clearbox = hg.If( self.clearable, hg.INPUT( type="checkbox", name=checkbox_name, id=checkbox_id, style="display: none", ), ) # clearbutton is always used, to allow clearing a just selected field in the browser clearbutton = hg.If( self.clearable, hg.SPAN( hg.BUTTON( Icon("close", size=16), _class="bx--file-close", type="button", aria_label="close", onclick=hg.If( clearbox, hg.format("$('#{}').checked = 'checked';", checkbox_id), ), ), data_for=inputelement_attrs.get("id"), _class="bx--file__state-container", ), ) super().__init__( label, hg.DIV( uploadbutton, input, clearbox, hg.DIV( hg.If( inputelement_attrs.get("value"), hg.SPAN( hg.P( hg.If( hg.F(lambda c: hasattr( hg.resolve_lazy(inputelement_attrs, c). get("value").file, "name", )), hg.A( hg.F(lambda c: os.path.basename( hg.resolve_lazy( inputelement_attrs, c).get( "value").name)), href=hg.F(lambda c: settings.MEDIA_URL + hg.resolve_lazy( inputelement_attrs, c ).get("value").name), ), hg.F(lambda c: os.path.basename( hg.resolve_lazy(inputelement_attrs, c). get("value").name)), ), _class="bx--file-filename", ), clearbutton, _class="bx--file__selected-file", ), ), data_file_container=True, _class="bx--file-container", ), help_text, errors, _class="bx--file", data_file=True, ), **attributes, )
def __init__( self, heading, *content, label="", size="sm", buttons=(), id=None, with_form=False, **attributes, ): """ heading: Modal title content: content elements of the modal, can be empty label: small informative label above heading size: one of ["xs", "sm", "md", "lg"] buttons: buttons displayed on bottom of modal, last button has default focus the attribute "data_modal_close" can be set on an button in order to make it a cancel button In order to open the modal just pass self.openerattributes as kwargs to another html element, e.g. a button modal = modal.Modal("My Modal", "Hello world") button.Button("Model", **modal.openerattributes) """ if size not in Modal.SIZES: raise ValueError( f"argument 'size' has value {size} but needs to be one of {Modal.SIZES}" ) if buttons and "data_modal_primary_focus" not in buttons[-1].attributes: buttons[-1].attributes["data_modal_primary_focus"] = True attributes["_class"] = attributes.get("_class", "") + " bx--modal" self.id = hg.html_id(self, prefix="modal-") if id is None else id self.openerattributes = { "data_modal_target": hg.format("#{}", self.id) } self.contentcontainer = hg.DIV( *content, _class="bx--modal-content" + (" bx--modal-content--with-form" if with_form else ""), tabindex=0, ) super().__init__( hg.DIV( hg.DIV( hg.P( label, _class="bx--modal-header__label", ), hg.P( heading, _class="bx--modal-header__heading", ), hg.BUTTON( Icon( "close", size=16, _class="bx--modal-close__icon", aria_hidden="true", ), _class="bx--modal-close", type="button", data_modal_close=True, ), _class="bx--modal-header", ), self.contentcontainer, hg.DIV(_class="bx--modal-content--overflow-indicator"), hg.DIV( *buttons, _class="bx--modal-footer", ) if buttons else "", _class=f"bx--modal-container bx--modal-container--{size}", ), data_modal=True, id=self.id, role="dialog", aria_modal="true", tabindex="-1", **attributes, )