def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, **attributes, ): inputelement_attrs = inputelement_attrs or {} attrs = {} if boundfield: attrs["checked"] = hg.F( lambda c: hg.resolve_lazy(boundfield, c).field.widget. check_test(hg.resolve_lazy(boundfield, c).value())) inputelement_attrs = _combine_lazy_dict(inputelement_attrs, attrs) label = None if label is None else label.label super().__init__( self.get_input_element(inputelement_attrs, errors), hg.LABEL( hg.SPAN(_class="bx--radio-button__appearance"), hg.SPAN(label, _class="bx--radio-button__label-text"), _class="bx--radio-button__label", _for=inputelement_attrs.get("id"), ), help_text, errors, **hg.merge_html_attrs(attributes, {"_class": "bx--radio-button-wrapper"}), )
def resolve(self, context: dict): kwargs = { k: hg.resolve_lazy(v, context) for k, v in self.kwargs.items() } # the django reverse function requires url-keyword arguments to be pass # in a parameter named "kwarg". This is a bit confusing since kwargs # normally referse to the python keyword arguments and not to URL # keyword arguments. However, we also want to support lazy URL # keywords, so we do the resolving of the actualy URL-kwargs as well if "kwargs" in kwargs: kwargs["kwargs"] = { k: hg.resolve_lazy(v, context) for k, v in kwargs["kwargs"].items() } if "args" in kwargs: kwargs["args"] = [ hg.resolve_lazy(arg, context) for arg in kwargs["args"] ] if "query" in kwargs: kwargs["query"] = { k: hg.resolve_lazy(v, context) for k, v in kwargs["query"].items() } return urlreverse(*[hg.resolve_lazy(a, context) for a in self.args], **kwargs)
def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, backend=None, **attributes, ): """ :param SearchBackendConfig backend: Where and how to get search results """ inputelement_attrs = inputelement_attrs or {} # This works inside a formset. Might need to be changed for other usages. widget_id = inputelement_attrs.get("id") resultcontainerid = hg.format("search-result-{}", widget_id) tag_id = hg.format("{}-tag", widget_id) super().__init__( label, Tag( hg.F(lambda c: hg.resolve_lazy(boundfield, c).field.to_python( hg.resolve_lazy(boundfield, c).value())) if boundfield else "", id=tag_id, style=hg.If( inputelement_attrs.get("value"), hg.BaseElement(""), hg.BaseElement("display: none;"), ), onclick="return false;", ), self.get_input_element(inputelement_attrs, errors, type="hidden"), Search( backend=backend, resultcontainerid=resultcontainerid, resultcontainer_onload_js=_resultcontainer_onload_js( backend, resultcontainerid, tag_id, widget_id), size="lg", disabled=inputelement_attrs.get("disabled", False), widgetattributes={"id": hg.format("search__{}", widget_id)}, ), help_text, errors, **hg.merge_html_attrs(attributes, {"_class": "bx--text-input-wrapper"}), )
def resolve(self, context): object = self.object if isinstance(self.object, str): object = resolve_modellookup(context, self.object)[0] object = hg.resolve_lazy(object, context) parts = self.fieldname.split(".") # test if the value has a matching get_FIELDNAME_display function try: value = hg.resolve_lookup( object, f"{'.'.join(parts[:-1])}.get_{parts[-1]}_display".lstrip(".") ) except Exception: value = None if value is None: try: value = hg.resolve_lookup(object, self.fieldname) except AttributeError: # e.g. for non-existing OneToOneField related value pass if isinstance(value, datetime.datetime): value = localtime(value) if self.formatter: value = self.formatter(value) value = localize(value, use_l10n=settings.USE_L10N) if isinstance(value, models.Manager): value = ", ".join([str(x) for x in value.all()]) if isinstance(value, str): value = linebreaksbr(value) return value
def _optgroups_from_choices(optchoices, name, value): groups = [] for index, (option_value, option_label) in enumerate(optchoices): if option_value is None: option_value = "" subgroup = [] if isinstance(option_label, (list, tuple)): group_name = option_value subindex = 0 choices = option_label else: group_name = None subindex = None choices = [(option_value, option_label)] groups.append((group_name, subgroup, index)) for subvalue, sublabel in choices: selected = hg.F(lambda c, v=subvalue: hg.resolve_lazy(v, c) == hg. resolve_lazy(value, c)) subgroup.append({ "name": name, "value": subvalue, "label": sublabel, "selected": selected, "attrs": { "selected": selected, }, }) if subindex is not None: subindex += 1 return groups
def wrapper_func(context): _classlist = [] for _class in _classes: _classlist.append(_class) _classlist.append(" ") ret = hg.resolve_lazy(lazy_attrs, context) or {} ret["_class"] = hg.BaseElement(ret.get("_class", ""), " ", *_classlist) return ret
def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, **attributes, ): inputelement_attrs = inputelement_attrs or {} attrs = {} if boundfield: attrs["checked"] = hg.F( lambda c: hg.resolve_lazy(boundfield, c).field.widget. check_test(hg.resolve_lazy(boundfield, c).value())) attrs["value"] = None inputelement_attrs = _combine_lazy_dict(inputelement_attrs, attrs) # labels for checkboxes are treated a bit different, need to use plain value label = hg.F(lambda c, label=label: getattr(hg.resolve_lazy(label, c), "label", label)) required = hg.F( lambda c, label=label: hg.resolve_lazy(label, c) is not None) super().__init__( hg.LABEL( self.get_input_element(inputelement_attrs, errors), label, hg.If(inputelement_attrs.get("required"), hg.If(required, REQUIRED_LABEL)), _class=hg.BaseElement( "bx--checkbox-label", hg.If(inputelement_attrs.get("disabled"), " bx--label--disabled"), ), data_contained_checkbox_state=hg.If( inputelement_attrs.get("checked"), "true", "false", ), data_invalid=hg.If(getattr(errors, "condition", False), True), ), help_text, errors, **hg.merge_html_attrs(attributes, {"_class": "bx--checkbox-wrapper"}), )
def resolve(self, context): object = self.object if isinstance(self.object, str): object = resolve_modellookup(context, self.object)[0] object = hg.resolve_lazy(object, context) label = resolve_modellookup(object._meta.model, self.fieldname)[-1] if hasattr(label, "verbose_name"): return label.verbose_name if isinstance(label, property): return label.fget.__name__.replace("_", " ") if callable(label): return label.__name__.replace("_", " ") if isinstance(label, ManyToOneRel): return ( label.related_model._meta.verbose_name_plural ) # this is "more correct", but not sure if it always works.. # return label.name.replace("_", " ").capitalize() return label.title() if self.title and isinstance(label, str) else label
def buildattribs(context): realform = hg.resolve_lazy(form, context) id = None if realform[fieldname].auto_id and "id" not in orig_inputattribs: id = (realform[fieldname].html_initial_id if show_hidden_initial else realform[fieldname].auto_id) return { "id": id, "name": realform[fieldname].html_initial_name if show_hidden_initial else realform[fieldname].html_name, "value": realform[fieldname].value(), **realform[fieldname].build_widget_attrs({}), **realform[fieldname].field.widget.attrs, **orig_inputattribs, }
def __init__(self, model: Union[models.Model, hg.Lazy], name: str, *args, return_to_current: bool = False, **kwargs): # if this is an instance of a model, we can extract the pk URL argument directly # TODO: instance-specific routes which don't use the pk argument will fail if isinstance(model, hg.Lazy): url = hg.F( lambda c: model_urlname(hg.resolve_lazy(model, c), name)) else: url = model_urlname(model, name) if return_to_current: if "query" not in kwargs: kwargs["query"] = {} kwargs["query"]["next"] = hg.C("request.get_full_path") super().__init__(url, *args, **kwargs)
def render(self, context): form = htmlgenerator.resolve_lazy(self.form, self, context) for formfield in self.formfieldelements(): formfield.form = form for error in form.non_field_errors(): self.insert( 0, InlineNotification(_("Form error"), error, kind="error")) for hidden in form.hidden_fields(): for error in hidden.errors: self.insert( 0, InlineNotification(_("Form error: "), hidden.name, error, kind="error"), ) if self.standalone: if form.is_multipart() and "enctype" not in self.attributes: self.attributes["enctype"] = "multipart/form-data" return super().render(context) return super().render_children(context)
def __init__( self, name, size=None, **attributes, ): if Icon.ICONS is None: Icon.ICONS = loadicons(RAW_ICON_BASE_PATH) attributes["viewBox"] = "0 0 32 32" attributes["preserveAspectRatio"] = "xMidYMid meet" attributes["focusable"] = "false" attributes["style"] = attributes.get("style", "") + " will-change: transform;" if size is None: attributes["width"] = "32" attributes["height"] = "32" else: attributes["width"] = size attributes["height"] = size self.name = name super().__init__(hg.F(lambda c: Icon.ICONS[hg.resolve_lazy(name, c)]), **attributes)
def __init__( self, *children, buttontype="primary", icon=None, notext=False, small=False, **attributes, ): attributes["type"] = attributes.get("type", "button") attributes["tabindex"] = attributes.get("tabindex", "0") attributes["_class"] = hg.BaseElement( attributes.get("_class", ""), f" bx--btn bx--btn--{buttontype}", hg.If( hg.F(lambda c: hg.resolve_lazy( self.attributes.get("disabled", False), c)), " bx--btn--disabled", ), ) if small: attributes["_class"] += " bx--btn--sm " if notext or not children: attributes["_class"] += " bx--btn--icon-only" if children: attributes["_class"] += ( " bx--btn--icon-only bx--tooltip__trigger bx--tooltip--a11y " "bx--tooltip--bottom bx--tooltip--align-center") children = (hg.SPAN(*children, _class="bx--assistive-text"), ) if icon is not None: if isinstance(icon, str): icon = Icon(icon) if isinstance(icon, Icon): icon.attributes["_class"] = ( icon.attributes.get("_class", "") + " bx--btn__icon") children += (icon, ) super().__init__(*children, **attributes)
def wrapper(context): realform = hg.resolve_lazy(form, context) widgetclass = type(realform[fieldname].field.widget) fieldclass = type(realform[fieldname].field) # Hidden widgets have highest priority if issubclass(widgetclass, forms.HiddenInput): return HiddenInput # Manually passed widgets have second priority if suggested_widgetclass is not None: return suggested_widgetclass # Automated detection via django-bread-widget-mapp have lowest priority if fieldclass in widget_map: return widget_map[fieldclass][0] if widgetclass in widget_map: return widget_map[widgetclass][0] # Fallback for unknown widgets warnings.warn( f"Form field {type(realform).__name__}.{fieldname} ({fieldclass}) uses widget {widgetclass} but " "bread has no implementation, default to TextInput") return TextInput
def __init__( self, label=None, help_text=None, errors=None, inputelement_attrs=None, boundfield=None, style_short=False, style_simple=False, **attributes, ): inputelement_attrs = inputelement_attrs or {} def format_date_value(context): bfield = hg.resolve_lazy(boundfield, context) return bfield.field.widget.format_value(bfield.value()) super().__init__( hg.DIV( label, hg.If( style_simple, self.get_input_element( inputelement_attrs, errors, data_invalid=hg.If(getattr(errors, "condition"), True), pattern=hg.F(lambda c: (TimeRE().compile( hg.resolve_lazy(boundfield, c).field.widget.format or formats.get_format( hg.resolve_lazy(boundfield, c).field.widget. format_key)[0]).pattern)), value=hg.F(format_date_value), ), hg.DIV( self.get_input_element( inputelement_attrs, errors, data_date_picker_input=True, data_invalid=hg.If(getattr(errors, "condition"), True), data_date_format=hg.F(lambda c: to_php_formatstr( hg.resolve_lazy(boundfield, c).field.widget. format, hg.resolve_lazy(boundfield, c ).field.widget.format_key, )), value=hg.F(format_date_value), ), Icon( "calendar", size=16, _class="bx--date-picker__icon", data_date_picker_icon="true", ), _class="bx--date-picker-input__wrapper", ), ), help_text, errors, _class="bx--date-picker-container", ), **hg.merge_html_attrs( attributes, { "data_date_picker": not style_simple, "data_date_picker_type": None if style_simple else "single", "_class": hg.BaseElement( "bx--date-picker", hg.If( style_simple, " bx--date-picker--simple", " bx--date-picker--single", ), hg.If(style_short, " bx--date-picker--short"), ), }, ), )
def generate_widget_element( fieldname: str = None, # required to derive the widget from a django form field form: Union[ forms.Form, hg.Lazy, str] = DEFAULT_FORM_CONTEXTNAME, # required to derive the widget from a django form field no_wrapper: bool = False, # wrapper produces less dense layout, from carbon styles no_label: bool = False, no_helptext: bool = False, show_hidden_initial: bool = False, # required in special cases to add an initial value # # # -------------------------------------------------------------------------- # parameters which are normally not required, when using a django form field # but can be filled in to create form fields independently from django form fields or # manually overriding values from the form field widgetclass: Optional[Union[ Type[BaseWidget], hg. Lazy]] = None, # normally be taken from the django form field, will be carbon-ized label: Union[ str, hg. BaseElement] = None, # normally be taken from the django form field, will be carbon-ized help_text: Union[ str, hg. BaseElement] = None, # normally be taken from the django form field, will be carbon-ized errors: Optional[List[ str]] = None, # normally be taken from the django form field, will be carbon-ized inputelement_attrs: Optional[Union[ dict, hg. Lazy]] = None, # normally be taken from the django form field, will be carbon-ized **attributes, ) -> FormFieldMarker: """ Function to produce a carbon design based form field widget which is compatible with Django forms and based on htmlgenerator. """ hidden = None if show_hidden_initial: hidden = generate_widget_element( fieldname=fieldname, form=form, inputelement_attrs=inputelement_attrs, widgetclass=HiddenInput, no_wrapper=True, no_label=True, no_helptext=True, show_hidden_initial=False, **attributes, ) inputelement_attrs = inputelement_attrs or {} boundfield = None # warnings for deprecated API usage if "widgetattributes" in attributes: warnings.warn( "FormField does no longer support the parameter 'widgetattributes'. " "The parameter 'inputelement_attrs' serves the same purpose'") if "elementattributes" in attributes: warnings.warn( "FormField does no longer support the parameter 'elementattributes'. " "attributes can now be directly passed as kwargs.") # check if this field will be used with a django form if yes, derive the # according values lazyly from the context if fieldname is not None and form is not None: if isinstance(form, str): form = hg.C(form) label = label or form[fieldname].label help_text = help_text or form.fields[fieldname].help_text errors = errors or form[fieldname].errors # do this to preserve the original inputelement_attrs in the # buildattribs scope orig_inputattribs = inputelement_attrs def buildattribs(context): realform = hg.resolve_lazy(form, context) id = None if realform[fieldname].auto_id and "id" not in orig_inputattribs: id = (realform[fieldname].html_initial_id if show_hidden_initial else realform[fieldname].auto_id) return { "id": id, "name": realform[fieldname].html_initial_name if show_hidden_initial else realform[fieldname].html_name, "value": realform[fieldname].value(), **realform[fieldname].build_widget_attrs({}), **realform[fieldname].field.widget.attrs, **orig_inputattribs, } inputelement_attrs = hg.F(buildattribs) labelfor = form[fieldname].id_for_label boundfield = form[fieldname] else: labelfor = inputelement_attrs.get("id") # helper elements label = Label( label, required=inputelement_attrs.get("required"), disabled=inputelement_attrs.get("disabled"), _for=labelfor, ) help_text = HelpText(help_text, disabled=inputelement_attrs.get("disabled")) errors = ErrorList(errors) # instantiate field (might create a lazy element when using _guess_widget) widgetclass = _guess_widget(fieldname, form, widgetclass) ret = widgetclass( label=None if no_label else label, help_text=None if no_helptext else help_text, errors=errors, inputelement_attrs=inputelement_attrs, boundfield=boundfield, **attributes, ) if show_hidden_initial: ret = hg.BaseElement(ret, hidden) if not no_wrapper: ret = hg.If( hg.F(lambda c: isinstance( hg.resolve_lazy(boundfield, c).field.widget, forms.HiddenInput) ), ret, ret.with_fieldwrapper(), ) return FormFieldMarker(fieldname, ret)
def render(self, context): steps = hg.resolve_lazy(self.steps, context) self.extend((ProgressStep(label, status) for label, status in steps)) return super().render(context)
def _combine_lazy_dict(attrs1, attrs2): return hg.F( lambda c: { **(hg.resolve_lazy(attrs1, c) or {}), **(hg.resolve_lazy(attrs2, c) or {}), })
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 format_date_value(context): bfield = hg.resolve_lazy(boundfield, context) return bfield.field.widget.format_value(bfield.value())
def introspections(context): return json.dumps(DjangoQLSchemaSerializer().serialize( DjangoQLSchema( hg.resolve_lazy(boundfield, context).value().queryset.model)))
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, 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, 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 wrapper(context): bfield = hg.resolve_lazy(boundfield, context) return bfield.field.widget.optgroups( bfield.name, bfield.field.widget.format_value(bfield.value()), )
def from_queryset( queryset, # column behaviour columns: Iterable[Union[str, "DataTableColumn"]] = (), prevent_automatic_sortingnames=False, # row behaviour rowvariable="row", rowactions: Iterable[Link] = (), rowactions_dropdown=False, rowclickaction=None, # bulkaction behaviour bulkactions: Iterable[Link] = (), checkbox_for_bulkaction_name="_selected", # toolbar configuration title=None, primary_button: Optional[Button] = None, settingspanel: Any = None, pagination_config: Optional[PaginationConfig] = None, search_urlparameter: Optional[str] = None, model=None, # required if queryset is Lazy **kwargs, ): """TODO: Write Docs!!!! Yeah yeah, on it already... :param settingspanel: A panel which will be opened when clicking on the "Settings" button of the datatable, usefull e.g. for showing filter options. Currently only one button and one panel are supported. More buttons and panels could be interesting but may to over- engineered because it is a rare case and it is not difficutl to add another button by modifying the datatable after creation. """ if not isinstance(queryset, hg.Lazy): model = queryset.model if model is None: raise ValueError( "Argument for 'model' must be given if 'queryset' is of type hg.Lazy" ) columns = columns or filter_fieldlist(model, ["__all__"]) title = title or pretty_modelname(model, plural=True) if primary_button is None: primary_button = Button.from_link( Link( href=ModelHref(model, "add"), label=_("Add %s") % pretty_modelname(model), permissions=[ f"{model._meta.app_label}.add_{model._meta.model_name}" ], ), icon=Icon("add", size=20), ) if rowactions_dropdown: objectactions_menu: hg.HTMLElement = OverflowMenu( rowactions, flip=True, item_attributes={"_class": "bx--table-row--menu-option"}, ) else: objectactions_menu = hg.DIV( hg.Iterator( rowactions, "link", hg.F(lambda c: Button.from_link( c["link"], notext=True, small=True, buttontype="ghost", _class="bx--overflow-menu", ) if isinstance(c["link"], Link) else c["link"]), ), style="display: flex; justify-content: flex-end;", ) column_definitions: List[DataTableColumn] = [] for col in columns: if not (isinstance(col, DataTableColumn) or isinstance(col, str)): raise ValueError( f"Argument 'columns' needs to be of a List[str] or a List[DataTableColumn], but found {col}" ) td_attributes: Optional[dict] = None if rowclickaction and getattr(col, "enable_row_click", True): assert isinstance(rowclickaction, Link), "rowclickaction must be of type Link" td_attributes = { **aslink_attributes(rowclickaction.href), **(rowclickaction.attributes or {}), } # convert simple string (modelfield) to column definition if isinstance(col, str): col = DataTableColumn.from_modelfield( col, model, prevent_automatic_sortingnames, rowvariable, td_attributes=td_attributes, ) else: if td_attributes: col = col._replace( td_attributes=td_attributes) # type: ignore column_definitions.append(col) return DataTable( column_definitions + ([ DataTableColumn( "", objectactions_menu, td_attributes=hg.F( lambda c: { "_class": "bx--table-column-menu" if rowactions_dropdown else "" }), th_attributes=hg.F( lambda c: {"_class": "bx--table-column-menu"}), ) ] if rowactions else []), # querysets are cached, the call to all will make sure a new query is used in every request hg.F(lambda c: queryset), **kwargs, ).with_toolbar( title, helper_text=hg.format( "{} {}", hg.F(lambda c: len(hg.resolve_lazy(queryset, c)) if pagination_config is None else pagination_config. paginator.count), model._meta.verbose_name_plural, ), primary_button=primary_button, bulkactions=bulkactions, pagination_config=pagination_config, checkbox_for_bulkaction_name=checkbox_for_bulkaction_name, search_urlparameter=search_urlparameter, settingspanel=settingspanel, )
def countselected(context): options = [ o for og in hg.resolve_lazy(optgroups, context) for o in og[1] ] return len([o for o in options if o and o["selected"]])