示例#1
0
 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 variable_size_header_part(platform, company, searchbar, searchbar_position):
    return hg.BaseElement(
        hg.A(
            logo(),
            platform,
            _class="bx--header__name",
            style="font-weight: 400",  # override carbon design
            href=hg.F(lambda c: c["request"].META["SCRIPT_NAME"] or "/"),
        ),
        hg.If(
            searchbar,
            hg.SPAN(
                searchbar,
                style=f"position: absolute; left: {searchbar_position}",
                _class="theme-gray-100",
            ),
            "",
        ),
        hg.A(
            hg.SPAN(
                company,
                style=hg.format(
                    "position: absolute; left: {}",
                    hg.If(searchbar, "50%", searchbar_position),
                ),
            ),
            _class="bx--header__name",
            style="font-weight: 400",  # override carbon design
            href=hg.F(lambda c: c["request"].META["SCRIPT_NAME"] or "/"),
        ),
    )
示例#3
0
def header():

    editbutton = breadlayout.button.Button(
        _("Edit"),
        buttontype="ghost",
        icon="edit",
        notext=True,
    ).as_href(ModelHref.from_object(hg.C("object"), "edit"))
    readbutton = breadlayout.button.Button(
        _("Read"),
        buttontype="ghost",
        icon="view",
        notext=True,
    ).as_href(ModelHref.from_object(hg.C("object"), "read"))

    deletebutton = breadlayout.button.Button(
        _("Delete"),
        buttontype="tertiary",
        icon="trash-can",
        notext=True,
        style="border-color: red; background-color: inherit",
    ).as_href(ModelHref.from_object(hg.C("object"), "delete"))
    deletebutton[1].attributes["style"] = "fill: red; color: red;"

    copybutton = breadlayout.button.Button(
        _("Copy"),
        buttontype="ghost",
        icon="copy",
        notext=True,
    ).as_href(ModelHref.from_object(hg.C("object"), "copy"))

    return hg.DIV(
        hg.H3(
            hg.If(
                hg.C("object"),
                hg.BaseElement(
                    hg.SPAN(hg.C("object")),
                    hg.SPAN(
                        hg.If(
                            hg.C("request").resolver_match.url_name.endswith(".read"),
                            editbutton,
                            readbutton,
                        ),
                        copybutton,
                        breadlayout.button.PrintPageButton(buttontype="ghost"),
                        deletebutton,
                        _class="no-print",
                        style="margin-bottom: 1rem; margin-left: 1rem",
                        width=3,
                    ),
                ),
                hg.SPAN(hg.format(_("Add {}"), hg.C("view").model._meta.verbose_name)),
            ),
        ),
        style="padding-top: 1rem",
    )
 def __init__(self, platform, company, searchbar, actions=(), *args, **kwargs):
     super().__init__(
         hg.If(
             HasBreadCookieValue("sidenav-hidden", "true"),
             variable_size_header_part(hg.BaseElement(), company, searchbar, "5rem"),
             variable_size_header_part(
                 hg.SPAN(platform, _class="bx--header__name--prefix"),
                 company,
                 searchbar,
                 "18rem",
             ),
         ),
         hg.DIV(
             hg.If(
                 hg.F(lambda c: c["request"].user.is_authenticated),
                 hg.A(
                     hg.SPAN(
                         hg.C("request.user.get_username"),
                         _class="bx--header__name--prefix",
                     ),
                     _class="bx--header__name",
                     href=reverse("userprofile"),
                     title=hg.C("request.user.get_username"),
                     style="padding: 0; margin-right: 1rem",
                 ),
             ),
             hg.If(
                 hg.F(lambda c: c["request"].user.is_authenticated),
                 hg.BUTTON(
                     Icon(
                         "logout",
                         size=20,
                         _class="bx--navigation-menu-panel-expand-icon",
                         aria_hidden="true",
                     ),
                     Icon(
                         "logout",
                         size=20,
                         _class="bx--navigation-menu-panel-collapse-icon",
                         aria_hidden="true",
                     ),
                     _class="bx--header__menu-trigger bx--header__action",
                     title=_("Logout"),
                     data_navigation_menu_panel_label_expand=_("Logout"),
                     data_navigation_menu_panel_label_collapse=_("Close"),
                     onclick=f"document.location = '{reverse('logout')}'",
                 ),
             ),
             _class="bx--header__global",
         ),
         _class="bx--header",
         data_header=True,
     )
 def asbutton(self):
     return hg.DIV(
         hg.SPAN(self.email, style="margin-right: 0.25rem"),
         hg.SPAN(style="flex-grow: 1"),
         button.Button(
             icon="email",
             onclick=f"window.location = 'mailto:{self.email}';",
             buttontype="ghost",
             _class="bx--overflow-menu",
         ),
         style="display: flex; flex-wrap: nowrap; align-items: center",
     )
