Esempio n. 1
0
    def test_can_walk(self):
        elem = div([h1('title'), p('title')])
        items = list(elem.walk())
        assert len(items) == 5

        elem = div([h1('title'), 'title'])
        assert len(list(elem.walk())) == 4
Esempio n. 2
0
def render_collapsible(lst,
                       item_role='collapsible-item',
                       title=None,
                       expanded=False,
                       **kwargs):
    """
    Renders a queryset or list of objects

    Args:
        lst:
            List or queryset of objects inside the collapsible list.
        item_role:
            Role assigned to each element in the list. Defaults to
            'collapsible-item'.
        title (bool):
            Title in which the list of object is displayed.
        expanded (str):
            If true, start list in the "expanded" state.
    """
    if title is None:
        if isinstance(lst, QuerySet):
            title = title.model._meta.verbose_name_plural
        else:
            raise TypeError('must provide an explicit title!')

    data = [html(x, item_role, **kwargs) for x in lst]
    return div(class_='CollapsibleList', is_component=True)[
        h2([title, span(f'({len(data)})'
                        ), fa_icon('angle-up')]),
        div(class_='CollapsibleList-data')[html_list(data), ]]
Esempio n. 3
0
def popup(title, content, action=None, **kwargs):
    """
    Return a popup screen.

    It does not include positioning and the overlay element.

    Args:
        title: Title of the popup.
        content: HTML or text content of the popup.
        action: Action button. Can be an anchor or other element.
    """
    return div(
        [
            icon("times-circle",
                 class_="popup__close",
                 is_component="popup:close"),
            div(
                [
                    h1([title], class_="title"),
                    p(content), action and div(action)
                ],
                class_="popup__contents",
            ),
        ],
        **kwargs,
    ).add_class("popup")
Esempio n. 4
0
def command_bar(*actions, **kwargs):
    """
    Element that includes configuration links bellow the header bar.
    """
    if len(actions) == 1:
        return div(actions, **kwargs).add_class("CommandBar")
    elif len(actions) == 2:
        return div(actions, **kwargs).add_class("CommandBar")
    else:
        n = len(actions)
        raise ValueError(f"cannot include more than 2 actions, got: {n}")
Esempio n. 5
0
def toast(icon, title, description=None, **kwargs):
    """
    Toast component: display some title with a highlighted icon.
    """
    body = [h1(title)]
    if description:
        body.append(p(description))
    return div([
        _icon(icon, class_="toast__icon"),
        div(body, class_="toast__content")
    ], **kwargs).add_class("toast")
Esempio n. 6
0
    def render(self, name, value, attrs=None, renderer=None):
        widget = self.get_context(name, value, attrs)["widget"]

        w_name = widget.get("name", "")
        w_type = widget.get("type", "")
        w_attrs = widget.get("attrs", {})

        return div(class_="FileInput")[div(class_="PickFileButton")[
            input_(style="opacity: 0", type_=w_type, name=w_name, **w_attrs),
            _("Choose a file")],
                                       div(class_="FileStatus"
                                           )[_("No file chosen")], ].render()
Esempio n. 7
0
def collapsible(data, title=None, collapsed=False):
    """
    Renders a collapsible content.
    """

    angle = fa_icon("angle-up", class_="collapsible__handle")
    return div(
        class_="collapsible",
        is_component=True,
        is_collapsed=collapsed,
        children=[
            h2([title, angle], class_="collapsible__title"),
            div(data, class_="collapsible__data"),
        ],
    )
Esempio n. 8
0
def conversation_create_comment(conversation, request=None, **kwargs):
    """
    Render "create comment" button for one conversation.
    """
    conversation.set_request(request)
    n_comments = conversation.n_user_total_comments
    n_moderation = conversation.n_pending_comments

    fn = rules.get_value("ej.max_comments_per_conversation")
    user = getattr(request, "user", None)
    max_comments = fn(conversation, user)

    moderation_msg = _("{n} awaiting moderation").format(n=n_moderation)
    comments_count = _("{n} of {m} comments").format(n=n_comments,
                                                     m=max_comments)

    # FIXME: Reactivate when full UI for the comment form is implemented
    # return extra_content(
    #     _("Create comment"),
    #     Blob(f"{comments_count}" f'<div class="text-7 strong">{moderation_msg}</div>'),
    #     icon="plus",
    #     id="create-comment",
    # )
    return div(
        Blob(f"{comments_count}"
             f'<div class="text-7 strong">{moderation_msg}</div>'),
        id="create-comment",
        class_="extra-content",
    )
