Commit initial
This commit is contained in:
commit
54a1e935ac
111
Views/FiltersView.php
Normal file
111
Views/FiltersView.php
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FiltersView
|
||||||
|
* Gère l'affichage des filtres de mesures
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class FiltersView {
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
add_shortcode('graph_mesures', __CLASS__.'::create_measures_filters');
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Ajout des scripts et styles
|
||||||
|
**/
|
||||||
|
// TODO : Réfléchir à un moyen de n'inclure tout ce bouzin uniquement si nécessaire (quand filtres appelés)
|
||||||
|
add_action('wp_enqueue_scripts', __CLASS__.'::enqueue_filters_scripts', 20);
|
||||||
|
add_action( 'wp_enqueue_scripts', __CLASS__.'::enqueue_filters_style' );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function enqueue_filters_scripts() {
|
||||||
|
wp_register_script('filtersJS', plugin_dir_url(__FILE__) . 'inc/filters.js', array('jquery'),'1.1', true);
|
||||||
|
wp_enqueue_script('filtersJS');
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function enqueue_filters_style() {
|
||||||
|
wp_enqueue_style( 'filters-style', plugin_dir_url(__FILE__) . 'inc/filters.css' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère la liste des filtres pour les mesures affichées
|
||||||
|
*/
|
||||||
|
public static function create_measures_filters() {
|
||||||
|
|
||||||
|
// Total des articles
|
||||||
|
$total = wp_count_posts('post')->publish;
|
||||||
|
|
||||||
|
// Ensuite, pour chaque catégorie donnée, on va :
|
||||||
|
// récupérer le nombre de posts de cette cat
|
||||||
|
// récupérer la couleur associée
|
||||||
|
// calculer le pourcentage sur le total de mesures
|
||||||
|
// créer un bloc html avec la data correspondante
|
||||||
|
|
||||||
|
$stats = ["validated", "partially-validated", "discussed", "danger", "rejected", "undiscussed"];
|
||||||
|
$obj_total = 0;
|
||||||
|
|
||||||
|
|
||||||
|
$html = "";
|
||||||
|
|
||||||
|
// C'parti pour le cacamembert
|
||||||
|
|
||||||
|
$pie_chart = '<div class="pie">';
|
||||||
|
$labels = "<div class='home-legend-items'>";
|
||||||
|
|
||||||
|
foreach ($stats as $stat) {
|
||||||
|
|
||||||
|
// TODO : Sortir la requête du foreach et monter un modèle pour les taxonomies (pour embarquer la couleur)
|
||||||
|
$stat = get_term_by('slug', $stat, 'post-status');
|
||||||
|
$color = get_field('couleur', "category_" . $stat->term_id);
|
||||||
|
$percent = $stat->count * 100 / $total;
|
||||||
|
$huge = $percent > 50 ? 1 : 0;
|
||||||
|
|
||||||
|
static $offset = 0;
|
||||||
|
|
||||||
|
$pie_chart .= "<div class='pie__segment $stat->slug' status='$stat->slug'
|
||||||
|
style='--offset: $offset; --value: $percent; --bg: $color; --over50: $huge;'
|
||||||
|
filter-label='$stat->name'
|
||||||
|
filter-id='$stat->slug'>
|
||||||
|
<p class='label statusTotal $stat->slug'>$stat->count</p>
|
||||||
|
</div>";
|
||||||
|
|
||||||
|
$offset += $percent;
|
||||||
|
|
||||||
|
$labels .= "<div class='home-legend-item'>";
|
||||||
|
$labels .= "<div class='legend-color' style='background-color: $color;'></div>";
|
||||||
|
$labels .= "<p class='status-title'
|
||||||
|
status='". $stat->slug ."'
|
||||||
|
filter-label='$stat->name'
|
||||||
|
filter-id='$stat->slug'>
|
||||||
|
$stat->name ( <span class='statusTotal $stat->slug'>$stat->count</span> )</p>";
|
||||||
|
$labels .= "</div>";
|
||||||
|
|
||||||
|
if (in_array($stat->slug, ['validated', 'partially-validated'])) {
|
||||||
|
$obj_total += $stat->count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$pie_chart .= '</div>';
|
||||||
|
$labels .= '</div>';
|
||||||
|
|
||||||
|
//ok, on monte les filtres par catégorie maintenant
|
||||||
|
$cats_title = "<p class='filters-cats-label'> Thématique </p>";
|
||||||
|
$cats = "<div class='filters-categories'>
|
||||||
|
<select name='cat' id='filters_cat_select' class='postform'>
|
||||||
|
<option value='' selected='selected'>Toutes les thématiques</option>";
|
||||||
|
|
||||||
|
foreach (get_categories(['parent' => 0]) as $the_cat) {
|
||||||
|
$cats .= "<option class='topic'
|
||||||
|
value='$the_cat->term_id'
|
||||||
|
filter-label='$the_cat->name'
|
||||||
|
filter-id='$the_cat->term_id'>
|
||||||
|
$the_cat->name</option>";
|
||||||
|
|
||||||
|
}
|
||||||
|
$cats .= "</select></div>";
|
||||||
|
|
||||||
|
return '<div id="measures-filters-container">' . $cats_title . $cats . $pie_chart . $labels . '</div>' ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
94
Views/inc/filters.css
Normal file
94
Views/inc/filters.css
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
** Filters graph & selection
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* Thematiques */
|
||||||
|
|
||||||
|
.filters-cats-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: .8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters-categories select {
|
||||||
|
font-size: .6em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Pie chart */
|
||||||
|
|
||||||
|
.pie {
|
||||||
|
border-radius: 100%;
|
||||||
|
height: calc(var(--size, 250) * 1px);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: calc(var(--size, 250) * 1px);
|
||||||
|
}
|
||||||
|
.pie__segment {
|
||||||
|
--a: calc(var(--over50, 0) * -100%);
|
||||||
|
--b: calc((1 + var(--over50, 0)) * 100%);
|
||||||
|
--degrees: calc((var(--offset, 0) / 100) * 360);
|
||||||
|
-webkit-clip-path: polygon(var(--a) var(--a), var(--b) var(--a), var(--b) var(--b), var(--a) var(--b));
|
||||||
|
clip-path: polygon(var(--a) var(--a), var(--b) var(--a), var(--b) var(--b), var(--a) var(--b));
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
transform: translate(0, -50%) rotate(90deg) rotate(calc(var(--degrees) * 1deg));
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: calc(1 + var(--over50));
|
||||||
|
}
|
||||||
|
.pie__segment:after,
|
||||||
|
.pie__segment:before {
|
||||||
|
background: var(--bg, #e74c3c);
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.pie__segment:before {
|
||||||
|
--degrees: calc((var(--value, 45) / 100) * 360);
|
||||||
|
transform: translate(0, 100%) rotate(calc(var(--degrees) * 1deg));
|
||||||
|
transform-origin: 50% 0%;
|
||||||
|
}
|
||||||
|
.pie__segment:after {
|
||||||
|
opacity: var(--over50, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* au cas où on en revienne aux labels dans les slices :*/
|
||||||
|
|
||||||
|
.pie__segment .label {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -30px;
|
||||||
|
color: #fff;
|
||||||
|
z-index: 99;
|
||||||
|
transform: translate(0, -50%) rotate(-90deg) rotate(calc(var(--degrees) * -1deg));
|
||||||
|
left: 8px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: .8em;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Legends ***/
|
||||||
|
|
||||||
|
.home-legend-items {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-legend-item {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-legend-item .legend-color {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
225
Views/inc/filters.js
Normal file
225
Views/inc/filters.js
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
jQuery( document ).ready(function($) {
|
||||||
|
|
||||||
|
/** Filtrage des propositions **/
|
||||||
|
|
||||||
|
// On crée les var : boite à filtre, et un tableua pour stocker lesdits filtres
|
||||||
|
var filters_div = $('#filters-container');
|
||||||
|
var current_filters = new Object();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter_actions
|
||||||
|
* ajoute ou supprime les filtres au tableau et la classe selected aux filtres, en fonction de l'élément (et de si c'est select ou statut)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function filters_action(elem, currentFilters) {
|
||||||
|
//Si c'est le select qui a changé, on supprime les filtres par catégorie existants
|
||||||
|
if (elem.nodeName === 'OPTION') {
|
||||||
|
$('#filters_cat_select option').removeClass('active');
|
||||||
|
currentFilters = new Object();
|
||||||
|
$(filters_div).children().remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si la valeur est nulle on arrête tout
|
||||||
|
if (!elem.hasAttribute('filter-id')) {
|
||||||
|
$(filters_div).trigger("filters-changed", [currentFilters]);
|
||||||
|
changePie(elem, currentFilters);
|
||||||
|
return currentFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sinon s'occupe du tableau et de la classe
|
||||||
|
var filter_id = elem.getAttribute('filter-id');
|
||||||
|
var filter_label = elem.getAttribute('filter-label');
|
||||||
|
|
||||||
|
// Si c'est un filtre de thématique qui a été supprimé, on réinitialise le select et les filtres
|
||||||
|
if ($(elem).hasClass('measure-filter') && !isNaN(parseInt(filter_id))) {
|
||||||
|
$('#filters_cat_select').prop('selectedIndex', 0);
|
||||||
|
// TODO : supprimer les autres filtres (status) si on supprime une catégorie
|
||||||
|
}
|
||||||
|
|
||||||
|
// Il y est déjà : on supprime le filtre
|
||||||
|
if (filter_id in currentFilters) {
|
||||||
|
//suppression du tableau
|
||||||
|
delete currentFilters[filter_id];
|
||||||
|
//suppression de la barre des filtres
|
||||||
|
$(filters_div).children('[filter-id="' + filter_id + '"]').remove();
|
||||||
|
}
|
||||||
|
// il y est pas : on l'ajoute
|
||||||
|
else {
|
||||||
|
//ajout dans le tableau
|
||||||
|
currentFilters[filter_id] = filter_label;
|
||||||
|
// Ajout dans la barre
|
||||||
|
var htmlFilter = "<div class='measure-filter' filter-id='" + filter_id + "'><p class='name'>" + filter_label + "</p><div class='filter-close'>+</div></div>";
|
||||||
|
filters_div.append(htmlFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// on switche la classe
|
||||||
|
$('#measures-filters-container [filter-id="' + filter_id + '"]').toggleClass('active');
|
||||||
|
|
||||||
|
console.log($(elem).attr('filter-id'));
|
||||||
|
if(!isNaN(filter_id)){
|
||||||
|
changePie(elem, currentFilters);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enfin on déclenche l'event filters-change
|
||||||
|
$(filters_div).trigger("filters-changed", [currentFilters]);
|
||||||
|
|
||||||
|
// On renvoie la liste des filtres mise à jour;
|
||||||
|
return currentFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Au clic sur un élément (ou change du select), on appelle la fonction avec l'élément (id&label) en argument
|
||||||
|
|
||||||
|
//Les statuts
|
||||||
|
$(".home-legend-item .status-title, .pie .pie__segment").click(function(){
|
||||||
|
current_filters = filters_action(this, current_filters);
|
||||||
|
});
|
||||||
|
// les catégories
|
||||||
|
$("#filters_cat_select").change(function(){
|
||||||
|
var elem = $(this).children(':selected').get(0);
|
||||||
|
current_filters = filters_action(elem, current_filters);
|
||||||
|
|
||||||
|
// on change la pie
|
||||||
|
changePie(elem, current_filters);
|
||||||
|
});
|
||||||
|
// La barre de filtres
|
||||||
|
|
||||||
|
$("body").on("click", "div.filters-container .filter-close", function (){
|
||||||
|
elem = $(this).closest('.measure-filter').get(0);
|
||||||
|
current_filters = filters_action(elem, current_filters);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filters-changed
|
||||||
|
* Evènement gérant l'affichage/masquage des mesures selon les filtres
|
||||||
|
*/
|
||||||
|
$(filters_div).on("filters-changed", function(event, currentFilters) {
|
||||||
|
|
||||||
|
var measures = $("article.measure-wrapper");
|
||||||
|
|
||||||
|
// si aucun filtre on affiche tout
|
||||||
|
if ($.isEmptyObject(currentFilters)) {
|
||||||
|
measures.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On récupère les filtres triés
|
||||||
|
var filters = filtersSeparation(currentFilters);
|
||||||
|
|
||||||
|
// On filtre pour afficher/masquer ce qui correspond aux bonnes classe
|
||||||
|
measures.filter(filters[0] + filters[1].join(', ' + filters[0] )).show();
|
||||||
|
measures.not(filters[0] + filters[1].join(', ' + filters[0] )).hide();
|
||||||
|
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* filtersSeparation
|
||||||
|
* Spare les filtres en catégories et statuts
|
||||||
|
*/
|
||||||
|
function filtersSeparation(currentFilters){
|
||||||
|
|
||||||
|
// On parcourt le tableau actuel des filtres, et on récupère les ID
|
||||||
|
var idsToShow = Object.keys(currentFilters);
|
||||||
|
// séparer catégories et statut : si une valeur nupérique est présente dans les filtres, on la récupère et l'extrait.
|
||||||
|
var cat = '';
|
||||||
|
var status = [];
|
||||||
|
|
||||||
|
// On parcourt la liste des id et on sépare cat et stat
|
||||||
|
for (var i = 0; i < idsToShow.length; i++) {
|
||||||
|
|
||||||
|
// Si c'est numérique c'est une catégorie (AND)
|
||||||
|
if (!isNaN(parseInt(idsToShow[i]))) {
|
||||||
|
cat = '.' + idsToShow[i];
|
||||||
|
}
|
||||||
|
//sinon c'est un statut (OR)
|
||||||
|
else {
|
||||||
|
status.push('.' + idsToShow[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [cat, status];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChangePie
|
||||||
|
* Met à jour le pie chart à partir de l'option selectionnée
|
||||||
|
*/
|
||||||
|
|
||||||
|
function changePie(elem, currentFilters) {
|
||||||
|
var container = $('div#measures-filters-container');
|
||||||
|
var measures = $('div#measures-container');
|
||||||
|
|
||||||
|
|
||||||
|
// On récupère les filtres triés
|
||||||
|
var filters = filtersSeparation(currentFilters);
|
||||||
|
|
||||||
|
|
||||||
|
// C'est parti pour les calculs !
|
||||||
|
|
||||||
|
// Pour chaque statut, on calcule en fonction des filtres
|
||||||
|
var statusNames = ["validated", 'partially-validated', "discussed", "danger", "rejected", "undiscussed"];
|
||||||
|
//total des mesures
|
||||||
|
var counts = {'total': measures.children('.measure-wrapper' + filters[0]).length};
|
||||||
|
|
||||||
|
var offset = 0;
|
||||||
|
for (var i = 0; i < statusNames.length; i++) {
|
||||||
|
// on compte
|
||||||
|
counts[statusNames[i]] = measures.children('.measure-wrapper' + filters[0] + '.' + statusNames[i]).length;
|
||||||
|
|
||||||
|
var percent = counts[statusNames[i]] * 100 / counts['total'];
|
||||||
|
var huge = percent > 50 ? 1 : 0;
|
||||||
|
|
||||||
|
// on change les custom val
|
||||||
|
|
||||||
|
container.find('.pie .pie__segment.' + statusNames[i]).css('--value', percent);
|
||||||
|
container.find('.pie .pie__segment.' + statusNames[i]).css('--offset', offset);
|
||||||
|
container.find('.pie .pie__segment.' + statusNames[i]).css('--over50', huge);
|
||||||
|
container.find('.statusTotal.' + statusNames[i]).text(counts[statusNames[i]]);
|
||||||
|
|
||||||
|
//mise à jour de l'offset
|
||||||
|
offset = offset + percent;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recherche de type texte
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var css_selector = ".measure-wrapper, .measure-wrapper";
|
||||||
|
|
||||||
|
/* Par recherche texte */
|
||||||
|
function clearSearchField() {
|
||||||
|
$(".search-container #measure-search").val("");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".search-container #measure-search").keyup(function() {
|
||||||
|
|
||||||
|
// récupération du query actuel
|
||||||
|
var query = $.trim($(this).val());
|
||||||
|
|
||||||
|
if (query === "") { // si il est vide on affiche tout
|
||||||
|
// on affiche toutes les propositions, en tenant compte des filtres éventuels
|
||||||
|
$(css_selector).show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// on va chercher les mentions de ce query dans les mesures et on cache celles qui ne l'ont pas
|
||||||
|
$(css_selector).show().not(':Contains(' + query + ')').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".search-container #measure-search-reset").click(function () {
|
||||||
|
clearSearchField();
|
||||||
|
$(".search-container #measure-search").trigger("keyup");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
52
cent-filtres.php
Normal file
52
cent-filtres.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Plugin Name: Cent Filtres
|
||||||
|
* Description: Plugin permettant l'affichage des mesures de la CCC selon des filtres
|
||||||
|
* Plugin URI: https://sansfiltre.les150.fr
|
||||||
|
* Version : 0.1
|
||||||
|
* Requires at least: 5.6
|
||||||
|
* Requires PHP: 7.4
|
||||||
|
* Author: Matt Marcha
|
||||||
|
* Author URI: https://matt.marcha.pro
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options
|
||||||
|
* Si les mettre éditable en admin n'est pas nécessaire pour l'instant
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Choisir le hook pour temporaliser l'initialisation de l'autoloader (si utres plugins pré-requis par exemple)
|
||||||
|
add_action('init', 'spclInitAutoLoader');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Autoloader
|
||||||
|
*/
|
||||||
|
function spclInitAutoLoader() {
|
||||||
|
// La liste des dossier à parcourir
|
||||||
|
// Par défaut les plus courants
|
||||||
|
$spcls_folders = [
|
||||||
|
//"Models",
|
||||||
|
//"Controllers",
|
||||||
|
"Views"
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($spcls_folders as $folder) {
|
||||||
|
// dans chaque, on inclut chaque fichier php
|
||||||
|
foreach (glob(plugin_dir_path( __FILE__ ). "$folder/*.php") as $filename) {
|
||||||
|
include_once "$filename";
|
||||||
|
|
||||||
|
/* instanciation des classes statiques de hook et d'outils */
|
||||||
|
// on précise la liste des dossiers contenant les classes à instancier directement
|
||||||
|
if (in_array($folder, [
|
||||||
|
//"Models",
|
||||||
|
//"Controllers",
|
||||||
|
"Views"
|
||||||
|
])) {
|
||||||
|
// On extrait la classe: il faut récupérer ce qu'il y a après le dernier slash et virer l'extension
|
||||||
|
$class = explode(".", array_slice(explode("/", $filename), -1, 1)[0])[0];
|
||||||
|
//et on instancie
|
||||||
|
new $class();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue