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"))
Пример #2
0
 def get_layout(self):
     self.checkboxcounterid = hg.html_id(self, "checkbox-counter")
     ret = super().get_layout()
     toolbar = list(
         ret.filter(lambda e, a: getattr(e, "attributes", {}).get(
             "_class", "") == "bx--toolbar-content"))[0]
     nfilters = self._checkbox_count()
     toolbar.insert(
         -2,
         hg.DIV(
             hg.SPAN(nfilters, id=self.checkboxcounterid),
             layout.icon.Icon(
                 "close",
                 focusable="false",
                 size=15,
                 role="img",
                 onclick=
                 f"document.location = '{self.request.path}?reset=1'",
             ),
             role="button",
             _class=
             "bx--list-box__selection bx--list-box__selection--multi bx--tag--filter",
             style="margin: auto 0.5rem;" +
             (" display: none;" if nfilters == 0 else ""),
             tabindex="0",
             title=("Reset"),
         ),
     )
     return ret
Пример #3
0
    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,
        )
Пример #4
0
    def __init__(
        self,
        size="xl",
        placeholder=None,
        widgetattributes=None,
        backend=None,
        resultcontainerid=None,
        show_result_container=True,
        resultcontainer_onload_js=None,
        disabled=False,
        **kwargs,
    ):
        """
        :param SearchBackendConfig backend: Where and how to get search results
        """
        kwargs["_class"] = kwargs.get("_class", "") + f" bx--search bx--search--{size}"
        kwargs["data_search"] = True
        kwargs["role"] = "search"
        width = kwargs.get("width", None)
        if width:
            kwargs["style"] = kwargs.get("style", "") + f"width:{width};"

        widgetattributes = {
            "id": "search__" + hg.html_id(self),
            "_class": "bx--search-input",
            "type": "text",
            "placeholder": placeholder or _("Search"),
            "autocomplete": "off",
            **(widgetattributes or {}),
        }
        if backend:
            if resultcontainerid is None:
                resultcontainerid = f"search-result-{hg.html_id((self, backend.url))}"
            widgetattributes["hx_get"] = backend.url
            widgetattributes["hx_trigger"] = "changed, click, keyup changed delay:500ms"
            widgetattributes["hx_target"] = hg.format("#{}", resultcontainerid)
            widgetattributes["hx_indicator"] = hg.format(
                "#{}-indicator", resultcontainerid
            )
            widgetattributes["name"] = backend.query_parameter

        self.close_button = _close_button(resultcontainerid, widgetattributes)

        super().__init__(
            hg.DIV(
                hg.LABEL(_("Search"), _class="bx--label", _for=widgetattributes["id"]),
                hg.INPUT(**widgetattributes),
                _search_icon(),
                self.close_button,
                hg.If(backend is not None, _loading_indicator(resultcontainerid)),
                **kwargs,
            ),
            hg.If(
                backend is not None and show_result_container,
                _result_container(resultcontainerid, resultcontainer_onload_js, width),
            ),
            style=hg.If(disabled, hg.BaseElement("display: none")),
        )