示例#6
0
def editperson_toolbar(request):
    deletebutton = layout.button.Button(
        _("Delete"),
        buttontype="ghost",
        icon="trash-can",
        notext=True,
        **layout.aslink_attributes(
            hg.F(lambda c: layout.objectaction(c["object"], "delete"))),
    )
    restorebutton = layout.button.Button(
        _("Restore"),
        buttontype="ghost",
        icon="undo",
        notext=True,
        **layout.aslink_attributes(
            hg.F(lambda c: layout.objectaction(
                c["object"], "delete", query={"restore": True}))),
    )
    copybutton = layout.button.Button(
        _("Copy"),
        buttontype="ghost",
        icon="copy",
        notext=True,
        **layout.aslink_attributes(
            hg.F(lambda c: layout.objectaction(c["object"], "copy"))),
    )

    return hg.SPAN(
        hg.If(hg.C("object.deleted"), restorebutton, deletebutton),
        copybutton,
        layout.button.PrintPageButton(buttontype="ghost"),
        _class="no-print",
        style="margin-bottom: 1rem; margin-left: 1rem",
        width=3,
    )
示例#7
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
示例#8
0
文件: button.py 项目: tpokorra/bread
    def __init__(
        self,
        *children,
        buttontype="primary",
        disabled_func=lambda context: False,
        icon=None,
        notext=False,
        small=False,
        **attributes,
    ):
        self.disabled_func = disabled_func
        attributes["type"] = attributes.get("type", "button")
        attributes["tabindex"] = attributes.get("tabindex", "0")
        attributes["_class"] = (
            attributes.get("_class", "") + f" bx--btn bx--btn--{buttontype}"
        )
        if small:
            attributes["_class"] += " bx--btn--sm "
        if notext:
            attributes[
                "_class"
            ] += " bx--btn--icon-only bx--tooltip__trigger bx--tooltip--a11y bx--tooltip--bottom bx--tooltip--align-center"
            children = (htmlgenerator.SPAN(*children, _class="bx--assistive-text"),)

        if icon:
            icon.attributes["_class"] = (
                icon.attributes.get("_class", "") + " bx--btn__icon"
            )
            children += (icon,)
        super().__init__(*children, **attributes)
示例#9
0
    def full(title, datatable, primary_button, helper_text=None):
        header = [hg.H4(title)]
        if helper_text:
            header.append(
                hg.P(helper_text, _class="bx--data-table-header__description"))

        return hg.DIV(
            hg.DIV(*header, _class="bx--data-table-header"),
            hg.SECTION(
                hg.DIV(
                    hg.DIV(_class="bx--action-list"),
                    hg.DIV(
                        hg.P(
                            hg.SPAN(0, data_items_selected=True),
                            _(" items selected"),
                            _class="bx--batch-summary__para",
                        ),
                        _class="bx--batch-summary",
                    ),
                    _class="bx--batch-actions",
                    aria_label=_("Table Action Bar"),
                ),
                hg.DIV(
                    hg.DIV(Search(),
                           _class="bx--toolbar-search-container-expandable"),
                    primary_button,
                    _class="bx--toolbar-content",
                ),
                _class="bx--table-toolbar",
            ),
            datatable,
            _class="bx--data-table-container",
            data_table=True,
        )
示例#10
0
    def __init__(self, *labels, selected=0, **wrapperkwargs):
        """
        labels: tuples in the form (label, button-attributes)
                button-attributes will be applied to the generated button for a context label
                data_target can be a CSS-selector of a panel for the according context
        """

        wrapperkwargs["_class"] = (wrapperkwargs.get("_class", "") +
                                   " bx--content-switcher")
        super().__init__(
            *[
                hg.BUTTON(
                    hg.SPAN(label, _class="bx--content-switcher__label"),
                    _class="bx--content-switcher-btn" +
                    (" bx--content-switcher--selected"
                     if i == selected else ""),
                    role="tab",
                    type="button",
                    **kwargs,
                ) for i, (label, kwargs) in enumerate(labels)
            ],
            data_content_switcher=True,
            role="tablist",
            **wrapperkwargs,
        )
    def __init__(
        self,
        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"))
示例#12
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,
     )
示例#13
0
def edit_postal_button(modal):
    return hg.SPAN(
        layout.button.Button(
            "",
            buttontype="ghost",
            icon="edit",
            **modal.openerattributes,
        ),
        modal,
    )
示例#14
0
    def __init__(
        self,
        item_iterator,
        iteratorclass=htmlgenerator.Iterator,
        menuname=None,
        direction="bottom",
        flip=False,
        item_attributes={},
        **attributes,
    ):
        # making the class inline seems better, I think we can enforce scoping the type to this instance of OverflowMenu
        class MenuItemValueProvider(htmlgenerator.ValueProvider):
            attributename = "item"

        """item_iterator: an iterable which contains bread.menu.Action objects where the onclick value is what will be passed to the onclick attribute of the menu-item (and therefore should be javascript, e.g. "window.location.href='/home'"). All three item_iterator in the tuple can be lazy objects
        iteratorclass: If the Iterator needs additional values in order to generate item_iterator it can be customized and passed here"""
        attributes["data-overflow-menu"] = True
        attributes["_class"] = attributes.get("_class",
                                              "") + " bx--overflow-menu"
        menuid = f"overflow-menu-{hash(id(self))}"
        super().__init__(
            htmlgenerator.BUTTON(
                Icon("overflow-menu--vertical", size=16),
                _class="bx--overflow-menu__trigger" +
                (" bx--tooltip__trigger bx--tooltip--a11y bx--tooltip--right bx--tooltip--align-start"
                 if menuname is not None else ""),
                aria_haspopup="true",
                aria_expanded="false",
                aria_controls=menuid,
                _id=f"{menuid}-trigger",
            ),
            htmlgenerator.DIV(
                htmlgenerator.UL(
                    iteratorclass(
                        item_iterator,
                        MenuItemValueProvider.Binding(OverflowMenuItem)(
                            MenuItemValueProvider, **item_attributes),
                        MenuItemValueProvider,
                    ),
                    _class="bx--overflow-menu-options__content",
                ),
                _class="bx--overflow-menu-options" +
                (" bx--overflow-menu--flip" if flip else ""),
                tabindex="-1",
                role="menu",
                aria_labelledby=f"{menuid}-trigger",
                data_floating_menu_direction=direction,
                id=menuid,
            ),
            **attributes,
        )
        if menuname is not None:
            self[0].insert(
                0, htmlgenerator.SPAN(menuname, _class="bx--assistive-text"))
