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