Пример #5
0
    def __init__(
        self,
        label: Any,
        description: Any,
        align: str = "center",
        position: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        label : Any
            Text to be displayed
        description : Any
            One line of string defining the label
        align : str, optional
            Specify where the arrow pointing the icon should align to the text.
            It can be either start, center, or end.
            The default value is 'center'.
        position : str
            Where should the tooltip appear besides the tooltip icon.
            It can be either top, left, right, or bottom.
            The default value is 'bottom'.
        """
        tooltip_attributes = {
            "_class": "bx--tooltip--definition bx--tooltip--a11y ",
            "data_tooltip_definition": True,
        }

        tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes)
        asst_txt_id = hg.html_id(self, "bx--tooltip--definition-id")

        super().__init__(
            hg.DIV(
                hg.DIV(
                    label,
                    _class=(
                        "bx--tooltip__trigger "
                        "bx--tooltip--a11y "
                        "bx--tooltip__trigger--definition "
                        "bx--tooltip--%s "
                        "bx--tooltip--align-%s"
                    )
                    % (position, align),
                    aria_describedby=asst_txt_id,
                ),
                hg.DIV(
                    description,
                    _class="bx--assistive-text",
                    id=asst_txt_id,
                    role="tooltip",
                ),
                **tooltip_attributes,
            )
        )
Пример #6
0
 def as_plain(*args, add_label=_("Add"), **kwargs):
     """Shortcut to render a complete formset with add-button"""
     formset = FormsetField(*args, **kwargs)
     id = hg.html_id(formset, prefix="formset-")
     return hg.BaseElement(
         hg.DIV(formset, id=id),
         formset.management_form,
         formset.add_button(
             buttontype="ghost",
             notext=False,
             label=add_label,
             container_css_selector=f"#{id}",
         ),
     )
Пример #7
0
 def __init__(
     self,
     label,
     offlabel=_("Off"),
     onlabel=_("On"),
     help_text=None,
     errors=None,
     disabled=None,
     required=None,
     widgetattributes={},
     **attributes,
 ):
     attributes["_class"] = attributes.get("_class", "") + " bx--form-item"
     widgetattributes["_class"] = (widgetattributes.get("_class", "") +
                                   " bx--toggle-input")
     widgetattributes["type"] = "checkbox"
     widgetattributes["id"] = widgetattributes.get("id",
                                                   None) or hg.html_id(self)
     self.input = hg.INPUT(**widgetattributes)
     self.label = hg.LABEL(
         label,
         hg.If(required, REQUIRED_LABEL),
         hg.SPAN(
             hg.SPAN(offlabel,
                     _class="bx--toggle__text--off",
                     aria_hidden="true"),
             hg.SPAN(onlabel,
                     _class="bx--toggle__text--on",
                     aria_hidden="true"),
             _class="bx--toggle__switch",
         ),
         _class=hg.BaseElement(
             "bx--label bx--toggle-input__label",
             hg.If(disabled, " bx--label--disabled"),
         ),
         _for=widgetattributes["id"],
     )
     super().__init__(
         self.input,
         self.label,
         HelpText(help_text),
         ErrorList(errors),
         **attributes,
     )
Пример #8
0
    def __init__(self,
                 label,
                 status,
                 optional=False,
                 tooltip=None,
                 disabled=False,
                 **kwargs):
        assert (status in ProgressStep.STATUS
                ), f"{status} must be one of {ProgressStep.STATUS}"
        kwargs["class"] = (kwargs.get("class", "") +
                           f" bx--progress-step bx--progress-step--{status}")
        if disabled:
            kwargs["aria_disabled"] = "true"
            kwargs["class"] += "  bx--progress-step--disabled"
        elements = [
            Icon(ProgressStep.STATUS[status], size=16),
            hg.P(label, tabindex=0, _class="bx--progress-label"),
            hg.SPAN(_class="bx--progress-line"),
        ]

        if optional:
            elements.insert(
                2, hg.P(_("Optional"), _class="bx--progress-optional"))

        if tooltip is not None:
            tooltipid = hg.html_id(tooltip, "tooltip-label")
            elements[1]["aria-describedby"] = tooltipid
            elements.insert(
                2,
                hg.DIV(
                    hg.SPAN(_class="bx--tooltip__caret"),
                    hg.P(tooltip, _class="bx--tooltip__text"),
                    id=tooltipid,
                    role="tooltip",
                    data_floating_menu_direction="bottom",
                    _class="bx--tooltip",
                    data_avoid_focus_on_open=True,
                ),
            )

        super().__init__(*elements, **kwargs)
Пример #9
0
    def __init__(
            self,
            label=None,
            help_text=None,
            errors=None,
            inputelement_attrs=None,
            boundfield=None,  # for django-form select elements use this
            choices=None,  # for non-django-form select elements use this
            **attributes,  # for non-django-form select elements use this
    ):
        inputelement_attrs = inputelement_attrs or {}
        optgroups = (_optgroups_from_choices(
            choices,
            name=inputelement_attrs.get("name"),
            value=inputelement_attrs.get("value"),
        ) if choices else _gen_optgroup(boundfield))

        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"]])

        searchfieldid = hg.html_id(self)
        super().__init__(
            label,
            hg.If(
                inputelement_attrs.get("disabled"),
                hg.DIV(
                    hg.Iterator(
                        optgroups,
                        "optiongroup",
                        hg.Iterator(
                            hg.C("optiongroup.1"),
                            "option",
                            hg.If(hg.C("option.selected"),
                                  Tag(hg.C("option.label"))),
                        ),
                    )),
                hg.DIV(
                    hg.DIV(
                        hg.DIV(
                            hg.F(countselected),
                            Icon(
                                "close",
                                focusable="false",
                                size=15,
                                role="img",
                                onclick=
                                "clearMultiselect(this.parentElement.parentElement.parentElement)",
                            ),
                            role="button",
                            _class=
                            "bx--list-box__selection bx--list-box__selection--multi bx--tag--filter",
                            tabindex="0",
                            title="Clear all selected items",
                        ),
                        hg.INPUT(
                            id=searchfieldid,
                            _class="bx--text-input",
                            placeholder="Filter...",
                            onclick=
                            "this.parentElement.nextElementSibling.style.display = 'block'",
                            onkeyup=
                            "filterOptions(this.parentElement.parentElement)",
                        ),
                        hg.DIV(
                            Icon("chevron--down",
                                 size=16,
                                 role="img",
                                 focusable="false"),
                            _class="bx--list-box__menu-icon",
                            onclick=
                            "this.parentElement.nextElementSibling.style.display = this.parentElement.nextElementSibling.style.display == 'none' ? 'block' : 'none';",
                        ),
                        role="button",
                        _class="bx--list-box__field",
                        tabindex="0",
                        onload=
                        "window.addEventListener('click', (e) => {this.nextElementSibling.style.display = 'none'})",
                    ),
                    hg.FIELDSET(
                        hg.Iterator(
                            optgroups,
                            "optgroup",
                            hg.Iterator(
                                hg.C("optgroup.1"),
                                "option",
                                hg.DIV(
                                    hg.DIV(
                                        hg.DIV(
                                            hg.LABEL(
                                                hg.INPUT(
                                                    type="checkbox",
                                                    readonly=True,
                                                    _class="bx--checkbox",
                                                    value=hg.C("option.value"),
                                                    lazy_attributes=hg.C(
                                                        "option.attrs"),
                                                    onchange=
                                                    "updateMultiselect(this.closest('.bx--multi-select'))",
                                                    checked=hg.C(
                                                        "option.selected"),
                                                    name=hg.C("option.name"),
                                                ),
                                                hg.SPAN(
                                                    _class=
                                                    "bx--checkbox-appearance"),
                                                hg.SPAN(
                                                    hg.C("option.label"),
                                                    _class=
                                                    "bx--checkbox-label-text",
                                                ),
                                                title=hg.C("option.label"),
                                                _class="bx--checkbox-label",
                                            ),
                                            _class=
                                            "bx--form-item bx--checkbox-wrapper",
                                        ),
                                        _class=
                                        "bx--list-box__menu-item__option",
                                    ),
                                    _class="bx--list-box__menu-item",
                                ),
                            ),
                        ),
                        _class="bx--list-box__menu",
                        role="listbox",
                        style="display: none",
                    ),
                    _class=hg.BaseElement(
                        "bx--multi-select bx--list-box bx--multi-select--selected bx--combo-box bx--multi-select--filterable",
                        hg.If(
                            inputelement_attrs.get("disabled"),
                            " bx--list-box--disabled",
                        ),
                    ),
                    data_invalid=hg.If(getattr(errors, "condition", None),
                                       True),
                ),
            ),
            help_text,
            errors,
            **hg.merge_html_attrs(
                attributes,
                {
                    "onclick": "event.stopPropagation()",
                    "_class": "bx--list-box__wrapper",
                },
            ),
        )
Пример #10
0
    def __init__(
        self,
        label: Any,
        body: Any,
        heading: Optional[Any] = None,
        link: Optional[Link] = None,
        button: Optional[Button] = None,
        icon: Union[Icon, str] = "information",
        menudirection: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        label : Any
            Label for a tooltip
        body : Any
            The content inside a tooltip
        heading : Any, optional
            A heading for a tooltip.
        link : Link, optional
            Bread's Link NamedTuple in case you want to bring users to a specific webpage.
        button : Button, optional
            Insert the Bread's Button onto the tooltip.
        icon : Icon, str, optional
            Specify an icon for the tooltip
            The default value is "information".
        menudirection : str
            Where should the tooltip appear besides the tooltip icon.
            It can be either top, left, right, or bottom.
            The default value is "bottom".
        """
        base_class = "bx--tooltip"

        footer_elements: typing.List[hg.BaseElement] = []
        if link:
            footer_elements.append(hg.A(link.label, href=link.href, _class="bx--link"))
        if button:
            footer_elements.append(button)

        base_id = hg.html_id(self, base_class + "-id")
        label_id = f"{base_id}-label"

        trigger_attributes = {
            "_class": base_class + "__trigger",
            "aria_controls": base_id,
            "aria_expanded": "false",
            "aria_haspopup": "true",
            "aria_labelledby": label_id,
            "data_tooltip_target": "#" + base_id,
            "data_tooltip_trigger": True,
        }

        tooltip_attributes = {
            "_class": base_class,
            "aria_hidden": "true",
            "data_floating_menu_direction": menudirection,
            "id": base_id,
        }

        tooltip_content_attributes = {
            "_class": base_class + "__content",
            "aria_describedby": base_id + "-body",
            "aria_labelledby": label_id,
            "role": "dialog",
            "tabindex": "-1",
        }

        tooltip_attributes = hg.merge_html_attrs(tooltip_attributes, attributes)
        icon = _get_icon(icon)

        super().__init__(
            # tooltip label
            hg.DIV(
                label,
                # cannot use bread's Button class because
                # only the class bx--tooltip__trigger can be used
                hg.DIV(
                    icon,
                    **trigger_attributes,
                ),
                id=label_id,
                _class=base_class + "__label",
            ),
            # the real tooltip goes here
            hg.DIV(
                hg.SPAN(_class=base_class + "__caret"),
                hg.DIV(
                    hg.If(
                        bool(heading),
                        hg.H4(
                            heading,
                            id=base_id + "-heading",
                            _class=base_class + "__heading",
                        ),
                    ),
                    hg.P(body, id=base_id + "-body"),
                    hg.If(
                        len(footer_elements) > 0,
                        hg.DIV(
                            *footer_elements,
                            _class=base_class + "__footer",
                        ),
                    ),
                    **tooltip_content_attributes,
                ),
                hg.SPAN(tabindex="0"),
                **tooltip_attributes,
            ),
        )
 def test(self):
     self.assertNotEqual(hg.html_id(object()), hg.html_id(object()))
     o = object()
     self.assertNotEqual(hg.html_id(o), hg.html_id(o))
Пример #12
0
    def as_datatable(
        fieldname: str,
        fields: List,
        title: typing.Optional[str] = None,
        formname: str = "form",
        formsetfield_kwargs: dict = None,
        **kwargs,
    ) -> hg.BaseElement:
        from ..datatable import DataTable, DataTableColumn
        """
        :param str fieldname: The fieldname which should be used for an
                              formset, in general a one-to-many or many-to-many field
        :param list fields: A list of strings or objects. Strings are converted
                            to DataTableColumn, objects are passed on as they are
        :param str title: Datatable title, automatically generated from form if None
        :param str formname: Name of the surounding django-form object in the context
        :param dict formsetfield_kwargs: Arguments to be passed to the FormSetField constructor
        :param kwargs: Arguments to be passed to the DataTable constructor
        :return: A datatable with inline-editing capabilities
        :rtype: hg.HTMLElement
        """

        columns = []
        for f in fields:
            if isinstance(f, str):
                f = FormField(f,
                              no_wrapper=True,
                              no_label=True,
                              no_helptext=True)
            if isinstance(f, FormFieldMarker):
                f = DataTableColumn(
                    hg.BaseElement(
                        hg.
                        C(f"{formname}.{fieldname}.formset.form.base_fields.{f.fieldname}.label"
                          ),
                        HelpText(
                            hg.
                            C(f"{formname}.{fieldname}.formset.form.base_fields."
                              f"{f.fieldname}.help_text")),
                    ),
                    f,
                )
            columns.append(f)
        columns.append(
            DataTableColumn(
                hg.If(
                    hg.C(f"{formname}.{fieldname}.formset.can_order"),
                    _("Order"),
                ),
                hg.If(
                    hg.C(f"{formname}.{fieldname}.formset.can_order"),
                    FormField(
                        forms.formsets.ORDERING_FIELD_NAME,
                        no_wrapper=True,
                        no_label=True,
                    ),
                ),
            ))
        columns.append(
            DataTableColumn(
                "",
                hg.If(
                    hg.C(f"{formname}.{fieldname}.formset.can_delete"),
                    InlineDeleteButton(parentcontainerselector="tr"),
                ),
            ))

        formset = FormsetField(
            fieldname,
            DataTable.row(columns),
            formname=formname,
            **(formsetfield_kwargs or {}),
        )
        id = hg.html_id(formset, prefix="formset-")

        return hg.BaseElement(
            DataTable(
                row_iterator=formset,
                rowvariable="",
                columns=columns,
                id=id,
                **kwargs,
            ).with_toolbar(
                title=title or hg.C(f"{formname}.{fieldname}.label"),
                primary_button=formset.add_button(
                    buttontype="primary",
                    container_css_selector=f"#{id} tbody"),
            ),
            formset.management_form,
        )
Пример #13
0
    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,
        )