示例#15
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)
def profile_field(fieldname):
    return R(
        C(
            hg.SPAN(
                layout.ObjectFieldLabel(fieldname),
                style="font-weight: 700",
            ),
            width=4,
        ),
        C(layout.ObjectFieldValue(fieldname)),
        style="margin-bottom: 2rem",
    )
def person_in_relationship(
    header: str,
    field_name: str,
    get_person: Callable[[Relationship], Person],
) -> DataTableColumn:
    return DataTableColumn(
        header,
        hg.SPAN(
            person_name(field_name),
            person_number_in_brackets(field_name),
            **attributes_for_link_to_person(get_person),
        ),
    )
示例#18
0
 def management_form(self):
     # the management form is required for Django formsets
     return hg.BaseElement(
         # management forms, for housekeeping of inline forms
         hg.F(lambda c: Form(
             c[self.formname][self.fieldname].formset.management_form,
             *[
                 FormField(
                     f, no_wrapper=True, no_label=True, no_helptext=True)
                 for f in c[self.formname][self.fieldname].formset.
                 management_form.fields
             ],
             standalone=False,
         )),
         # Empty form as template for new entries. The script tag works very well
         # for this since we need a single, raw, unescaped HTML string
         hg.SCRIPT(
             Form(
                 hg.C(f"{self.formname}.{self.fieldname}.formset.empty_form"
                      ),
                 hg.WithContext(
                     self.content,
                     **{
                         DEFAULT_FORMSET_CONTEXTNAME:
                         hg.
                         C(f"{self.formname}.{self.fieldname}.formset.empty_form"
                           )
                     },
                 ),
                 standalone=False,
             ),
             id=hg.BaseElement(
                 "empty_",
                 hg.C(f"{self.formname}.{self.fieldname}.formset.prefix"),
                 "_form",
             ),
             type="text/plain",
         ),
         hg.SCRIPT(
             mark_safe(
                 "document.addEventListener('DOMContentLoaded', e => init_formset('"
             ),
             hg.C(f"{self.formname}.{self.fieldname}.formset.prefix"),
             mark_safe("'));"),
         ),
         hg.SPAN(onload=hg.BaseElement(
             mark_safe("init_formset('"),
             hg.C(f"{self.formname}.{self.fieldname}.formset.prefix"),
             mark_safe("');"),
         ), ),
     )
 def _display_as_list_item(person):
     if person is None:
         # this happens if we have entries in the search-backend which have been deleted
         return hg.BaseElement()
     return hg.LI(
         hg.SPAN(
             mark_safe(highlight.highlight(person.personnumber)),
             style="width: 48px; display: inline-block",
             _class=ITEM_VALUE_CLASS,
         ),
         hg.SPAN(
             person.name,
             _class=ITEM_LABEL_CLASS,
             style="dispay:none;",
         ),
         " ",
         mark_safe(highlight.highlight(person.search_index_snippet())),
         style="cursor: pointer; padding: 8px 0;",
         onclick=onclick(person),
         onmouseenter="this.style.backgroundColor = 'lightgray'",
         onmouseleave="this.style.backgroundColor = 'initial'",
         _class=ITEM_CLASS,
     )
示例#20
0
    def __init__(self, header: Any, content: Any, **attributes):
        """
        Parameters
        ----------
        header : Any
            the header of the tile that is not hidden under the fold
        content : Any
            the hidden content that only visible when user click to unfold the tile
        **attributes : optional
            keyword arguments representing the specific HTML attributes for the tile
        """

        super().__init__(
            hg.BUTTON(Icon("chevron--down", size="16"),
                      _class="bx--tile__chevron"),
            hg.DIV(
                hg.SPAN(
                    header,
                    data_tile_atf=True,
                    _class="bx--tile-content__above-the-fold",
                ),
                hg.SPAN(
                    content,
                    _class="bx--tile-content__below-the-fold",
                    onclick="event.stopPropagation();",
                    style="cursor: initial",
                ),
                _class="bx--tile-content",
            ),
            **hg.merge_html_attrs(
                attributes,
                {
                    "_class": "bx--tile bx--tile--expandable",
                    "data_tile": "expandable",
                    "tabindex": "0",
                },
            ))
