elements with hx-swap-oob attributes
* for the filter columns, and the students
as the main swap target.
*
* Expected variables:
* $repData array output of Database::getRepertoireFilterData()
* $activeFilters array{years:int[], ap:string[], or:string[], fi:string[], kw:string[]}
* $isOob bool (optional) render OOB-only response for HTMX swaps
*/
$isOob = $isOob ?? false;
$activeSets = [
'years' => array_map('strval', $activeFilters['years'] ?? []),
'ap' => $activeFilters['ap'] ?? [],
'or' => $activeFilters['or'] ?? [],
'fi' => $activeFilters['fi'] ?? [],
'kw' => $activeFilters['kw'] ?? [],
];
// ── Students ────────────────────────────────────────────────────────────────
$studentWorks = [];
foreach ($repData['students'] as $s) {
if (empty($s['authors'])) continue;
foreach (explode(',', $s['authors']) as $name) {
$name = trim($name);
if ($name === '') continue;
$studentWorks[$name][] = (int)$s['id'];
}
}
ksort($studentWorks);
// ── Shared helpers ──────────────────────────────────────────────────────────
// AP abbreviation mapping (cf. maquette: diminutifs entre crochets)
const AP_ABBREVIATIONS = [
'Atelier Pratiques Situées' => '[APS]',
'Design et Politique du Multiple' => '[DPM]',
'Lieux, Interdisciplinarités, Écologie, Nécessité, Systèmes' => '[L.I.E.N.S.]',
];
function formatApDisplay(string $name): string {
$abbr = AP_ABBREVIATIONS[$name] ?? '';
return $abbr !== '' ? "$name $abbr" : $name;
}
function repToggleUrl(array $sets, string $dim, string $value): string {
if (in_array($value, $sets[$dim], true)) {
$sets[$dim] = array_values(array_filter($sets[$dim], fn($v) => $v !== $value));
} else {
$sets[$dim][] = $value;
}
$params = [];
foreach ($sets['years'] as $v) $params[] = 'fy[]=' . urlencode((string)$v);
foreach ($sets['ap'] as $v) $params[] = 'ap[]=' . urlencode($v);
foreach ($sets['or'] as $v) $params[] = 'or[]=' . urlencode($v);
foreach ($sets['fi'] as $v) $params[] = 'fi[]=' . urlencode($v);
foreach ($sets['kw'] as $v) $params[] = 'kw[]=' . urlencode($v);
$qs = implode('&', $params);
return '/repertoire' . ($qs ? '?' . $qs : '');
}
/**
* Render a single filter entry - .
*
* Fading logic: when any filter is active, entries that would yield zero
* results (matched=false) are faded and disabled. The selected entry itself
* is never faded.
*/
function repFilterEntry(
array $item,
string $dim,
array $activeSets,
bool $anyActive,
bool $noResults,
string $hx,
): void {
$val = (string)$item['value'];
$isActive = in_array($val, $activeSets[$dim], true);
$isFaded = $anyActive && ($noResults || !$item['matched']) && !$isActive;
$cls = 'rep-entry'
. ($isActive ? ' rep-entry--selected' : '')
. ($isFaded ? ' rep-entry--faded' : '');
$url = repToggleUrl($activeSets, $dim, $val);
?>
-
(no section chrome).
*/
function renderStudentsList(array $studentWorks): void {
?>
'years', 'dim' => 'years', 'heading' => 'Années'],
['dataKey' => 'ap_programs', 'dim' => 'ap', 'heading' => 'Ateliers Pluridisciplinaires'],
['dataKey' => 'orientations', 'dim' => 'or', 'heading' => 'Orientations'],
['dataKey' => 'finality_types', 'dim' => 'fi', 'heading' => 'Finalité du Master'],
['dataKey' => 'keywords', 'dim' => 'kw', 'heading' => 'Mots-clés'],
];
// Render order: students column is inserted between fi and kw
$renderOrder = ['years', 'ap', 'or', 'fi', 'students', 'kw'];
// ── OOB response: only