Components - Filters

Filters in a drawer

This drawer is useful for interactive dashboards with filters that need to be hidden in a drawer on a side to offer more visibility/space for the data-visualization and content. The drawer contains a header (with a close button in the example), a content (with ods-facets), and a footer (empty here).

<ods-dataset-context context="ctx"
                     ctx-dataset="us-hospitals"
                     ctx-domain="userclub">
    <div class="fullpage-app"
         ng-init="modalswitchstatus">

        <!-- THE RIGHT MODAL -->
        <div class="rightmodal">
            <div class="backdrop"
                 ng-click="modalswitchstatus = !modalswitchstatus"
                 ng-class="{'backdrop-active':modalswitchstatus}">
            </div>
            <div class="cl-modal"
                 ng-class="{'cl-modal-active':modalswitchstatus}">
                <div class="cl-modal__rightside">
                    <div class="cl-modal__header"
                         ng-click="modalswitchstatus = !modalswitchstatus">
                        <a href="#"
                           class="closebtn">
                            Close <i class="fa fa-times-circle" aria-hidden="true"></i>
                        </a>
                    </div>
                    <div class="cl-modal__content">
                        <ods-facets context="ctx"></ods-facets>
                    </div>
                    <div class="cl-modal__footer">
                        <div class="">
                            Footer
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- THE CONTENT -->
        <div class="content container">
            <div class="level">
                <h2>
                    Page content
                </h2>
                <button ng-click="modalswitchstatus = !modalswitchstatus">
                    Filtrer data
                </button>
            </div>
            <ods-filter-summary context="ctx">
                {{ refinements }}
            </ods-filter-summary>
            <ods-table context="ctx"></ods-table>
        </div>

    </div>
</ods-dataset-context>
:root {
    /* change values by the size of the header & the footer to make the modal fit your page properly */
    --header-size: 20px;
    --footer-size: 20px;
    --modal-margin: calc(var(--header-size) + var(--footer-size));
}

/* ALL PAGE CONTAINER */
.fullpage-app {
    /* prerequisite for full height modal bar */
    position: relative;
    width: 100%;
    /* prevent the page to be shrinked if the content is less than the browser height, force a minimal height */
    min-height: calc(100vh - var(--modal-margin));
}

.level {
    display:flex;
    justify-content: space-between;
}

/** FILTER SUMMARY */
ul.odswidget.odswidget-filter-summary {
    margin: 15px 0;
    position: relative;
}
@media (min-width: 420px) {
    ul.odswidget.odswidget-filter-summary {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
    }
}
li.odswidget-filter-summary__active-filter:first-child {
    margin-left: 100px;
}
li.odswidget-filter-summary__active-filter:first-child:before {
    content: 'Active filters: ';
    font-weight: 600;
    line-height: 1.8em;
    position: absolute;
    left: 0;
}
li.odswidget-filter-summary__active-filter,
li.odswidget-filter-summary__clear-all {
    border-radius: 3px;
    margin: 2px 4px;
    padding: 0;
}
li.odswidget-filter-summary__active-filter {
    background-color: #f3f3f3;
}

a.odswidget-filter-summary__active-filter-link,
a.odswidget-clear-all-filters {
    border-left: none;
    margin: 0;
    padding: 2px 4px 2px 2px;
    display: flex;
}
a.odswidget-clear-all-filters {
    align-items: center;
}
a.odswidget-clear-all-filters i {
    margin-right: 3px;
}



/* BACKDROP (black screen when modal active) */
.backdrop {
    visibility: hidden;
    opacity: 0;
    position: absolute;
    height: 100%;
    width: 100%;
    z-index: 900;
    background-color: #000;
    cursor: default;
    -webkit-transition: visibility .5s,opacity .5s;
    transition: visibility .5s,opacity .5s;
}
.backdrop-active {
    visibility: visible;
    opacity: .6; /* make a more or less darker backdrop, 1:totally dark, 0:totally transparent (invisibile) */
}
/* MODAL */
/* The modal is composed of 2 parts:
the leftside, that is the banner that is always visible, and contains an icon on top to click and actiavte the modal
the right side, that contains filters, searchbar, charts etc... that is not visible, activated on demand
the modal default size (width) is 57px, it correspond to the leftside width */
.cl-modal {
    display: flex;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 0;
    background-color: white;
    z-index: 1000;
    overflow: hidden; /* Disable horizontal scroll */
    transition: ease-in 0.3s;
}
.cl-modal-active {
    width: 400px; /* size of the modal when active */
}
/* specific because charts are loaded inside */
.cl-modal__content {
    width: 400px;
}
@media (max-width: 420px) {
    .cl-modal-active {
        width: 100%;
    }
    .cl-modal__content {
        width: 100%;
    }
}

/* SHOW/HIDE SIDE */
.cl-modal__rightside {
    display: flex;
    flex-direction: column;
    border-left: solid 1px lightgrey;
}
.cl-modal__header,
.cl-modal__footer {
    min-height: 35px;
    background-color: white;
    padding: 0 10px;
    display: flex;
    align-items: center;
    justify-content: start;
    opacity: 0;
    transition: opacity 0s;
}
.cl-modal-active .cl-modal__header, .cl-modal-active .cl-modal__footer {
    opacity: 1;
    transition-delay: 0.3s;
}
/* CONTENT OF THE MODAL */
/* HEADER / UPPER PART */
.cl-modal__header {
    border-bottom: 1px solid lightgrey;
}
/* FOOTER / LOWER PART (optionnal) */
.cl-modal__footer {
    border-top: 1px solid lightgrey;
}
/* THEN THE MIDDLE / CONTENT */
.cl-modal__content {
    height: 100%;
    overflow-y: auto;
    padding: 25px;
    opacity: 0;
    transition: opacity 0s;
}
/* WARNING ! This part is very important : it renders the content only after the end of the openning of the modal
ie. when the modal is resized, nothing is visible, after 0.3s of resizing, it's displays the content
this trick is very important to avoid the browser the resize all the content during the modal opening */
.cl-modal-active .cl-modal__content {
    opacity: 1;
    transition-delay: 0.3s;
}