Esempio n. 9
0
 def html(self, classes=()):
     if self.format == Format.HTML:
         data = sanitize_html(self.content)
     elif self.format == Format.MARKDOWN:
         data = markdown(self.content)
     text = Text(data, escape=False)
     return div(text, class_=classes)
Esempio n. 10
0
def popup_content(title, text, action, **kwargs):
    """
    Content of a pop-up window.
    """
    return div(**kwargs)[h1(title), p(text), action].add_class(
        "PopupWindow", first=True
    )
Esempio n. 11
0
def render_collapsible(lst,
                       item_role='collapsible-item',
                       title=None,
                       expanded=False,
                       **kwargs):
    """
    Renders a queryset or list of objects

    Args:
        lst:
            List or queryset of objects inside the collapsible list.
        item_role:
            Role assigned to each element in the list. Defaults to
            'collapsible-item'.
        title (bool):
            Title in which the list of object is displayed.
        expanded (str):
            If true, start list in the "expanded" state.
    """
    if title is None:
        if isinstance(lst, QuerySet):
            title = title.model._meta.verbose_name_plural
        else:
            raise TypeError('must provide an explicit title!')

    data = [render(x, item_role, **kwargs) for x in lst]
    random_id = str(uuid.uuid4())
    display = 'block' if expanded else 'none'

    return div(
        class_='CollapsibleList'
    )[h2(onclick=f"$('#{random_id}').toggle()",
         children=[title, span(f'({len(data)})'),
                   fa_icon('angle-down')]),
      html_list(data, style=f'display: {display}', id=random_id), ]
Esempio n. 12
0
def row(*children, padding=True, wrap=False, align=None, **kwargs):
    """
    A row that contains several columns as children.

    Args:
        align ({'top', 'bottom', 'center', 'stretch', 'baseline'}):
            Defines the vertical alignment of elements in the row. Each of those
            options can also be passed as a boolean argument as in
            ``row(..., top=True)``.
        padding (bool):
            If set to False, eliminate the padding for cells in the given row.
        wrap (bool):
            If True, allow children to wrap over the next line when overflow.

        ``row`` also accepts additional HTML attributes as keyword arguments.
    """
    options = {"row-no-padding": not padding, "row-wrap": wrap}
    options.update((k, kwargs[k]) for k in ROW_ALIGNMENTS.intersection(kwargs))
    classes = ["row"]
    classes.extend(cls for cls, on in options.items())

    if align is not None:
        if align not in ROW_ALIGNMENTS:
            raise ValueError(f"invalid alignment: {align!r}")
        classes.append(f"row-{align}")
    return hp.div(class_=classes, children=children, **kwargs)
Esempio n. 13
0
 def test_pretty(self):
     tag = div(class_='foo')[p('hello'), p('world')]
     html = ('<div class="foo">\n'
             '  <p>hello</p>\n'
             '  <p>world</p>\n'
             '</div>\n')
     assert tag.pretty() == html
Esempio n. 14
0
def conversation_create_comment(conversation, request=None, **kwargs):
    """
    Render "create comment" button for one conversation.
    """
    conversation.set_request(request)
    n_comments = conversation.n_user_comments
    n_moderation = conversation.n_pending_comments
    max_comments = max_comments_per_conversation()
    moderation_msg = _("{n} awaiting moderation").format(n=n_moderation)
    comments_count = _("{ratio} comments").format(
        ratio=f"<strong>{n_comments}</strong> / {max_comments}")

    # FIXME: Reactivate when full UI for the comment form is implemented
    # return extra_content(
    #     _("Create comment"),
    #     Blob(f"{comments_count}" f'<div class="text-7 strong">{moderation_msg}</div>'),
    #     icon="plus",
    #     id="create-comment",
    # )
    return div(
        Blob(f"{comments_count}"
             f'<div class="text-7 strong">{moderation_msg}</div>'),
        id="create-comment",
        class_="extra-content",
    )
Esempio n. 15
0
def paragraph(title, description=None, **kwargs):
    """
    Display a centered title with a small description paragraph.

    This content is wrapped into a div that centers the content into the main
    page layout.
    """
    children = [h1(title, class_='Paragraph-title')]
    if description:
        children.append(p(description, class_='Paragraph-text'))
    return div(children, **kwargs).add_class('Paragraph', first=True)