示例#21
0
    def __init__(self, *label, can_delete=False, tag_color=None, **kwargs):
        kwargs.setdefault(
            "type", "button"
        )  # prevents this from trying to submit a form when inside a FORM element
        kwargs["_class"] = (kwargs.get("_class", "") + " bx--tag" +
                            (" bx--tag--filter" if can_delete else "") +
                            (f" bx--tag--{tag_color}" if tag_color else ""))
        if can_delete:
            kwargs.setdefault("title", _("Remove"))

        super().__init__(
            hg.SPAN(*label, _class="bx--tag__label"),
            *([Icon("close", size=16)] if can_delete else []),
            **kwargs,
        )
示例#22
0
def editperson_head(request):
    return hg.BaseElement(
        R(
            C(
                hg.H3(
                    hg.SPAN(
                        hg.C("object"),
                        style=hg.If(hg.C("object.deleted"),
                                    "text-decoration: line-through"),
                    ),
                    editperson_toolbar(request),
                ),
                width=12,
            ),
            style="padding-top: 1rem",
        ), )
示例#23
0
    def __init__(
        self,
        description: Any,
        icon: Union[Icon, str] = "information",
        align: str = "center",
        position: str = "bottom",
        **attributes,
    ):
        """
        Parameters
        ----------
        description : Any
            One line of string describing an icon.
        icon : Icon, str, optional
            Specify an icon for the tooltip.
            The default value is 'information'.
        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__trigger "
                "bx--tooltip--a11y "
                "bx--tooltip--%s "
                "bx--tooltip--align-%s"
            )
            % (position, align),
            "data_tooltip_icon": True,
        }

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

        super().__init__(
            hg.DIV(
                hg.SPAN(description, _class="bx--assistive-text"),
                icon,
                **tooltip_attributes,
            )
        )
示例#24
0
def as_download(value, label=None):
    if not value:
        return CONSTANTS[None]
    if not value.storage.exists(value.name):
        return hg.SMALL(hg.EM(_("File not found")))
    if label is None:
        label = hg.SPAN(os.path.basename(value.name))
    return hg.A(
        layout.icon.Icon(
            "launch",
            size=16,
            style="vertical-align: middle; margin-right: 0.25rem;",
        ),
        label,
        newtab=True,
        href=value.url,
        style="margin-right: 0.5rem; margin-left: 0.5rem",
        onclick="event.stopPropagation();",
    )
示例#25
0
 def __init__(
     self,
     columns,
     row_iterator,
     valueproviderclass=hg.ValueProvider,
     spacing="default",
     zebra=False,
 ):
     """columns: tuple(header_expression, row_expression)
     if the header_expression/row_expression has an attribute td_attributes it will be used as attributes for the TH/TD elements (necessary because sometimes the content requires additional classes on the parent element)
     spacing: one of "default", "compact", "short", "tall"
     valueproviderclass: A class which implements ValueProvider which will be passed to the Iterator
     """
     assert spacing in ["default", "compact", "short", "tall"]
     classes = ["bx--data-table"]
     if spacing != "default":
         classes.append(f"bx--data-table--{spacing}")
     if zebra:
         classes.append("bx--data-table--zebra")
     super().__init__(
         hg.TABLE(
             hg.THEAD(
                 hg.TR(*[
                     hg.TH(
                         hg.SPAN(
                             column[0],
                             _class="bx--table-header-label",
                         ),
                         **getattr(column[1], "td_attributes", {}),
                     ) for column in columns
                 ])),
             hg.TBODY(
                 hg.Iterator(
                     row_iterator,
                     hg.TR(*[
                         hg.TD(column[1],
                               **getattr(column[1], "td_attributes", {}))
                         for column in columns
                     ]),
                     valueproviderclass,
                 )),
             _class=" ".join(classes),
         ))
示例#26
0
def email(request):
    return tile_with_datatable(
        models.Email,
        hg.F(lambda c: c["object"].core_email_list.all()),
        [
            DataTableColumn(
                layout.ObjectFieldLabel("type", models.Email),
                hg.SPAN(
                    hg.C("row.type"),
                    hg.If(
                        hg.F(lambda c: c["row"] == c["row"].person.
                             primary_email_address),
                        hg.BaseElement(" (", _("primary"), ")"),
                        "",
                    ),
                ),
            ),
            "email",
        ],
        request,
    )
示例#27
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)
示例#28
0
class PersonBrowseView(BrowseView):
    columns = [
        DataTableColumn(
            layout.ObjectFieldLabel("personnumber", models.Person),
            hg.DIV(
                hg.C("row.personnumber"),
                style=hg.If(hg.C("row.deleted"),
                            "text-decoration:line-through"),
            ),
            "personnumber__int",
        ),
        DataTableColumn(
            hg.DIV(
                layout.tooltip.IconTooltip(
                    hg.UL(
                        hg.LI(hg.SPAN("●", style="color: green"), " ",
                              _("Active")),
                        hg.LI(hg.SPAN("●", style="color: red"), " ",
                              _("Inactive")),
                    ),
                    position="top",
                )),
            hg.DIV(
                "●",
                style=hg.format("color: {}",
                                hg.If(hg.C("row.active"), "green", "red")),
            ),
            "active",
        ),
        DataTableColumn(layout.ObjectFieldLabel("_type", models.Person),
                        hg.C("row._type"), "_type"),
        DataTableColumn(
            _("Name"),
            hg.DIV(
                hg.If(
                    hg.F(lambda context: type(context["row"]) == models.
                         NaturalPerson),
                    hg.C("row.last_name"),
                    hg.C("row.name"),
                ),
                style=hg.If(hg.C("row.deleted"),
                            "text-decoration:line-through"),
            ),
            "default_sorting_name",
        ),
        DataTableColumn(
            layout.ObjectFieldLabel("first_name", models.NaturalPerson),
            hg.DIV(
                hg.If(
                    hg.F(lambda context: type(context["row"]) == models.
                         NaturalPerson),
                    hg.C("row.first_name"),
                    "",
                ),
                style=hg.If(hg.C("row.deleted"),
                            "text-decoration:line-through"),
            ),
            "naturalperson__first_name",
        ),
        "primary_postal_address.address",
        "primary_postal_address.postcode",
        "primary_postal_address.city",
        "primary_postal_address.country",
        DataTableColumn(
            _("Email"),
            hg.C("row.primary_email_address.asbutton", ),
            "primary_email_address__email",
            False,
        ),
        DataTableColumn(
            layout.ObjectFieldLabel("tags", models.Person),
            hg.UL(
                hg.Iterator(hg.C("row.tags.all"), "tag",
                            layout.tag.Tag(hg.C("tag")))),
        ),
    ]
    bulkactions = (
        BulkAction(
            "add-tag",
            label=_("Add tag"),
            iconname="add",
            action=bulkaddtag,
        ),
        BulkAction(
            "remove-tag",
            label=_("Remove tag"),
            iconname="subtract",
            action=bulkremovetag,
        ),
        BulkAction(
            "delete",
            label=_("Delete"),
            iconname="trash-can",
            action=bulkdelete,
        ),
        BulkAction(
            "restore",
            label=_("Restore"),
            iconname="restart",
            action=bulkrestore,
        ),
        BulkAction(
            "excel",
            label=_("Excel"),
            iconname="download",
            action=export,
        ),
    )
    search_backend = layout.search.SearchBackendConfig(url=reverse(
        "basxconnect.core.views.person.search_person_view.searchperson"))
    rowclickaction = BrowseView.gen_rowclickaction("read")
    viewstate_sessionkey = "personbrowseview"

    class FilterForm(forms.Form):
        naturalperson = forms.BooleanField(required=False,
                                           label=_("Natural Person"))
        legalperson = forms.BooleanField(required=False,
                                         label=_("Legal Person"))
        personassociation = forms.BooleanField(required=False,
                                               label=_("Person Association"))
        naturalperson_subtypes = forms.ModelMultipleChoiceField(
            queryset=models.Term.objects.filter(
                vocabulary__slug="naturaltype"),
            widget=forms.CheckboxSelectMultiple,
            required=False,
            label="",
        )
        legalperson_subtypes = forms.ModelMultipleChoiceField(
            queryset=models.Term.objects.filter(vocabulary__slug="legaltype"),
            widget=forms.CheckboxSelectMultiple,
            required=False,
            label="",
        )
        personassociation_subtypes = forms.ModelMultipleChoiceField(
            queryset=models.Term.objects.filter(
                vocabulary__slug="associationtype"),
            widget=forms.CheckboxSelectMultiple,
            required=False,
            label="",
        )
        tags = forms.ModelMultipleChoiceField(
            queryset=models.Term.objects.filter(vocabulary__slug="tag"),
            widget=forms.CheckboxSelectMultiple,
            required=False,
        )
        preferred_language = forms.MultipleChoiceField(
            choices=settings.PREFERRED_LANGUAGES,
            widget=forms.CheckboxSelectMultiple,
            required=False,
        )
        status = forms.MultipleChoiceField(
            choices=[("active", _("Active")), ("inactive", _("Inactive"))],
            widget=forms.CheckboxSelectMultiple,
            required=False,
        )
        trash = forms.BooleanField(required=False, label=_("Trash"))

    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

    def _filterform(self):
        return self.FilterForm({"status": ["active"], **self.request.GET})

    def _checkbox_count(self):
        counter = 0
        form = self._filterform()
        if form.is_valid():
            counter += 1 if form.cleaned_data["naturalperson"] else 0
            counter += 1 if form.cleaned_data["legalperson"] else 0
            counter += 1 if form.cleaned_data["personassociation"] else 0
            counter += form.cleaned_data["naturalperson_subtypes"].count()
            counter += form.cleaned_data["legalperson_subtypes"].count()
            counter += form.cleaned_data["personassociation_subtypes"].count()
            counter += form.cleaned_data["tags"].count()
            counter += len(form.cleaned_data["preferred_language"])
            counter += (
                1 if "inactive" in form.cleaned_data["status"] else 0
            )  # don't count the "active" checkbox, it is a permanent default
            counter += 1 if form.cleaned_data["trash"] else 0
        return counter

    def get_queryset(self):
        form = self._filterform()
        qs = super().get_queryset()
        if form.is_valid():
            ret = ((qs.filter(
                deleted=form.cleaned_data.get("trash", False))).select_related(
                    "primary_email_address", "primary_postal_address",
                    "_type").prefetch_related("tags"))
            if any([
                    form.cleaned_data[i] for i in (
                        "naturalperson",
                        "legalperson",
                        "personassociation",
                        "naturalperson_subtypes",
                        "legalperson_subtypes",
                        "personassociation_subtypes",
                    )
            ]):
                q = Q()
                for i in ("naturalperson", "legalperson", "personassociation"):
                    # setup some logic descriptors
                    maintype_selected = bool(form.cleaned_data[i])
                    subtype_selected = bool(form.cleaned_data[f"{i}_subtypes"])
                    all_subtypes_selected = bool(
                        form.cleaned_data[f"{i}_subtypes"].count() ==
                        form.fields[f"{i}_subtypes"].queryset.count())

                    # the semantics for this filter are not 100% clear
                    # there are also cases where a subtype has the wrong maintype
                    # This code tries to make the selection consistent to what a user
                    # would expect, but these expectations can still vary...
                    if maintype_selected:
                        typeq = Q(_maintype=i)
                        if subtype_selected:
                            if not all_subtypes_selected:
                                typeq &= Q(_type__in=form.
                                           cleaned_data[f"{i}_subtypes"])
                        else:
                            typeq &= ~Q(_type__in=form.fields[f"{i}_subtypes"].
                                        queryset)
                        q |= typeq
                    else:
                        q |= Q(_type__in=form.cleaned_data[f"{i}_subtypes"])
                ret = ret.filter(q)
            if form.cleaned_data.get("tags"):
                ret = ret.filter(tags__in=form.cleaned_data["tags"])
            if form.cleaned_data.get("preferred_language"):
                ret = ret.filter(preferred_language__in=form.
                                 cleaned_data["preferred_language"])
            if len(form.cleaned_data.get("status")
                   ) == 1 and not form.cleaned_data.get("trash", False):
                ret = ret.filter(
                    active=form.cleaned_data.get("status")[0] == "active")

            return ret
        return qs

    def get_settingspanel(self):
        return hg.DIV(
            layout.forms.Form(
                self._filterform(),
                hg.DIV(
                    hg.DIV(
                        hg.DIV(layout.helpers.Label(_("Person Type"))),
                        hg.DIV(
                            hg.DIV(
                                hg.DIV(
                                    layout.forms.FormField(
                                        "naturalperson",
                                        onclick=
                                        "updateCheckboxGroupItems(this.parentElement.parentElement)",
                                    ),
                                    hg.DIV(
                                        layout.forms.FormField(
                                            "naturalperson_subtypes",
                                            style="padding-left: 1rem",
                                        ),
                                        style="margin-top: -2rem",
                                    ),
                                ),
                                layout.forms.FormField(
                                    "personassociation",
                                    onclick=
                                    "updateCheckboxGroupItems(this.parentElement.parentElement)",
                                ),
                                hg.DIV(
                                    layout.forms.FormField(
                                        "personassociation_subtypes",
                                        style="padding-left: 1rem",
                                    ),
                                    style="margin-top: -2rem",
                                ),
                                style="margin-right: 16px",
                            ),
                            hg.DIV(
                                layout.forms.FormField(
                                    "legalperson",
                                    onclick=
                                    "updateCheckboxGroupItems(this.parentElement.parentElement)",
                                ),
                                hg.DIV(
                                    layout.forms.FormField(
                                        "legalperson_subtypes",
                                        style="padding-left: 1rem",
                                    ),
                                    style="margin-top: -2rem",
                                ),
                                style="margin-right: 16px",
                            ),
                            style="display: flex",
                        ),
                        style=
                        "border-right: #ccc solid 1px; margin: 0 16px 0 0",
                    ),
                    hg.DIV(
                        hg.DIV(
                            layout.forms.FormField("tags"),
                            style="margin-right: 16px",
                        ),
                        style=
                        "border-right: #ccc solid 1px; margin: 0 16px 0 0; overflow-y: scroll",
                    ),
                    hg.DIV(
                        hg.DIV(
                            layout.forms.FormField("preferred_language"),
                            style="margin-right: 16px",
                        ),
                        style=
                        "border-right: #ccc solid 1px; margin: 0 16px 0 0",
                    ),
                    hg.DIV(
                        hg.DIV(layout.forms.FormField("status"),
                               style="flex-grow: 0"),
                        hg.DIV(style="flex-grow: 1"),
                        hg.DIV(
                            layout.forms.FormField("trash"),
                            style="max-height: 2rem",
                        ),
                        style="display: flex; flex-direction: column",
                    ),
                    style=
                    "display: flex; max-height: 50vh; padding: 24px 32px 0 32px",
                ),
                hg.DIV(
                    layout.button.Button(
                        _("Cancel"),
                        buttontype="ghost",
                        onclick=
                        "this.parentElement.parentElement.parentElement.parentElement.parentElement.style.display = 'none'",
                    ),
                    layout.button.Button.from_link(
                        Link(
                            label=_("Reset"),
                            href=self.request.path + "?reset=1",
                            iconname=None,
                        ),
                        buttontype="secondary",
                    ),
                    layout.button.Button(
                        pgettext_lazy("apply filter", "Filter"),
                        type="submit",
                    ),
                    style=
                    "display: flex; justify-content: flex-end; margin-top: 24px",
                    _class="bx--modal-footer",
                ),
                method="GET",
            ),
            hg.SCRIPT(
                mark_safe("""
                    function updateCheckboxGroupItems(group) {
                        var items = $$('input[type=checkbox]', group);
                        var value = items[0].getAttribute('aria-checked');
                        value = value == 'true' ? 'true' : 'false';
                        for(var i = 1; i < items.length; ++i) {
                            new CarbonComponents.Checkbox(items[i]).setState(value);
                        }
                    }
                    function updateCheckboxCounter(group) {
                        var items = $$('input[type=checkbox]', group);
                        var count = 0;
                        for(item of items) {
                            if(!(item.name === 'status' && item.value === 'active'))
                                count += item.getAttribute('aria-checked') == 'true' ? 1 : 0
                        }
                        $('#%s').innerHTML = count;
                        $('#%s').closest('div[role=button]').style.display = count === 0 ? "none" : "flex";
                    }
                    """ % (self.checkboxcounterid, self.checkboxcounterid))),
            style="background-color: #fff",
            onclick="updateCheckboxCounter(this)",
        )
示例#29
0
    def with_toolbar(
        self,
        title: Any,
        helper_text: Any = None,
        primary_button: Optional[Button] = None,
        bulkactions: Iterable[Link] = (),
        pagination_config: Optional[PaginationConfig] = None,
        checkbox_for_bulkaction_name: str = "_selected",
        search_urlparameter: Optional[str] = None,
        settingspanel: Any = None,
    ):
        """
        wrap this datatable with title and toolbar
        title: table title
        helper_text: sub title
        primary_button: bread.layout.button.Button instance
        bulkactions: List of bread.utils.links.Link instances. Will send a post or a get (depending
                     on its "method" attribute) to the target url the sent data will be a form with
                     the selected checkboxes as fields if the head-checkbox has been selected only
                     that field will be selected.
        """
        checkboxallid = f"datatable-check-{hg.html_id(self)}"
        header: List[hg.BaseElement] = [
            hg.H4(title, _class="bx--data-table-header__title")
        ]
        if helper_text is not None:
            header.append(
                hg.P(helper_text, _class="bx--data-table-header__description"))
        bulkactionlist = []
        for link in bulkactions:
            bulkactionlist.append(
                Button(
                    link.label,
                    icon=link.iconname,
                    onclick=hg.BaseElement(
                        "submitbulkaction(this.closest('[data-table]'), '",
                        link.href,
                        "', method='GET')",
                    ),
                ), )

        if bulkactions:
            self.head.insert(
                0,
                hg.TH(
                    hg.INPUT(
                        data_event="select-all",
                        id=checkboxallid,
                        _class="bx--checkbox",
                        type="checkbox",
                        name=checkbox_for_bulkaction_name,
                        value="all",
                    ),
                    hg.LABEL(
                        _for=checkboxallid,
                        _class="bx--checkbox-label",
                    ),
                    _class="bx--table-column-checkbox",
                ),
            )
            self.iterator[0].insert(
                0,
                hg.TD(
                    hg.INPUT(
                        data_event="select",
                        id=hg.BaseElement(
                            checkboxallid,
                            "-",
                            hg.C(self.iterator.loopvariable + "_index"),
                        ),
                        _class="bx--checkbox",
                        type="checkbox",
                        name=checkbox_for_bulkaction_name,
                        value=hg.If(
                            hg.F(lambda c: hasattr(
                                c[self.iterator.loopvariable], "pk")),
                            hg.C(f"{self.iterator.loopvariable}.pk"),
                            hg.C(f"{self.iterator.loopvariable}_index"),
                        ),
                    ),
                    hg.LABEL(
                        _for=hg.BaseElement(
                            checkboxallid,
                            "-",
                            hg.C(self.iterator.loopvariable + "_index"),
                        ),
                        _class="bx--checkbox-label",
                        aria_label="Label name",
                    ),
                    _class="bx--table-column-checkbox",
                ),
            )

        return hg.DIV(
            hg.DIV(*header, _class="bx--data-table-header"),
            hg.SECTION(
                hg.DIV(
                    hg.DIV(
                        *(bulkactionlist + [
                            Button(
                                _("Cancel"),
                                data_event="action-bar-cancel",
                                _class="bx--batch-summary__cancel",
                            )
                        ]),
                        _class="bx--action-list",
                    ),
                    hg.DIV(
                        hg.P(
                            hg.SPAN(0, data_items_selected=True),
                            _(" items selected"),
                            _class="bx--batch-summary__para",
                        ),
                        _class="bx--batch-summary",
                    ),
                    _class="bx--batch-actions",
                    aria_label=_("Table Action Bar"),
                ),
                hg.DIV(
                    searchbar(search_urlparameter)
                    if search_urlparameter else None,
                    Button(
                        icon="settings--adjust",
                        buttontype="ghost",
                        onclick="""