.content {
    padding-top: 20px;
}

Accordion Filter

Make your page have a cleaner look by putting the filters hidden in an accordion which can be opened and closed with a button click. The layout will adapt automatically depending on the amount of filters added.

<ods-dataset-context context="ctx" 
                     ctx-dataset="us-hospitals"
                     ctx-domain="userclub">
    <div class="container">

        <section class="accordion"
                 ng-init="is_open = false">
            <button class="accordion-button"
                    ng-class="{ 'is-active': is_open }"
                    ng-click="is_open = !is_open">
                <i class="fa fa-filter" 
                   aria-hidden="true"></i>
                <span class="accordion-button-text">
                    Filter
                </span>
                <i class="fa" 
                   ng-class="{ 'fa-angle-up': is_open, 'fa-angle-down': !is_open }"
                   aria-hidden="true"></i>
            </button>

            <ods-filter-summary context="ctx">
                {{ refinements }}
            </ods-filter-summary>

            <ods-facets context="ctx">
                <div class="accordion-content"
                    ng-class="{ 'is-open': is_open }">
                    
                    <div class="accordion-filter">
                        <h2 class="accordion-filter-title">
                            State
                        </h2>
                        <div class="accordion-filter-results">
                            <ods-facet name="state"
                                       value-search="true"
                                       disjunctive="true"
                                       visible-items="50"></ods-facet>
                        </div>
                    </div>

                    <div class="accordion-filter">
                        <h2 class="accordion-filter-title">
                            City
                        </h2>
                        <div class="accordion-filter-results">
                            <ods-facet name="city"
                                       value-search="true"
                                       disjunctive="true"
                                       visible-items="100"></ods-facet>
                        </div>
                    </div>

                    <div class="accordion-filter">
                        <h2 class="accordion-filter-title">
                           Status
                        </h2>
                        <div class="accordion-filter-results">
                            <ods-facet name="status"
                                       disjunctive="true"
                                       visible-items="100"></ods-facet>
                        </div>
                    </div>

                    <div class="accordion-filter">
                        <h2 class="accordion-filter-title">
                            Type
                        </h2>
                        <div class="accordion-filter-results">
                            <ods-facet name="type"
                                       disjunctive="true"
                                       visible-items="100"></ods-facet>
                        </div>
                    </div>
                </div>
            </ods-facets>
        </section>

        <ods-table context="ctx"></ods-table>

    </div>
</ods-dataset-context>
/* Accordion filters
   ========================================================================== */

.accordion {
    margin-bottom: 20px;
    padding-bottom: 13px;
    border-bottom: 1px solid rgba(0,0,0,.1);
}

.accordion-button {
    color: #FFFFFF;
    background-color: var(--highlight);
    border: 1px solid var(--highlight);
    font-size: 1rem;
    font-weight: normal;
    margin-bottom: 13px;
    border-radius: 100px; 
    padding: .5rem 1.2rem;
    transition: all .2s;
}

.accordion-button:hover {
    opacity: .65;
}

.accordion-button.is-active {
    box-shadow: 0 0 0 3px rgba(115, 115, 115,.2);
}

.accordion-button-text {
    margin-right: 10px;
    margin-left: 3px;
}

.accordion-content {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    height: 400px;
    max-height: 0;
    overflow: hidden;
    transition: all .25s;
}

@media screen and (min-width: 768px) {
    .accordion-content {
        display: flex;
        margin-top: -.75rem;
        margin-left: -.75rem;
        margin-right: -.75rem;        
    }
}

.accordion-content.is-open {
    max-height: 800px;
}

.accordion-filter {
    max-height: 100%;
    overflow: auto;
    padding: .75rem;
}

@media screen and (min-width: 768px) {
    .accordion-filter {
        max-height: none;
        overflow: visible;
        flex: 1 1 0;
    }
}

.accordion-filter-title {
    font-weight: bold;
    font-size: 1rem;
    margin-top: 13px;
    margin-bottom: 13px;
    padding-top: 8px;
    padding-bottom: 13px;
    border-bottom: 1px solid rgba(0,0,0,.1);
}

.accordion-filter-results {
    max-height: 100%;
    overflow: auto;
    padding-bottom: 2rem;
}

/**** Filter summary ****/

.odswidget.odswidget-filter-summary {
    position: relative;
}

@media (min-width: 420px) {
    .odswidget.odswidget-filter-summary {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
    }
}

.odswidget-filter-summary__active-filter,
.odswidget-filter-summary__clear-all {
    border-radius: 3px;
    margin-right: 4px;
    margin-bottom: 2px;
    padding: 0;
}

.odswidget-filter-summary__active-filter {
    background-color: #f3f3f3;
}

.odswidget-filter-summary__active-filter-link,
.odswidget-clear-all-filters {
    border-left: none;
    margin: 0;
    padding: 2px 4px 2px 2px;
    display: flex;
    align-items: center;
}

.odswidget-filter-summary__active-filter-link::after {
    content: '\f057';
    font-family: 'FontAwesome';
    line-height: 1;
    margin-left: 8px;
    opacity: .65;
}

.odswidget-clear-all-filters {
    color: var(--links);
}

.odswidget-clear-all-filters i {
    display: none;
}