Esempio n. 16
0
def stats_table(conversation,
                stats=None,
                data="votes",
                request=None,
                **kwargs):
    if stats is None:
        stats = conversation.statistics()

    get = COLUMN_NAMES.get
    return div([html_map({get(k, k): v}) for k, v in stats[data].items()],
               **kwargs).add_class("stat-slab", first=True)
Esempio n. 17
0
def role_model(model):
    links = []
    cls = get_class(model)
    for role in get_roles(cls):
        href = reverse("role-model-list", kwargs={"model": model, "role": role})
        links.append(a(role, href=href))

    if not links:
        raise Http404
    else:
        return {"data": div([h1(_("List of roles")), html_list(links)])}
Esempio n. 18
0
def intro(title, description=None, **kwargs):
    """
    Display a centered title with a small description paragraph.

    This content is wrapped into a div that centers the content into the main
    page layout.
    """
    children = [h1(title)]
    if description:
        children.append(p(description))
    return div(children, **kwargs).add_class("intro-paragraph", first=True)
Esempio n. 19
0
def role_model(model):
    links = []
    cls = get_class(model)
    for role in get_roles(cls):
        href = reverse('role-model-list',
                       kwargs={'model': model, 'role': role})
        links.append(a(role, href=href))

    if not links:
        raise Http404
    else:
        return {'data': div([h1(_('List of roles')), html_list(links)])}
Esempio n. 20
0
def progress_bar(*args):
    """
    Display a progress bar.

    progress_bar(pc)       --> tell a percentage
    progress_bar(n, total) --> pass the number of items and total
    """

    # Compute fractions
    if len(args) == 1:
        pc = args[0]
        n = total = None
    else:
        e = 1e-50
        n, total = args
        pc = round(100 * (n + e) / (total + e))

    # Build children
    children = [
        div(strong(f"{pc}%")),
        div(
            class_="progress-bar__progress",
            children=[
                div(" ",
                    class_="color-brand-lighter",
                    style=f"flex-grow: {pc + 3};"),
                div(" ", style=f"flex-grow: {100 - pc};"),
            ],
        ),
    ]
    if total is not None:
        children.append(div([strong(n), "/", total]))

    # Return
    return div(children, class_="progress-bar")
Esempio n. 21
0
def progress_bar(*args, **kwargs):
    """
    Display a progress bar.

    progress_bar(pc)       --> tell a percentage
    progress_bar(n, total) --> pass the number of items and total
    """

    # Compute fractions
    if len(args) == 1:
        pc = args[0]
        n = total = None
        aria_msg = _("Your progress: {pc} percent").format(pc=pc)
    else:
        e = 1e-50
        n, total = args
        pc = round(100 * (n + e) / (total + e))
        aria_msg = _("Your progress: {n} of {total}").format(n=n, total=total)

    # Build children
    children = [
        strong(f"{pc}%", class_="block margin-r2", aria_hidden="true"),
        div(
            class_="progress-bar__progress",
            children=[
                div(" ",
                    class_="color-brand-lighter",
                    style=f"flex-grow: {pc + 3};"),
                div(" ", style=f"flex-grow: {100 - pc};"),
            ],
        ),
    ]

    if total is not None:
        children.append(div([strong(n), "/", total], aria_hidden="true"))

    # Return
    return div(children, aria_label=aria_msg, role="img",
               **kwargs).add_class("progress-bar", first=True)
Esempio n. 22
0
def column(
    *children,
    size=None,
    offset=None,
    top=False,
    bottom=False,
    center=None,
    align=None,
    **kwargs,
):
    """
    A single column inside a flexible row.

    Args:
        size ({ 10, 20, 25, 33, 34, 40, 50, 60, 66, 67, 75, 80, 90, 100 }):
            Column size (in %). Only a few pre-determined sizes are accepted.
        offset (int):
            Column offset. It accepts the save values as size.
        align {'top', 'bottom', 'center'}:
            Defines the horizontal alignment of elements in a cell. Each of those
            options can also be passed as a boolean argument as in
            ``column(..., top=True)``.

        ``column`` also accepts additional HTML attributes as keyword arguments.
    """
    classes = ["column"]

    # Size
    if size is not None:
        if size not in VALID_COLUMN_SIZES:
            raise ValueError("Invalid size: %s" % size)
        classes.append(f"column-{size:d}")
    if offset:
        if offset not in VALID_COLUMN_SIZES:
            raise ValueError("Invalid offset: %s" % size)
        classes.append(f"column-offset-{offset:d}")

    # Alignment
    if align is not None:
        if align not in ("top", "bottom", "center"):
            raise ValueError(f"invalid alignment: {align!r}")
        classes.append(f"column-{align}")
    elif top:
        classes.append("column-top")
    elif bottom:
        classes.append("column-bottom")
    elif center:
        classes.append("column-center")

    return hp.div(children, **kwargs).add_class(classes)
