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"}),
     )
Beispiel #2
0
 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)
Beispiel #3
0
    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"}),
        )
Beispiel #4
0
    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"}),
     )
Beispiel #8
0
 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,
     }
Beispiel #10
0
    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)
Beispiel #11
0
 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)
Beispiel #12
0
 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)
Beispiel #13
0
    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)
Beispiel #14
0
    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"),
                    ),
                },
            ),
        )
Beispiel #16
0
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()),
     )
Beispiel #26
0
    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"]])