let settings = this.parentElement.parentElement.parentElement.querySelector('.settingscontainer');
settings.style.display = settings.style.display == 'block' ? 'none' : 'block';
event.stopPropagation()""",
                    ) if settingspanel else None,
                    primary_button or None,
                    _class="bx--toolbar-content",
                ),
                _class="bx--table-toolbar",
            ),
            hg.DIV(
                hg.DIV(
                    hg.DIV(
                        settingspanel,
                        _class="bx--tile raised",
                        style="margin: 0; padding: 0",
                    ),
                    _class="settingscontainer",
                    style=
                    "position: absolute; z-index: 999; right: 0; display: none",
                    onload=
                    "document.addEventListener('click', (e) => {this.style.display = 'none'})",
                ),
                style="position: relative",
                onclick="event.stopPropagation()",
            ),
            self,
            Pagination.from_config(pagination_config)
            if pagination_config else None,
            _class="bx--data-table-container",
            data_table=True,
        )
    def get_layout(self):

        return hg.BaseElement(
            hg.H4(_("Manage Profile")),
            hg.DIV(
                layout.grid.Grid(
                    R(
                        C(
                            hg.H4(
                                layout.icon.Icon("user--profile"),
                                style="text-align: center",
                            ),
                            width=1,
                        ),
                        C(
                            hg.H4(_("Personal Information"),
                                  style="margin-bottom: 3rem"),
                            profile_field("first_name"),
                            profile_field("last_name"),
                            R(
                                C(
                                    hg.SPAN(
                                        _("Preferred Language"),
                                        style="font-weight: 700",
                                    ),
                                    width=4,
                                ),
                                C(
                                    hg.F(lambda c: get_language_info(
                                        c["request"].user.preferences.get(
                                            "general__preferred_language") or
                                        get_language())["name_translated"])),
                                style="margin-bottom: 2rem",
                            ),
                            R(
                                C(
                                    hg.SPAN(
                                        _("Timezone"),
                                        style="font-weight: 700",
                                    ),
                                    width=4,
                                ),
                                C(
                                    hg.F(lambda c: c["request"].user.
                                         preferences.get("general__timezone")
                                         or get_current_timezone())),
                                style="margin-bottom: 2rem",
                            ),
                            layout.modal.modal_with_trigger(
                                layout.modal.Modal.with_ajax_content(
                                    _("Personal Information"),
                                    reverse("userprofile.personal",
                                            query={"asajax": True}),
                                    submitlabel=_("Save"),
                                ),
                                layout.button.Button,
                                _("Edit"),
                                buttontype="tertiary",
                                icon="edit",
                                style="margin-top: 1rem",
                            ),
                            width=7,
                        ),
                        C(
                            hg.H4(
                                layout.icon.Icon("password"),
                                style="text-align: center",
                            ),
                            width=1,
                        ),
                        C(
                            hg.H4(_("Login"), style="margin-bottom: 3rem"),
                            profile_field("username"),
                            profile_field("email"),
                            profile_field_password("password"),
                            layout.modal.modal_with_trigger(
                                layout.modal.Modal.with_ajax_content(
                                    _("Login"),
                                    reverse("userprofile.login",
                                            query={"asajax": True}),
                                    submitlabel=_("Save"),
                                ),
                                layout.button.Button,
                                _("Edit"),
                                buttontype="tertiary",
                                icon="edit",
                                style="margin-top: 1rem",
                            ),
                            width=7,
                        ),
                        style="margin-bottom: 2rem; margin-top: 2rem",
                    ),
                    R(
                        C(
                            hg.H4(
                                layout.icon.Icon("virtual-column--key"),
                                style="text-align: center",
                            ),
                            width=1,
                        ),
                        C(
                            hg.H4(_("Permissions")),
                            profile_field_checkbox("is_active"),
                            profile_field_checkbox("is_superuser"),
                            profile_field_checkbox("is_staff"),
                            layout.modal.modal_with_trigger(
                                layout.modal.Modal.with_ajax_content(
                                    _("Permissions"),
                                    reverse(
                                        "userprofile.permissions",
                                        query={"asajax": True},
                                    ),
                                    submitlabel=_("Save"),
                                ),
                                layout.button.Button,
                                _("Edit"),
                                buttontype="tertiary",
                                icon="edit",
                                style="margin-top: 1rem",
                                disabled=not self.request.user.is_superuser,
                            ),
                            width=7,
                        ),
                        C(width=1),
                        C(
                            hg.DIV(
                                layout.toggle.Toggle(
                                    _("Developer Mode"),
                                    _("Disabled"),
                                    _("Enabled"),
                                    help_text=hg.SPAN(
                                        _(
                                            "Warning: This is a dangerous option!",
                                        ),
                                        hg.BR(),
                                        _(
                                            "Enable it only if you know what you are doing!",
                                        ),
                                        style="color: red",
                                    ),
                                    widgetattributes={
                                        "checked":
                                        hg.
                                        C(f"request.session.{layout.DEVMODE_KEY}"
                                          ),
                                    },
                                    onclick=
                                    f"fetch('{reverse('devmode', kwargs={'enable': not self.request.session.get(layout.DEVMODE_KEY, False)})}').then((resp) => {{}}).then(() => location.reload(true)); return false;",
                                    style="margin-bottom: 0",
                                ),
                                style="align-self: flex-end",
                            ),
                            width=7,
                            style="display: flex; justify-content: flex-end",
                        ),
                        style="margin-bottom: 2rem; margin-top: 2rem",
                    ),
                ),
                _class="bx--tile",
            ),
        )