christabor/flask_extras

View on GitHub
flask_extras/macros/bootstrap.html

Summary

Maintainability
Test Coverage
{% from 'utils.html' import apply_dattrs, apply_classes, apply_prop %}
{% from 'macros.html' import objects2table %}

{% macro progress(percent,
                  animated=False,
                  classes=[],
                  context=None,
                  min=0,
                  max=100,
                  striped=False,
                  show_percent=True
    )
%}
{#
    Create a bootstrap progress bar with lots of options.

    Usage:
    {{ progress(30) }}
    {{ progress(100, animated=True, striped=True) }}
    {{ progress(89, context='success') }}
    {{ progress(89, context='danger') }}
    {{ progress(89, context='warning') }}
    {{ progress(89, context='info') }}
#}
<div class="{{ apply_classes(classes + ['progress']) }}">
    <div class="progress-bar {{ 'active' if animated else '' }} {{ 'progress-bar-striped' if striped else '' }} {{ 'progress-bar-' + context if context else '' }}" role="progressbar" aria-valuenow="{{ percent }}" aria-valuemin="{{ min }}" aria-valuemax="{{ max }}" style="min-width:2em; width: {{ percent }}%">
        <span class="{{ 'sr-only' if not show_percent else '' }}">{{ percent }}%</span>
    </div>
</div>
{% endmacro %}

{% macro modal(id, content,
               classes=[],
               close_btns=True,
               fade=True,
               footer=None,
               show_footer=True,
               include_btn=False,
               include_btn_text='Trigger Modal',
               title=None
               )
%}
{#
    Makes a bootstrap modal
    Usage:
    {{ modal('someId', 'Some html or text...', title='My Modal') }}
#}
{% if include_btn %}
    <a class="btn btn-primary" data-toggle="modal" href='#{{ id }}'>
        {{ include_btn_text }}
    </a>
{% endif %}
<div class="modal {{ 'fade' if fade else '' }}" {{ apply_prop('id', id) }}>
    <div class="modal-dialog">
        <div class="{{ apply_classes(classes + ['modal-content']) }}">
            <div class="modal-header">
                {% if close_btns %}
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                {% endif %}
                {% if title %}
                    <h4 class="modal-title">{{ title|safe }}</h4>
                {% endif %}
            </div>
            <div class="modal-body">{{ content|safe }}</div>
            <div class="modal-footer">
                {% if footer %}
                    {{ footer|safe }}
                {% elif show_footer %}
                    {% if close_btns %}
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    {% endif %}
                    <button type="button" class="btn btn-primary">Submit</button>
                {% endif %}
            </div>
        </div>
    </div>
</div>
{% endmacro %}

{% macro dict2carousel(id, items,
                       active=0,
                       classes=[],
                       data_attrs=[],
                       show_arrows=True
                       )
%}
{#
    Makes a bootstrap carousel
    Usage:
    {{ dict2carousel(
        'someId',
        [
            {'content': '<img src="https://placekitten.com/200/300">', 'caption': 'foo'},
            {'content': '<img src="http://placehold.it/350x150">', 'caption': 'bar'},
            {'content': '<img src="http://placehold.it/350x150">', 'caption': 'baz'},
        ],
        active=2)
    }}
#}
<div {{ apply_prop('id', id) }}
    class="{{ apply_classes(classes + ['carousel', 'slide']) }}"
    data-ride="carousel" {{ apply_dattrs(data_attrs) }}>

    <ol class="carousel-indicators">
        {% for i in range(items|length) %}
            <li data-target="#{{ id }}" data-slide-to="{{ i }}" class="{{ 'active' if i == active else '' }}"></li>
        {% endfor %}
    </ol>

    <div class="carousel-inner">
        {% for item in items %}
            <div class="item {{ 'active' if loop.index - 1 == active else '' }}">
                {{ item.content|safe }}
                <div class="container">
                    <div class="carousel-caption">
                        {{ item.caption|safe }}
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>

    {% if show_arrows %}
        <a class="left carousel-control" href="#{{ id }}" data-slide="prev">
            <span class="glyphicon glyphicon-chevron-left"></span>
        </a>
        <a class="right carousel-control" href="#{{ id }}" data-slide="next">
            <span class="glyphicon glyphicon-chevron-right"></span>
        </a>
    {% endif %}
</div>
{% endmacro %}


{%- macro bs3_dictlist_group(data,
                             classes=[],
                             filterkeys=[],
                             filtervals=[],
                             data_attrs=[],
                             asdict=False
                             )
%}
{#
    Makes a bootstrap list group from a set of dictionaries
    Usage:
    {{ bs3_dictlist_group({'foo': 'bar'}) }}

    Output:
    <div class="list-group">
        <h4 class="list-group-item-heading">foo</h4>
        <p class="list-group-item-text">bar</p>
    </div>
#}
{% if asdict %}{% set data = data._asdict() %}{% endif %}
<div class="{{ apply_classes(classes + ['list-group']) }}" {{ apply_dattrs(data_attrs) }}>
    {% for k, v in data.items() %}
        {% if k not in filterkeys and v not in filtervals %}
            <div class="list-group-item">
                <h4 class="list-group-item-heading">{{ k }}</h4>
                <p class="list-group-item-text">{{ v }}</p>
            </div>
        {% endif %}
    {% endfor %}
</div>
{% endmacro -%}


{%- macro bs3_list_group(data, classes=[], type='ul') %}
{#
    OL/UL based bootstrap list group

    Usage:
    {{ bs3_list_group(['foo', 'bar']) }}

    Output:
    <ul>
        <li class="list-group-item">foo</li>
        <li class="list-group-item">bar</li>
    </ul>
#}
<{{ type }} class="{{ apply_classes(classes + ['list-group']) }}">
    {% for val in data %}
        <li class="list-group-item">{{ val }}</li>
    {% endfor %}
</{{ type }}>
{% endmacro -%}


{%- macro dictlist_group_badged(data, classes=[], type='ul', align='right') %}
{#
    OL/UL based bootstrap list group
    with badge labels on the right (default).
    From: http://getbootstrap.com/components/#list-group-badges

    Usage:
    {{ dictlist_group_badged({'Messages': 20, 'Unread': 10, 'Read': 100, 'Starred': 34}) }}

    Output:
    <ul>
        <li class="list-group-item">Messages <span class="label label-default">20</span></li>
        <li class="list-group-item">Unread <span class="label label-default">10</span></li>
        <li class="list-group-item">Read <span class="label label-default">100</span></li>
        <li class="list-group-item">Starred <span class="label label-default">34</span></li>
    </ul>
#}
<{{ type }} class="{{ apply_classes(classes + ['list-group']) }}">
    {% for val, label in data.items() %}
        <li class="list-group-item">
            {% if align == 'left' %}
                <span class="label label-default }}">{{ label }}</span>
            {% endif %}
            {{ val }}
            {% if align == 'right' %}
                <span class="label label-default pull-right">{{ label }}</span>
            {% endif %}
        </li>
    {% endfor %}
</{{ type }}>
{% endmacro -%}


{%- macro bs3_label(value, label_map, text=None) -%}
{#
    Convert a dict of values with corresponding label types given a value, to
    the appropriate label.

    Usage:
    {{ bs3_label('prod', {'dev': 'info', 'stage': 'warning', 'prod': 'danger'}) }}

    Output:
    <span class="label label-danger">prod</span>
 #}
<span class="label label-{{ label_map[value] }}">{{ text if text else value }}</span>
{% endmacro -%}


{%- macro bs3_panel(items, paneltype='default') -%}
{#
    Generate bs3 panels for a dict of titles/body content.
    Usage:
    {{ bs3_panel({'Heading 1': 'Some body text... etc...', 'Heading 2': 'Some body text...'}, paneltype='info') }}

    Output:
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">Heading 1</h3>
        </div>
        <div class="panel-body">
            Some body text... etc...
        </div>
    </div>
    <div class="panel panel-info">
        <div class="panel-heading">
            <h3 class="panel-title">Heading 2</h3>
        </div>
        <div class="panel-body">
            Some body text...
        </div>
    </div>
 #}
    {% for title, body in items.items() %}
        <div class="panel panel-{{ paneltype }}">
            <div class="panel-heading">
                <h3 class="panel-title">
                    {{ title }}
                </h3>
            </div>
            <div class="panel-body">
                {{ body }}
            </div>
        </div>
    {% endfor %}
{% endmacro -%}


{%- macro bs3_breadcrumb(bcrumbs) %}
{#
    Generate bs3 style breadcrumbs using a dynamic breadcrumbs object.
    (Most likely from something like flask-breadcrumbs.)
    Usage:
    {{ bs3_breadcrumb(breadcrumbs) }}
#}
    {% if bcrumbs %}
    <ol class="breadcrumb">
        {%- for bcrumb in bcrumbs -%}
            {% if not loop.last %}
            <li>
                <a href="{{ bcrumb.url }}">{{ bcrumb.text }}</a>
            </li>
            {% else %}
                <li class="active">{{ bcrumb.text }}</li>
            {% endif %}
        {%- endfor -%}
    </ol>
    {% endif %}
{% endmacro -%}


{%- macro dict2labels(data, filterkeys=[], filtervals=[], aslist=False) %}
{#
    Makes a dict of `name`:`label` into bootstrap labels
    Usage:
    {{ dict2labels({'foo': 'danger'}) }}
    <span class="label label-danger">foo</span>

    {{ dict2labels({'foo': 'danger'}, aslist=False) }}
    <span class="label label-danger">foo</span>

    Or wrap it in an html list by specifying `aslist`:
    {{ dict2labels({'foo': 'danger'}, aslist=True) }}
#}
{% if aslist %}<ul class="list-unstyled">{% endif %}
    {% for k, label in data.items() %}
        {% if k not in filterkeys and label not in filtervals %}
            {% if aslist %}<li>{% endif %}
                <span class="label label-{{ label }}">{{ label }}</span>
            {% if aslist %}</li>{% endif %}
        {% endif %}
    {% endfor %}
{% if aslist %}</ul>{% endif %}
{% endmacro -%}


{%- macro dict2tabs(data, filterkeys=[], filtervals=[],
                    asdict=False,
                    id='',
                    headings=False,
                    heading_tag='h4',
                    data_attrs=[],
                    tab_links={},
                    active='',
                    disabled=[],
                    tab_classes=[],
                    content_classes=[],
                    tab_data_attrs=[],
                    nav_tab_classes=[],
                    tab_content_classes=[],
                    content_data_attrs=[],
                    classes=[]) %}
{% if asdict %}{% set data = data._asdict() %}{% endif %}
{#
    Make a dict into a bs3 tab group with tab content. Keys are tab labels,
    and values are tab content.
    Usage:
    {{ dict2tabs({"foo": "Some content rendered from html or macro"}) }}

    You can also customize styles, data-attrs, etc... see function signature for more info.

    You can even pass in content generated from other macros, such as the wtform_form macro:

    {{
        dict2tabs({
            "tab1": wtform_form(form1, action=url_for('app.page1')),
            "tab2": wtform_form(form2, action=url_for('app.page2')),
            id="MyCoolTabContainer"
        })
    }}

    Note: unless active is specified, order will be determined by sorting the keys. If you need ordering to be specific to the dictionary key order, use `dictlist2tabs` instead.
#}
<div role="tabpanel" {{ apply_prop('id', id) }} class="{{ apply_classes(classes) }}" {{ apply_dattrs(data_attrs) }}>
    <!-- Nav tabs -->
    <ul class="nav nav-tabs {{ apply_classes(nav_tab_classes) }}" role="tablist">
        {% for label in data.keys()|sort %}
            <li role="presentation"
                class="{{ apply_classes(tab_classes) }} {% if label == active %}active{% elif loop.first and not active %}active{% endif %} {{ 'disabled' if label in disabled }}"
                {{ apply_dattrs(tab_data_attrs) }}>
                {% set link = '#' + label|css_selector if label not in tab_links else tab_links[label] %}
                <a href="{{ link }}" aria-controls="{{ label|css_selector }}" role="tab" data-toggle="tab">{{ label }}</a>
            </li>
        {% endfor %}
    </ul>

    <!-- Tab panes -->
    <div class="tab-content {{ apply_classes(tab_content_classes) }}">
        {% for label in data.keys()|sort %}
            <div role="tabpanel"
                class="{{ apply_classes(content_classes) }} tab-pane {% if label == active %}active{% elif loop.first and not active %}active{% endif %} {{ 'disabled' if label in disabled }}" id="{{ label|css_selector }}" {{ apply_dattrs(content_data_attrs) }}>
                {% if headings %}<{{ heading_tag }}>{{ label }}</{{ heading_tag }}>{% endif %}
                {{ data[label]|safe }}
            </div>
        {% endfor %}
    </div>
</div>
{% endmacro -%}


{%- macro dictlist2tabs(data, filterkeys=[], filtervals=[],
                    asdict=False,
                    id='',
                    headings=False,
                    heading_tag='h4',
                    heading_macros={},
                    data_attrs=[],
                    tab_links={},
                    active='',
                    disabled=[],
                    tab_classes=[],
                    content_classes=[],
                    tab_data_attrs=[],
                    nav_tab_classes=[],
                    tab_content_classes=[],
                    content_data_attrs=[],
                    classes=[]) %}
{#
    Make a list of dicts into a bs3 tab group with tab content. Keys are tab labels,
    and values are tab content.
    Usage:
    {{ dictlist2tabs([{"foo": "Some content rendered from html or macro"}]) }}

    You can also customize styles, data-attrs, etc... see function signature for more info.

    You can even pass in content generated from other macros, such as the wtform_form macro:

    {{
        dictlist2tabs([
            {"tab1": wtform_form(form1, action=url_for('app.page1'))},
            {"tab2": wtform_form(form2, action=url_for('app.page2'))},
        ],
        id="MyCoolTabContainer",
        })
    }}

    Note: unless active is specified, order will be determined by sorting the keys.
#}
<div role="tabpanel" {{ apply_prop('id', id) }} class="{{ apply_classes(classes) }}" {{ apply_dattrs(data_attrs) }}>
    <!-- Nav tabs -->
    <ul class="nav nav-tabs {{ apply_classes(nav_tab_classes) }}" role="tablist">
        {% for item in data %}
            {% set label = item.keys()[0] %}
            <li role="presentation"
                class="{{ apply_classes(tab_classes) }} {% if label == active %}active{% elif loop.first and not active %}active{% endif %} {{ 'disabled' if label in disabled }}"
                {{ apply_dattrs(tab_data_attrs) }}>
                {% set link = '#' + label|css_selector if label not in tab_links else tab_links[label] %}
                <a href="{{ link }}" aria-controls="{{ label|css_selector }}" role="tab" data-toggle="tab">
                    {% if label in heading_macros %}
                        {{ heading_macros[label](label, item) }}
                    {% else %}
                        {{ label }}
                    {% endif %}
                </a>
            </li>
        {% endfor %}
    </ul>

    <!-- Tab panes -->
    <div class="tab-content {{ apply_classes(tab_content_classes) }}">
        {% for item in data %}
            {% set label = item.keys()[0] %}
            <div role="tabpanel"
                class="{{ apply_classes(content_classes) }} tab-pane {% if label == active %}active{% elif loop.first and not active %}active{% endif %} {{ 'disabled' if label in disabled }}" id="{{ label|css_selector }}" {{ apply_dattrs(content_data_attrs) }}>
                {% if headings %}<{{ heading_tag }}>{{ label }}</{{ heading_tag }}>{% endif %}
                {{ item[label]|safe }}
            </div>
        {% endfor %}
    </div>
</div>
{% endmacro -%}


{%- macro inline_list(lst, divider='|', classes=[], data_attrs=[]) %}
<ul class="{{ apply_classes(classes + ['list-inline']) }}" {{ apply_dattrs(data_attrs) }}>
    {%- for item in lst %}
    <li>
        {{ item }}
        {%- if not loop.last %}&nbsp;&nbsp;{{ divider }} {% endif -%}
    </li>
    {% endfor %}
</ul>
{% endmacro -%}


{%- macro inline_dictlist(dlist, divider='|', seperator=': ', classes=[], data_attrs=[]) %}
<ul class="{{ apply_classes(classes + ['list-inline']) }}" {{ apply_dattrs(data_attrs) }}>
    {%- for item in dlist %}
    <li>{{ item.keys()[0] }}{{ seperator }}{{ item.values()[0] }}
        {%- if not loop.last %}&nbsp;&nbsp;{{ divider }} {% endif -%}
    </li>
    {% endfor %}
</ul>
{% endmacro -%}


{%- macro dict2btn_group(dlist, size='md', classes=[], data_attrs=[], id=None) %}
<div {{ apply_prop('id', id) }} class="{{ apply_classes(classes + ['btn-group']) }}" {{ apply_dattrs(data_attrs) }} role="group">
    {% for type, text in dlist.items() %}
        <button type="button" class="btn btn-{{ size }} btn-{{ type }}">{{ text }}</button>
    {% endfor %}
</div>
{% endmacro -%}


{%- macro list2btn_group(list, btn_types='default', size='md', classes=[], data_attrs=[], id=None) %}
<div {{ apply_prop('id', id) }} class="{{ apply_classes(classes + ['btn-group']) }}" {{ apply_dattrs(data_attrs) }} role="group">
    {% for item in list %}
        <button type="button" class="btn btn-{{ size }} btn-{{ btn_types }}">{{ item }}</button>
    {% endfor %}
</div>
{% endmacro -%}


{%- macro dict2_pagination(dct, prev=None, next=None, size='md', classes=[], data_attrs=[], id=None, disabled=[]) %}
<nav aria-label="Page navigation" {{ apply_prop('id', id) }}>
  <ul class="pagination-{{ size }} pagination" class="{{ apply_classes(classes + ['btn-group']) }}" {{ apply_dattrs(data_attrs) }}>
    {% if prev %}
    <li {% if 'prev' in disabled %}class="disabled"{% endif %}>
      <a href="{{ prev.keys()[0] }}" aria-label="Previous">
        <span aria-hidden="true">{{ prev.values()[0]|safe }}</span>
      </a>
    </li>
    {% endif %}
    {% for link, text in dct.items() %}
        <li {% if text in disabled %}class="disabled"{% endif %}>
            <a href="{{ link }}">{{ text }}</a>
        </li>
    {% endfor %}
    {% if next %}
    <li {% if 'next' in disabled %}class="disabled"{% endif %}>
      <a href="{{ next.keys()[0] }}" aria-label="Next">
        <span aria-hidden="true">{{ next.values()[0]|safe }}</span>
      </a>
    </li>
    {% endif %}
  </ul>
</nav>
{% endmacro -%}


{%- macro modal_carousel(
    items,
    active=0,
    show_arrows=True,
    show_footer=True,
    footer=None,
    id=None,
    fade=True,
    include_btn=False,
    close_btns=True,
    include_btn_text='Trigger Modal',
    title=None
)
%}
{{ modal(id,
    dict2carousel(id,
        items,
        active=active,
        show_arrows=show_arrows,
    ),
    close_btns=close_btns,
    fade=fade,
    show_footer=show_footer,
    footer=footer,
    include_btn=include_btn,
    include_btn_text=include_btn_text,
    title=title
    )
}}
{% endmacro -%}


{%- macro table_panels(items,
    paneltype='default',
    table_classes=[],
    table_data_attrs=[]
    )
-%}
    {% for item in items %}
        {% for title, data in item.items() %}
            {# Xform data into magical data-table html #}
            {% set html = objects2table(data, classes=table_classes, data_attrs=table_data_attrs)
            %}
            {{ bs3_panel({title: html}, paneltype=paneltype) }}
        {% endfor %}
    {% endfor %}
{% endmacro -%}