Esempio n. 23
0
def tabs(items, select=0, js=True, **kwargs):
    """
    Return a tabbed interface.
    """
    items = items.items() if isinstance(items, Mapping) else items
    children = []
    if js:
        kwargs["is-component"] = True

    for idx, (k, v) in enumerate(items):
        args = {"href": v} if isinstance(v, str) else v
        anchor = a(args, k, is_selected=select == idx)
        children.append(anchor)

    return div(children, **kwargs).add_class("tabs", first=True)
Esempio n. 24
0
def column(*children, size=None, **kwargs):
    """
    A single column inside a 12-columns based flexible row.

    Args:
        size (optional, 1 to 12):
            Number of columns this cell spans. Each row has 12 columns, do the
            math. If not given, automatically fill the remaining space
            distributing equally between each column.

        ``column`` also accepts additional HTML attributes as keyword arguments.
    """
    if size is None:
        class_ = "col"
    else:
        class_ = f"col-{size}"
    return hp.div(children, **kwargs).add_class(class_)
Esempio n. 25
0
def extra_content(title, text, icon=None, **kwargs):
    """
    Simple element with a title and a small paragraph with information.

    Used, for instance, in the detail page of conversations to link the
    comment form or cluster information.

    Args:
        title: Title of the content
        text: Paragraph that follows content.
        icon: Optional icon to be included with title element.

    Returns:

    """
    title = h1([_icon(icon), title]) if icon else h1(title)
    return div([title, text], **kwargs).add_class("extra-content")
Esempio n. 26
0
def column(*children, size=12, offset=None, **kwargs):
    """
    A single column inside a 12-columns based flexible row.

    Args:
        size (1 to 12):
            Number of columns this cell spans. Each row has 12 columns, do the
            math.
        offset (int):
            Column offset in the 12-column system.

        ``column`` also accepts additional HTML attributes as keyword arguments.
    """
    classes = [COLUMN_MAP[size - 1], "columns"]
    if offset is not None:
        name = COLUMN_MAP[offset - 1]
        classes.append(f"offset-by-{name}")
    return hp.div(children, **kwargs).add_class(classes)
Esempio n. 27
0
def test_json_conversion():
    tag = div(class_='foo')[h1('bar'), safe('foo <b>bar</b>')]
    pprint(tag.json())
    assert tag.json() == {
        'tag':
        'div',
        'attrs': {
            'class': ['foo']
        },
        'children': [
            {
                'tag': 'h1',
                'children': [{
                    'text': 'bar'
                }]
            },
            {
                'raw': 'foo <b>bar</b>'
            },
        ]
    }
Esempio n. 28
0
def categories(items, select=0, js=True, **kwargs):
    """
    Similar to tabs, but display several categories for the user to select.
    """
    items = items.items() if isinstance(items, Mapping) else items
    children = [
        icon("chevron-left", class_="categories__left", is_element="leftArrow:click")
    ]
    if js:
        kwargs["is-component"] = True

    for idx, (k, v) in enumerate(items):
        args = {"href": v} if isinstance(v, str) else v
        if select == idx or select == v:
            args["is-selected"] = True
        children.append(a(args, k))
    children.append(
        icon("chevron-right", class_="categories_right", is_element="rightArrow:click")
    )

    return div(children, **kwargs).add_class("categories", first=True)
Esempio n. 29
0
def _make_tabs(cls, js, kwargs, children):
    if js:
        kwargs["is-component"] = True
    kwargs.setdefault("role", "tablist")
    return div(children, **kwargs).add_class(cls, first=True)
Esempio n. 30
0
def menu_from_sections(sections):
    # add role="menu" in the future?
    return div(*sections,
               class_="page-menu",
               id="page-menu",
               is_component=True)