Automated listing visualization
Edit only a few settings to generate a modern looking visulization that uses ODS components and widgets.
Try it! Click on “Edit on CodePen” to browse the code and see the configuration. You only need to edit the first bloc of settings to try it on your own data!
Dataset in use: oeuvres-de-johannes-vermeer
(See it on userclub domain)
Fields in use:
<div class="container"
ng-init="domain = 'userclub.opendatasoft.com';
datasetid = 'oeuvres-de-johannes-vermeer';
sort = 'surface';
filters = [
{'id':'technique','multiple':true},
{'id':'collection','multiple':true},
{'id':'genre','multiple':true}
];
fieldDate = '';
resetFiltersButton = true;
resetFiltersButtonLabel = 'Supprimer tous les filtres';
fieldDefaultRangeStartsNow = false;
view = 'cards';
fieldsList = ['collection','genre','format','date'];
fieldLink = 'wikipedia';
fieldLinkLabel = 'Lire l\'article';
cardTitle = 'titre';
fieldPhoto = 'image';
imagePosition = 'left';
itemsPerRow = '2';
mapView = false;
mapPicto = '';
mapPictoColor = '';
kpis = [
{
'title': 'Taille moyenne',
'select': 'avg(surface)',
'precision': 2,
'unit': 'm2',
'faicon': 'square-o'
},
{
'title': 'Nombre d\'oeuvre référencées',
'select': 'count(*)'
}
];
sourceLinkLabel = 'Accéder aux données source';
noResultMsg = 'Aucun résultat';
DO_NOT_MODIFY_BELOW;
localctxfields = {};
values = {};
activeFilters = {};
localctx = undefined;
">
<ods-dataset-context context="localctx"
localctx-domain="{{ domain || localctx.domain }}"
localctx-dataset="{{ datasetid || localctx.dataset.datasetid }}">
{{ localctx = ctx || localctx ; '' }}
<!-- Private datasets can be accessed by adding an API Key.
Add this param to the <ods-dataset-context above,
where XXX is your apikey :
localctx-apikey="XXX"
-->
<ods-dataset-context ng-if="localctx.dataset.datasetid"
context="localctxdate"
localctxdate-domain="{{ localctx.domain }}"
localctxdate-dataset="{{ localctx.dataset.datasetid }}">
<span ng-if="sort">
{{localctx.parameters['sort'] = sort; ''}}
</span>
<span ng-if="fieldDefaultRangeStartsNow">
{{localctxdate.parameters={'q.date': fieldDate + '>#now()'}; ''}}
</span>
<div ng-show="!$parent.$parent.$parent.$parent.$parent || blocks">
<h1 class="page-title">
{{ localctx.dataset.metas.title }}
</h1>
<p class="page-subtitle" ng-bind-html="localctx.dataset.metas.description | shortSummary"></p>
</div>
<span ng-repeat="field in localctx.dataset.fields">
{{ localctxfields[field.name] = field.label; '' }}
</span>
<div ng-if="!$parent.$parent.$parent.$parent.$parent || blocks">
<span ng-repeat="filter in filters">
{{ localctx.parameters['refine.' + filter.id] = activeFilters[filter.id] ; '' }}
</span>
</div>
<div ng-show="!$parent.$parent.$parent.$parent.$parent || blocks" class="content-card search-module-container">
<!-- SEARCH -->
<div class="search-module">
<i class="fa fa-search search-module-icon" aria-hidden="true"></i>
<input placeholder="Rechercher"
ng-model="localctx.parameters['q']"
ng-model-options="{ updateOn: 'keyup', debounce: { 'default': 300, 'blur': 0 }}"
ng-change="localctx.parameters['start'] = undefined"
class="search-module-input"
type="text"/>
<button class="search-module-clear"
ng-if="localctx.parameters['q']"
ng-click="localctx.parameters['q'] = undefined;
localctx.parameters['start'] = undefined">
<i class="fa fa-times-circle" aria-hidden="true"></i>
</button>
</div>
<!-- FILTERS Search & Select -->
<div class="filter-list"
ng-init="dropdown.open = '';
select = {}">
<div ng-repeat="filter in filters">
{{ localctx.parameters['disjunctive.' + filter.id] = true; '' }}
<div ods-facet-results="categories"
ods-facet-results-facet-name="{{ filter.id }}"
ods-facet-results-context="localctx"
ods-facet-results-sort="alphanum">
<ods-select ng-if="categories"
selected-values="activeFilters[filter.id]"
multiple="filter.multiple"
options="categories"
label-modifier="name"
value-modifier="name"
on-change="localctx.parameters['start'] = undefined"
placeholder="{{ filter.label || localctxfields[filter.id] }}"></ods-select>
</div>
</div>
<div class="clear-filters"
ng-show="(activeFilters | values).join('') || localctx.parameters['geofilter.distance'] || localctx.parameters['geofilter.polygon']">
<button class="clear-filters-button"
ng-click="activeFilters = {};
localctx.parameters['geofilter.distance'] = undefined;
localctx.parameters['geofilter.polygon'] = undefined;
localctx.parameters['start'] = undefined">
{{ resetFiltersButtonLabel }}
<i class="fa fa-times-circle" aria-hidden="true"></i>
</button>
</div>
</div>
<!-- FILTERS date (if any) -->
<div class="filter-date"
ng-if="fieldDate">
<!--
On récupère la plage de date du jeu de données. On fait une analyse pour récupérer la première et dernière date du champs date_de_restitution
pour le min: conditionnelle pour ajouter le 0 si le mois ou le jour est inférieur à 10 pour avoir une date iso
[dateminmax.results.length-1] : permet de récupérer dynamiquement le dernier élément de l'array du results => donc ici on calcule le nombre d'éléments dans l'array pour la date, donc 2, et 2-1= 1 donc 2019
Pareil que précédemment, ne pas hésiter à enlever le '' pour voir le comportement
-->
<span ods-analysis="dateminmax"
ods-analysis-context="localctxdate"
ods-analysis-x-year="{{ fieldDate }}.year"
ods-analysis-x-month="{{ fieldDate }}.month"
ods-analysis-x-day="{{ fieldDate }}.day"
ods-analysis-serie-c="COUNT()"
ods-analysis-sort="x.{{ fieldDate }}.year,x.{{ fieldDate }}.month,x.{{ fieldDate }}.day">
<span ng-if="dateminmax.results && dateminmax.results.length > 0">
{{ values['periode']['min'] = dateminmax.results[0].x.year + '-' + (10 > dateminmax.results[0].x.month?'0':'') + dateminmax.results[0].x.month + '-' + (10 > dateminmax.results[0].x.day?'0':'') + dateminmax.results[0].x.day;
values['periode']['max'] = dateminmax.results[dateminmax.results.length-1].x.year + '-' + (10 > dateminmax.results[dateminmax.results.length-1].x.month?'0':'') + dateminmax.results[dateminmax.results.length-1].x.month + '-' + (10 > dateminmax.results[dateminmax.results.length-1].x.day?'0':'') + dateminmax.results[dateminmax.results.length-1].x.day;
''}}
</span>
</span>
<!-- Affichage du range slider: les dates récupérées dans l'analyse pour initialiser les bounds
la sélection période par défaut avec le mois + 1
le min/maxselection qui correspondent aux bornes choisies par le user -->
<ods-date-range-slider ng-if="values.periode.min && values.periode.max"
context="localctx"
initial-from="{{ values.periode.min }}"
initial-to="{{ values.periode.max }}"
start-bound="values.periode.min"
end-bound="values.periode.max"
date-field="{{ fieldDate }}"
precision="day"
from="values.periode.minselection"
to="values.periode.maxselection">
</ods-date-range-slider>
<!-- le click sur toute la période resette la période choisie sur la date min et max -->
<button ng-if="resetFiltersButton && values.periode.min && values.periode.max"
ng-disabled="values.periode.minselection == values.periode.min &&
values.periode.maxselection == values.periode.max"
class="filter-date-button"
ng-class="{'filter-date-button-disabled':
values.periode.minselection == values.periode.min &&
values.periode.maxselection == values.periode.max}"
ng-click="values.periode.minselection = values.periode.min;
values.periode.maxselection = values.periode.max;
localctx.parameters['start'] = undefined">
Toute la période <i class="fa fa-arrows-h" aria-hidden="true"></i>
</button>
</div>
</div>
<!-- KPIs -->
<section class="kpis-container row row-equal-height">
<div class="{{ 'col-md-' + (12/itemsPerRow) }} margin-bottom-20"
ng-repeat="kpi in kpis">
<!-- KPI box component -->
<div class="kpi-card"
tabindex="0"
ods-adv-analysis="agg"
ods-adv-analysis-context="localctx"
ods-adv-analysis-select="{{ kpi.select }} as x">
<i ng-if="kpi.faicon"
class="kpi-icon fa fa-{{ kpi.faicon }}" aria-hidden="true"></i>
<p class="kpi-title">
{{ (agg[0].x || 0) | number : (kpi.precision || 0) }}
<span ng-if="kpi.unit" class="kpi-unit">{{ kpi.unit }}</span>
</p>
<p class="kpi-description">
{{ kpi.title }}
</p>
</div>
</div>
</section>
<!-- Source link -->
<div ng-show="!$parent.$parent.$parent.$parent.$parent">
<div class="margin-bottom-20" style="text-align: right; width: 100%">
<a href="https://{{ domain }}/explore/dataset/{{ datasetid }}/"
target="_blank">{{ sourceLinkLabel }}</a>
</div>
</div>
<!-- TABLE -->
<section ng-if="view == 'table'"
class="content-card">
<div class="table-module">
<table class="table-basic"
id="tableref"
ods-results="items"
ods-results-context="localctx"
ods-results-max="10">
<!-- prevent user selection/filters to display a page that is off limits from results (for example: display page 12 of a list of less than 119 items) -->
{{ localctx.parameters['start'] =
(localctx.nhits - localctx.parameters['start']) <= 0 ? undefined : localctx.parameters['start'] ; '' }}
<thead>
<tr>
<th ng-if="!fieldsList[0].field"
ng-repeat="field in fieldsList">{{ localctxfields[field] }}</th>
<th ng-if="fieldsList[0].field"
ng-repeat="fieldconfig in fieldsList">{{ fieldconfig.label || localctxfields[fieldconfig.field] }}</th>
<th ng-if="fieldLink"></th>
</tr>
</thead>
<tbody>
<tr tabindex="0"
ng-repeat="item in items">
<td ng-if="!fieldsList[0].field"
ng-repeat="field in fieldsList"
style="max-width: calc(100vw / {{ fieldsList.length }});"
title="{{ localctxfields[field] }}">
{{ item.fields[field] }}
</td>
<td ng-if="fieldsList[0].field"
ng-repeat="fieldconfig in fieldsList"
style="max-width: calc(100vw / {{ fieldsList.length }});"
title="{{ localctxfields[fieldconfig.field] }}">
<span ng-switch="fieldconfig.format">
<span ng-switch-when="number">
{{ item.fields[fieldconfig.field] | number : (fieldconfig.options.precision || 0) }} {{ fieldconfig.options.unit }}
</span>
<span ng-switch-when="date">
{{ item.fields[fieldconfig.field] | date : (fieldconfig.options.dateFormat || 'mediumDate') }}
</span>
<span ng-switch-default>
{{ item.fields[fieldconfig.field] }}
</span>
</span>
</td>
<td ng-if="fieldLink">
<!-- if fieldLink is a json and contains the 'id' key, it means that the link points to a file hosted on ODS platform and can be downloaded. If it's an external link, the other link tag is used -->
<a ng-if="item.fields[fieldLink].id"
href="https://{{ domain }}/explore/dataset/{{ datasetid }}/files/{{ item.fields[fieldLink].id }}/download/"
target="_blank"
title="{{ fieldLinkLabel }}"
class="table-dropdown-button">
<i class="fa fa-external-link"></i>
</a>
<!-- fieldLink is used here
For more advanced scenario, you can send the user to :
- the dataset table filtered with the fieldLink value, through a text query
href="/explore/dataset/{{ datasetid }}/table?q={{ item.fields[fieldLink] }}"
- the dataset table filtered with the fieldLink value, through a refine on the field
href="/explore/dataset/{{ datasetid }}/table?refine.{{ fieldLink }}={{ item.fields[fieldLink] }}"
- a page using url-sync=true setting :
href="/pages/yourpage/?refine.{{ fieldLink }}={{ item.fields[fieldLink] }}"
-->
<a ng-if="!item.fields[fieldLink].id"
href="{{ item.fields[fieldLink] }}"
target="_blank"
title="{{ fieldLinkLabel }}"
class="table-dropdown-button">
<i class="fa fa-external-link"></i>
</a>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<ods-pagination-block ng-if="view == 'table'"
context="localctx"
per-page="10"
nofollow="true"
container-identifier="tableref">
</ods-pagination-block>
<!-- CARDS -->
<section ng-if="view == 'cards'"
class="row">
<div class="{{ 'col-md-' + (mapView?5:12) }} {{ mapView ? 'map-active':''}}">
<ods-infinite-scroll-results context="localctx"
list-class="row row-equal-height listing"
result-class="{{ 'col-md-' + (12/(mapView?1:itemsPerRow)) }} margin-bottom-20"
no-results-message="{{ noResultMsg }}">
<div class="content-card"
tabindex="0"
ng-class="{'content-card-horizontal': $parent.$parent.$parent.$parent.$parent.imagePosition === 'left' }">
<div class="content-card-img"
ng-if="item.fields[$parent.$parent.$parent.$parent.$parent.fieldPhoto]"
style="{{ 'background-image: url(https://' + $parent.$parent.$parent.$parent.$parent.domain + '/explore/dataset/' + $parent.$parent.$parent.$parent.$parent.datasetid + '/files/' + item.fields[$parent.$parent.$parent.$parent.$parent.fieldPhoto].id + '/300/);' }}">
</div>
<div class="content-card-body">
<h2 class="content-card-title text-center">
{{ item.fields[$parent.$parent.$parent.$parent.$parent.cardTitle] }}
</h2>
<div class="content-card-fields">
<!-- If fieldsList is an array of fields ID -->
<dl ng-if="!$parent.$parent.$parent.$parent.$parent.fieldsList[0].field">
<dt ng-repeat-start="field in $parent.$parent.$parent.$parent.$parent.fieldsList">
{{ $parent.$parent.$parent.$parent.$parent.localctxfields[field] }}
</dt>
<dd ng-repeat-end>
{{ item.fields[field] }}
</dd>
</dl>
<!-- If fieldsList is an object (advanced settings of fields) -->
<dl ng-if="$parent.$parent.$parent.$parent.$parent.fieldsList[0].field">
<dt ng-repeat-start="fieldconfig in $parent.$parent.$parent.$parent.$parent.fieldsList"
ng-show="item.fields[fieldconfig.field]">
{{ (fieldconfig|keys).indexOf('label')>=0?fieldconfig.label : $parent.$parent.$parent.$parent.$parent.localctxfields[fieldconfig.field] }}
</dt>
<dd ng-show="item.fields[fieldconfig.field]"
ng-switch="fieldconfig.format"
ng-repeat-end>
<span ng-switch-when="number">
{{ item.fields[fieldconfig.field] | number : (fieldconfig.options.precision || 0) }} {{ fieldconfig.options.unit }}
</span>
<span ng-switch-when="date">
{{ item.fields[fieldconfig.field] | date : (fieldconfig.options.dateFormat || 'mediumDate') }}
</span>
<span ng-switch-default>
{{ item.fields[fieldconfig.field] }}
</span>
</dd>
</dl>
</div>
<div ng-if="$parent.$parent.$parent.$parent.$parent.fieldLink" class="text-center">
<!-- if fieldLink is a json and contains the 'id' key, it means that the link points to a file hosted on ODS platform and can be downloaded. If it's an external link, the other link tag is used -->
<a ng-if="item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink].id"
href="https://{{ $parent.$parent.$parent.$parent.$parent.domain }}/explore/dataset/{{ $parent.$parent.$parent.$parent.$parent.datasetid }}/files/{{ item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink].id }}/download/"
target="_blank"
class="content-card-button">
{{ $parent.$parent.$parent.$parent.$parent.fieldLinkLabel }}
</a>
<!-- fieldLink is used here
For more advanced scenario, you can send the user to :
- the dataset table filtered with the fieldLink value, through a text query
href="/explore/dataset/{{ $parent.$parent.$parent.$parent.$parent.datasetid }}/table?q={{ item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink] }}"
- the dataset table filtered with the fieldLink value, through a refine on the field
href="/explore/dataset/{{ $parent.$parent.$parent.$parent.$parent.datasetid }}/table?refine.{{ $parent.$parent.$parent.$parent.$parent.fieldLink }}={{ item.fields[$root.fieldLink] }}"
- a page using url-sync=true setting :
href="/pages/yourpage/?refine.{{ $parent.$parent.$parent.$parent.$parent.fieldLink }}={{ item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink] }}"
-->
<a ng-if="!item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink].id" href="{{ item.fields[$parent.$parent.$parent.$parent.$parent.fieldLink] }}"
target="_blank"
class="content-card-button">
{{ $parent.$parent.$parent.$parent.$parent.fieldLinkLabel }}
</a>
</div>
</div>
</div>
</ods-infinite-scroll-results>
</div>
<div ng-if="mapView" class="col-md-7">
<ods-map no-refit="false"
scroll-wheel-zoom="false">
<ods-map-layer-group>
<ods-map-layer context="localctx"
color="{{ mapPictoColor }}"
picto="{{ mapPicto }}"
show-marker="true"
display="auto"
shape-opacity="0.5"
point-opacity="1"
border-color="#FFFFFF"
border-opacity="1"
border-size="1"
border-pattern="solid"
size="4"
size-min="3"
size-max="5"
size-function="linear"></ods-map-layer>
</ods-map-layer-group>
</ods-map>
</div>
</section>
</ods-dataset-context>
</ods-dataset-context>
</div>
/* General Layout
========================================================================== */
:root {
--secondary-color: black;
}
#page- {
margin: 6rem 0 3em 0;
}
/**
* License: MIT License
* Generated on 25 Jun 2021
* Author: Labs from opendatasoft
* Version: 0.3
* Description: ODS Layout over-ride to Flexbox system
*/
.row.full-height{height:100%}.row{display:flex;flex-wrap:wrap;justify-content: start;}.row>*{margin-bottom:20px}.order-0{order:0}@media screen and (min-width:750px){.order-sm-0{order:0}}@media screen and (min-width:970px){.order-md-0{order:0}}@media screen and (min-width:1170px){.order-lg-0{order:0}.order-lg-1{order:1}}.order-1{order:1}@media screen and (min-width:750px){.order-sm-1{order:1}}@media screen and (min-width:970px){.order-md-1{order:1}.order-md-2{order:2}}.order-2{order:2}@media screen and (min-width:750px){.order-sm-2{order:2}}@media screen and (min-width:1170px){.order-lg-2{order:2}.order-lg-3{order:3}}.order-3{order:3}@media screen and (min-width:750px){.order-sm-3{order:3}}@media screen and (min-width:970px){.order-md-3{order:3}.order-md-4{order:4}}.order-4{order:4}@media screen and (min-width:750px){.order-sm-4{order:4}}@media screen and (min-width:1170px){.order-lg-4{order:4}.order-lg-5{order:5}}.order-5{order:5}@media screen and (min-width:750px){.order-sm-5{order:5}}@media screen and (min-width:970px){.order-md-5{order:5}.order-md-6{order:6}}.order-6{order:6}@media screen and (min-width:750px){.order-sm-6{order:6}}@media screen and (min-width:1170px){.order-lg-6{order:6}.order-lg-7{order:7}}.order-7{order:7}@media screen and (min-width:750px){.order-sm-7{order:7}}@media screen and (min-width:970px){.order-md-7{order:7}.order-md-8{order:8}}.order-8{order:8}@media screen and (min-width:750px){.order-sm-8{order:8}}@media screen and (min-width:1170px){.order-lg-8{order:8}.order-lg-9{order:9}}.order-9{order:9}@media screen and (min-width:750px){.order-sm-9{order:9}}@media screen and (min-width:970px){.order-md-9{order:9}.order-md-10{order:10}}.order-10{order:10}@media screen and (min-width:750px){.order-sm-10{order:10}}@media screen and (min-width:1170px){.order-lg-10{order:10}.order-lg-11{order:11}}.order-11{order:11}@media screen and (min-width:750px){.order-sm-11{order:11}}@media screen and (min-width:970px){.order-md-11{order:11}.order-md-12{order:12}}.order-12{order:12}@media screen and (min-width:750px){.order-sm-12{order:12}}@media screen and (min-width:1170px){.order-lg-12{order:12}}@media screen and (max-width:1169px){:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-1,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-10,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-11,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-12,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-2,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-3,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-4,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-5,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-6,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-7,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-8,:not([class*=col-md]):not([class*=col-sm]):not([class*=col-xs]).col-lg-9{width:100%}}@media screen and (max-width:969px){:not([class*=col-sm]):not([class*=col-xs]).col-md-1,:not([class*=col-sm]):not([class*=col-xs]).col-md-10,:not([class*=col-sm]):not([class*=col-xs]).col-md-11,:not([class*=col-sm]):not([class*=col-xs]).col-md-12,:not([class*=col-sm]):not([class*=col-xs]).col-md-2,:not([class*=col-sm]):not([class*=col-xs]).col-md-3,:not([class*=col-sm]):not([class*=col-xs]).col-md-4,:not([class*=col-sm]):not([class*=col-xs]).col-md-5,:not([class*=col-sm]):not([class*=col-xs]).col-md-6,:not([class*=col-sm]):not([class*=col-xs]).col-md-7,:not([class*=col-sm]):not([class*=col-xs]).col-md-8,:not([class*=col-sm]):not([class*=col-xs]).col-md-9{width:100%}}@media screen and (max-width:749px){.row{flex-direction:column}:not([class*=col-xs]).col-sm-1,:not([class*=col-xs]).col-sm-10,:not([class*=col-xs]).col-sm-11,:not([class*=col-xs]).col-sm-12,:not([class*=col-xs]).col-sm-2,:not([class*=col-xs]).col-sm-3,:not([class*=col-xs]).col-sm-4,:not([class*=col-xs]).col-sm-5,:not([class*=col-xs]).col-sm-6,:not([class*=col-xs]).col-sm-7,:not([class*=col-xs]).col-sm-8,:not([class*=col-xs]).col-sm-9{width:100%}}.no-margin-bottom,.no-mb{margin-bottom:0!important}.no-margin-x,.no-mx{margin-left:0!important;margin-right:0!important}
.page-title {
font-size: 3rem;
font-weight: bold;
margin-top: 0;
margin-bottom: 1rem;
}
.page-subtitle {
font-size: 1.2rem;
line-height: 2;
margin-top: 0;
margin-bottom: 3rem;
}
.margin-bottom-20 {
margin-bottom: 20px;
}
/* Search Module
========================================================================== */
.search-module-container {
padding: 26px;
margin: 20px 0;
}
.search-module {
display: flex;
align-items: stretch;
border-bottom: 1px solid #dee5ef;
margin-bottom: 13px;
transition: all .2s;
}
.search-module:hover,
.search-module:focus-within {
border-bottom-color: var(--links);
}
.search-module-icon {
color: #898d92;
margin-right: 8px;
align-self: center;
}
.search-module-input {
background-color: transparent;
width: 100%;
outline: none;
border: none;
padding: 12px 0;
transition: all .2s;
color: var(--text);
}
.search-module-input::placeholder {
transition: all .2s;
}
.search-module-clear {
color: #898d92;
font-size: 1rem;
background: transparent;
border: none;
margin: 0;
outline: none;
padding: 0 0 0 12px;
transition: all .2s;
}
.search-module-clear:hover {
opacity: .65;
}
.search-module:hover .search-module-icon,
.search-module:focus-within .search-module-icon,
.search-module:hover .search-module-input::placeholder,
.search-module:focus-within .search-module-input::placeholder {
color: var(--links)
}
/* Filters
========================================================================== */
.filter-list {
display: flex;
flex-wrap: wrap;
position: relative;
}
.filter-list > * {
margin: 0 0 10px;
width: 100%;
}
.odswidget-select .odswidget-select-dropdown.open .odswidget-select-dropdown-menu {
width: 100%
}
.clear-filters {
display: flex;
align-items: center;
justify-content: center;
}
.clear-filters-button {
background: none;
border: none;
color: var(--text);
}
.clear-filters-button:hover {
opacity: 0.65;
}
.odswidget-select,
.odswidget-select .odswidget-select-dropdown {
width: 100%;
}
@media screen and (min-width: 500px) {
.filter-list > * {
margin: 0 10px 10px 0;
width: inherit;
}
.odswidget-select .odswidget-select-dropdown.open .odswidget-select-dropdown-menu {
width: max-content;
min-width: 240px;
}
}
.odswidget-select-button:focus:not(.focus-ring) {
outline: var(--highlight) 2px solid;
}
/*********** Filter date ************/
.filter-date {
display: flex;
flex-direction: column;
align-items: center;
margin: 13px 26px 32px 26px;
}
.odswidget-date-range-slider {
width: 100%;
}
.filter-date-button {
margin-left: 0;
margin-top: 13px;
white-space: nowrap;
text-decoration: underline;
color: var(--text);
background: none;
border: none;
}
.filter-date-button:not(.filter-date-button-disabled):hover {
opacity: 0.65;
}
.filter-date-button-disabled {
opacity: 0.5;
pointer-event: none;
text-decoration: none;
}
@media screen and (min-width: 500px) {
.filter-date {
flex-direction: row;
}
.filter-date-button {
margin-left: 50px;
margin-top: 0;
}
}
/* date range slider style override */
.odswidget-date-range-slider .irs--flat .irs-from, .odswidget-date-range-slider .irs--flat .irs-single, .odswidget-date-range-slider .irs--flat .irs-to {
color: var(--text);
border: 1px solid #cbd2db;
border-radius: 2rem;
background: #FFFFFF;
}
.odswidget-date-range-slider .irs--flat .irs-from:before, .odswidget-date-range-slider .irs--flat .irs-single:before, .odswidget-date-range-slider .irs--flat .irs-to:before {
border-top-color: var(--text);
}
.odswidget-date-range-slider .irs--flat .irs-bar {
background-color: var(--highlight);
}
.odswidget-date-range-slider .irs--flat .irs-handle>i:first-child {
background-color: var(--highlight);
}
.odswidget-date-range-slider .irs--flat .irs-handle.state_hover>i:first-child, .odswidget-date-range-slider .irs--flat .irs-handle:hover>i:first-child {
background-color: var(--text);
}
/* Content Card
========================================================================== */
.content-card {
background-color: var(--boxes-background);
border-radius: 4px;
height: 100%;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13);
}
.content-card-horizontal {
display: flex;
}
.content-card-horizontal .content-card-img {
height: auto;
flex: 0 0 25%;
width: 25%;
border-radius: 4px 0 0 4px;
}
.content-card-img {
display: block;
height: 110px;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
.content-card-body {
padding: 26px;
flex: 1 1 auto;
}
.content-card-title {
color: var(--titles);
font-size: 1.2rem;
line-height: 1.5;
font-weight: normal;
margin-top: 0;
margin-bottom: 13px;
max-width: 100%;
}
.content-card-description {
color: var(--text);
font-size: 1rem;
line-height: 1.5;
font-weight: normal;
margin-top: 0;
margin-bottom: 26px;
max-width: 100%;
}
.content-card-fields dt {
font-size: 0.8rem;
opacity: 0.8;
}
.content-card-fields dd {
margin-left: 0;
}
.content-card-icon {
color: var(--highlight);
font-size: 2rem;
margin-bottom: 13px;
max-width: 100%;
}
.content-card-link {
color: var(--links);
font-weight: bold;
transition: all .2s;
opacity: 1;
max-width: 100%;
}
.content-card-link:hover {
opacity: .7;
text-decoration: none;
}
.content-card-button {
color: var(--highlight);
border: 1px solid var(--highlight);
background: transparent;
display: inline-block;
text-align: center;
font-size: .867rem;
border-radius: 4px;
padding: .5rem 1.15rem;
text-decoration: none;
transition: all .2s;
}
.content-card-button:hover {
background-color: var(--highlight);
color: #FFFFFF;
text-decoration: none;
}
/* KPI Card
========================================================================== */
@media screen and (min-width: 992px) {
.kpis-container {
display: flex;
justify-content: center;
}
}
.kpi-card {
background-color: var(--boxes-background);
height: 100%;
padding: 39px;
border-radius: 4px;
box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.13);
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
text-align: center;
}
.kpi-icon {
color: var(--highlight);
color: var(--secondary-color);
font-size: 4rem;
margin-top: 0;
margin-bottom: 13px;
max-width: 100%;
}
.kpi-title {
font-weight: normal;
color: var(--highlight);
font-size: 3.2rem;
margin-top: 0;
margin-bottom: 13px;
max-width: 100%;
}
.kpi-unit {
font-size: 0.8em;
color: var(--secondary-color);
}
.kpi-description {
color: var(--text);
font-size: 1rem;
line-height: 1.5;
font-weight: normal;
margin-top: 0;
margin-bottom: 0;
max-width: 100%;
}
/* Table Module Basic
========================================================================== */
.table-basic {
display: table;
border-collapse: collapse;
width: 100%;
white-space: nowrap;
background-color: #FFFFFF;
margin-bottom: 20px;
}
.table-basic thead th {
color: var(--titles);
background-color: #f6f8fb;
font-weight: 500;
padding: 13px 3px;
position: sticky;
top: 0;
z-index: 1;
}
.table-basic thead th:first-child,
.table-basic tbody td:first-child {
padding-left: 13px;
}
.table-basic tr td,
.table-basic tbody th {
font-weight: normal;
border-top: 1px solid #dee5ef;
}
.table-basic tr td {
padding: 13px 3px;
}
.table-basic td {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.table-dropdown-button {
width: 28px;
height: 28px;
padding: 0;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
background: white;
border: 1px solid transparent;
border-radius: 4px;
margin-left: auto;
}
.table-basic tr:hover .table-dropdown-button {
border-color: var(--highlight);
text-decoration: none;
}
.odswidget-pagination {
margin-bottom: 50px;
}
/* Map */
.odswidget-map__map {
min-height: 550px;
}
.map-active {
height: 550px;
overflow-y: auto;
}
/** Specific override for custom views **/
@media screen and (min-width: 1408px) {
.ods-dataset-visualization .ods-tabs__pane .container:not(.is-max-desktop):not(.is-max-widescreen) {
max-width: 100%;
}
}
@media screen and (min-width: 1216px) {
.ods-dataset-visualization .ods-tabs__pane .container:not(.is-max-desktop) {
max-width: 100%;
}
}
@media screen and (min-width: 1024px) {
.ods-dataset-visualization .ods-tabs__pane .container {
max-width: 100%;
}
}