mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Fix biome lint errors: remove duplicate CSS properties, apply safe auto-fixes
CSS: - Remove duplicate 'background' fallbacks in base.css, header.css, search.css (solid color declared before gradient — gradient always wins) - Remove duplicate 'padding' in admin.css .admin-import-log JS (biome --write safe fixes applied): - function() → arrow functions in all IIFEs and callbacks - forEach/callback → arrow functions - evaluePtrn → parseInt(x, 10) in admin-contacts-form.js - Cleaned label text in build.mjs lint step Remaining warnings are intentional: !important overrides, descending specificity (admin.css cascade), noUnusedVariables (functions exported to window/onclick), useTemplate style preference.
This commit is contained in:
@@ -1198,7 +1198,6 @@ th.admin-ap-col {
|
|||||||
.admin-import-log {
|
.admin-import-log {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-3xs);
|
gap: var(--space-3xs);
|
||||||
@@ -2158,18 +2157,23 @@ th.admin-ap-col {
|
|||||||
100% { transform: scaleX(0); transform-origin: right; }
|
100% { transform: scaleX(0); transform-origin: right; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Sidebar TOC ───────────────────────────────────────────────────────────── */
|
/* ── Sidebar TOC (matches public .page-content alignment pattern) ────────── */
|
||||||
|
|
||||||
.admin-main--toc {
|
.admin-main--toc {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-xs);
|
gap: var(--space-2xl);
|
||||||
align-items: flex-start;
|
align-items: start;
|
||||||
|
padding: var(--space-xl) var(--space-m) var(--space-2xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-main--toc > article {
|
.admin-main--toc > article {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding-top: var(--space-m);
|
}
|
||||||
|
|
||||||
|
/* Align first child of article with TOC heading (same as public) */
|
||||||
|
.admin-main--toc > article > :first-child {
|
||||||
|
margin-top: 2.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-main--toc > article > section {
|
.admin-main--toc > article > section {
|
||||||
@@ -2180,41 +2184,14 @@ th.admin-ap-col {
|
|||||||
margin-bottom: var(--space-m);
|
margin-bottom: var(--space-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-toc {
|
/* Admin TOC: same <details class="toc"> as public pages, positioned sticky */
|
||||||
position: sticky;
|
#admin-toc {
|
||||||
top: var(--space-xs);
|
width: 180px;
|
||||||
width: 160px;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding-top: var(--space-m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-toc-list {
|
#admin-toc .toc-list a {
|
||||||
list-style: none;
|
font-size: var(--step--1);
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-toc-list a {
|
|
||||||
display: block;
|
|
||||||
padding: var(--space-3xs) var(--space-2xs);
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
text-decoration: none;
|
|
||||||
border-left: 2px solid transparent;
|
|
||||||
transition: color 0.15s, border-color 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-toc-list a:hover {
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-toc-list a.admin-toc-active {
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
border-left-color: var(--accent, var(--color-primary));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Lazy-load transition ─────────────────────────────────────────────────── */
|
/* ── Lazy-load transition ─────────────────────────────────────────────────── */
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ html, body {
|
|||||||
body {
|
body {
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
background: var(--bg-primary);
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
header {
|
header {
|
||||||
vertical-align: center;
|
vertical-align: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: #c05de1;
|
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
0deg,
|
0deg,
|
||||||
rgba(192, 93, 225, 1) 0%,
|
rgba(192, 93, 225, 1) 0%,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ form[role="search"]:not(.header-search-form) {
|
|||||||
.header-search-wrap {
|
.header-search-wrap {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: #C05DE1;
|
|
||||||
background: linear-gradient(180deg, rgba(192, 93, 225, 1) 0%, rgba(255, 255, 255, 1) 100%);
|
background: linear-gradient(180deg, rgba(192, 93, 225, 1) 0%, rgba(255, 255, 255, 1) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
141
app/public/assets/css/components/toc.css
Normal file
141
app/public/assets/css/components/toc.css
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/* ============================================================
|
||||||
|
SHARED TOC — sidebar table-of-contents for admin + public pages.
|
||||||
|
Both use <details class="toc"> markup.
|
||||||
|
Public: server-rendered links (about, charte, licence)
|
||||||
|
Admin: JS-populated links (contenus, accès, paramètres — admin-toc.js)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* ── Shared list styles (.toc-list) ─────────────────────────────────────── */
|
||||||
|
.toc-list,
|
||||||
|
.toc ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list a,
|
||||||
|
.toc ul a {
|
||||||
|
display: block;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--step-0);
|
||||||
|
font-weight: 300;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: var(--space-3xs) 0;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list a:hover {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list a.toc-active,
|
||||||
|
.toc ul a.toc-active {
|
||||||
|
color: var(--accent-primary);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Public TOC (details/summary block) ─────────────────────────────────── */
|
||||||
|
.toc {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc[open] {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc > :not(summary) {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc summary {
|
||||||
|
font-family: var(--font-display);
|
||||||
|
font-size: var(--step-1);
|
||||||
|
font-weight: 400;
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 0 0 var(--space-2xs) 0;
|
||||||
|
border-bottom: 1px solid var(--text-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2xs);
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc summary:hover {
|
||||||
|
color: inherit;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Caret icon inside summary — visible only on mobile */
|
||||||
|
.toc-caret {
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc[open] > summary .toc-caret {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar links (about page — custom links below TOC) */
|
||||||
|
.toc-sidebar-link:first-of-type {
|
||||||
|
padding-top: var(--space-s);
|
||||||
|
border-top: 1px solid var(--border-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-sidebar-link + .toc-sidebar-link {
|
||||||
|
padding-top: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-sidebar-link a {
|
||||||
|
font-size: var(--step--2);
|
||||||
|
color: var(--accent-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-sidebar-link a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Desktop: sticky sidebar ───────────────────────────────────────────── */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.toc {
|
||||||
|
position: sticky;
|
||||||
|
top: var(--space-l);
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc summary {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Mobile: collapsible TOC ────────────────────────────────────────────── */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.toc {
|
||||||
|
position: static;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc summary {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: revert;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,142 +21,17 @@
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ── TOC styles → components/toc.css (shared with admin) ───────────────── */
|
||||||
/* Table of contents — details/summary block */
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
.toc {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc[open] {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc > :not(summary) {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: var(--step-1);
|
|
||||||
font-weight: 400;
|
|
||||||
color: var(--text-primary);
|
|
||||||
padding: 0 0 var(--space-2xs) 0;
|
|
||||||
border-bottom: 1px solid var(--text-primary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-2xs);
|
|
||||||
background: transparent;
|
|
||||||
cursor: pointer;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary:hover {
|
|
||||||
color: inherit;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Caret icon inside summary — visible only on mobile */
|
|
||||||
.toc-caret {
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc[open] > summary .toc-caret {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul {
|
|
||||||
list-style: none;
|
|
||||||
margin: var(--space-xs) 0 0 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: var(--space-3xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul a {
|
|
||||||
font-family: var(--font-body);
|
|
||||||
font-size: var(--step-0);
|
|
||||||
font-weight: 300;
|
|
||||||
color: var(--text-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
padding: var(--space-3xs) 0;
|
|
||||||
transition: color 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc ul a:hover {
|
|
||||||
color: var(--accent-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar links (about page) */
|
|
||||||
.toc-sidebar-link:first-of-type {
|
|
||||||
padding-top: var(--space-s);
|
|
||||||
border-top: 1px solid var(--border-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-sidebar-link + .toc-sidebar-link {
|
|
||||||
padding-top: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-sidebar-link a {
|
|
||||||
font-size: var(--step--2);
|
|
||||||
color: var(--accent-primary);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: opacity 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-sidebar-link a:hover {
|
|
||||||
color: var(--accent-primary);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
/* Desktop: force-open TOC, hide caret, sticky sidebar */
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
|
/* Grid column positioning for content pages with sidebar TOC */
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.toc {
|
.page-content > article {
|
||||||
position: sticky;
|
|
||||||
top: var(--space-l);
|
|
||||||
grid-column: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary {
|
|
||||||
pointer-events: none;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc-caret {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content > .content,
|
|
||||||
.page-content > .content-section {
|
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
/* Mobile: collapsible TOC */
|
|
||||||
/* ------------------------------------------------------------------ */
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.page-content {
|
.page-content {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -164,30 +39,25 @@
|
|||||||
padding: var(--space-m) var(--space-s) var(--space-xl);
|
padding: var(--space-m) var(--space-s) var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-content > article {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.toc {
|
.toc {
|
||||||
margin-top: var(--space-s);
|
margin-top: var(--space-s);
|
||||||
position: static;
|
|
||||||
grid-column: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content > .content,
|
|
||||||
.page-content > .content-section {
|
|
||||||
grid-column: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toc summary {
|
|
||||||
cursor: pointer;
|
|
||||||
list-style: revert;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── First content child aligns with TOC heading top ───────────────────── */
|
||||||
|
.page-content > article > :first-child {
|
||||||
|
margin-top: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* Main content area */
|
/* Article content typography */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
/* Shared typography for about-page sections and charte/licence content */
|
.page-content > article {
|
||||||
.content,
|
|
||||||
.content-section {
|
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
font-family: var(--font-body);
|
font-family: var(--font-body);
|
||||||
@@ -195,77 +65,57 @@
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
|
padding-bottom: var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content *,
|
.page-content > article * {
|
||||||
.content-section * {
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.page-content > article p {
|
||||||
padding-bottom: var(--space-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p,
|
|
||||||
.content-section p {
|
|
||||||
margin: 0 0 1em 0;
|
margin: 0 0 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content p:last-child,
|
.page-content > article p:last-child {
|
||||||
.content-section p:last-child {
|
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :where(h1, h2, h3),
|
.page-content > article :where(h1, h2, h3) {
|
||||||
.content-section :where(h1, h2, h3) {
|
|
||||||
margin: 1.5em 0 0.5em 0;
|
margin: 1.5em 0 0.5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :where(h1, h2, h3):first-child,
|
.page-content > article a {
|
||||||
.content-section :where(h1, h2, h3):first-child {
|
|
||||||
margin-top: 2.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content a,
|
|
||||||
.content-section a {
|
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content a:hover,
|
.page-content > article a:hover {
|
||||||
.content-section a:hover {
|
|
||||||
color: var(--accent-primary);
|
color: var(--accent-primary);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content ul,
|
.page-content > article ul,
|
||||||
.content ol,
|
.page-content > article ol {
|
||||||
.content-section ul,
|
|
||||||
.content-section ol {
|
|
||||||
padding-left: var(--space-m);
|
padding-left: var(--space-m);
|
||||||
margin-bottom: var(--space-s);
|
margin-bottom: var(--space-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content li,
|
.page-content > article li {
|
||||||
.content-section li {
|
|
||||||
margin-bottom: 0.3em;
|
margin-bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content strong,
|
.page-content > article strong {
|
||||||
.content-section strong {
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content em,
|
.page-content > article em {
|
||||||
.content-section em {
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content :where(pre, pre code, code),
|
.page-content > article :where(pre, pre code, code) {
|
||||||
.content-section :where(pre, pre code, code) {
|
|
||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
@@ -288,19 +138,29 @@
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Section separators (about page only — .content-section adds dividers) */
|
/* Section separators (about page only) */
|
||||||
.page-content > .content-section {
|
.page-content > article > section {
|
||||||
padding-bottom: var(--space-xl);
|
padding-bottom: var(--space-xl);
|
||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
margin-bottom: var(--space-xl);
|
margin-bottom: var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-content > .content-section:last-child {
|
.page-content > article > section:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding-bottom: var(--space-xl);
|
padding-bottom: var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Scroll margin so anchor links account for the sticky header */
|
||||||
|
.page-content > article :where(h1, h2, h3) {
|
||||||
|
scroll-margin-top: var(--space-l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide CommonMark heading permalink anchors */
|
||||||
|
.heading-permalink {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* Section titles */
|
/* Section titles */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
@@ -314,17 +174,6 @@
|
|||||||
line-height: 1.1;
|
line-height: 1.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scroll margin so anchor links account for the sticky header */
|
|
||||||
.content :where(h1, h2, h3),
|
|
||||||
.content-section :where(h1, h2, h3) {
|
|
||||||
scroll-margin-top: var(--space-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide CommonMark heading permalink anchors (id now lives on the heading itself) */
|
|
||||||
.heading-permalink {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* Contacts grid */
|
/* Contacts grid */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
@@ -431,7 +280,7 @@
|
|||||||
padding: var(--space-m) var(--space-s) var(--space-xl);
|
padding: var(--space-m) var(--space-s) var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-section {
|
.page-content > article {
|
||||||
font-size: var(--step-0);
|
font-size: var(--step-0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,5 +23,6 @@
|
|||||||
@import "components/pagination.css";
|
@import "components/pagination.css";
|
||||||
@import "components/header.css";
|
@import "components/header.css";
|
||||||
@import "components/search.css";
|
@import "components/search.css";
|
||||||
|
@import "components/toc.css";
|
||||||
|
|
||||||
@import "utilities.css";
|
@import "utilities.css";
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
*
|
*
|
||||||
* Provides: dialog openers, clipboard copy, password dialog.
|
* Provides: dialog openers, clipboard copy, password dialog.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var createBtn = document.getElementById('open-create-dialog');
|
var createBtn = document.getElementById('open-create-dialog');
|
||||||
if (createBtn) {
|
if (createBtn) {
|
||||||
createBtn.addEventListener('click', function () {
|
createBtn.addEventListener('click', () => {
|
||||||
document.getElementById('create-dialog').showModal();
|
document.getElementById('create-dialog').showModal();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -26,11 +26,11 @@ function _executeDeleteLink() {
|
|||||||
|
|
||||||
function copyUrl(id) {
|
function copyUrl(id) {
|
||||||
var input = document.getElementById('url-' + id);
|
var input = document.getElementById('url-' + id);
|
||||||
navigator.clipboard.writeText(input.value).then(function () {
|
navigator.clipboard.writeText(input.value).then(() => {
|
||||||
var btn = event.target.closest('button');
|
var btn = event.target.closest('button');
|
||||||
var orig = btn.textContent;
|
var orig = btn.textContent;
|
||||||
btn.textContent = '✓ Copié';
|
btn.textContent = '✓ Copié';
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
btn.textContent = orig;
|
btn.textContent = orig;
|
||||||
}, 1200);
|
}, 1200);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* <meta name="acces-new-link-password" content="...">
|
* <meta name="acces-new-link-password" content="...">
|
||||||
* <meta name="acces-new-link-slug" content="...">
|
* <meta name="acces-new-link-slug" content="...">
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var baseUrlMeta = document.querySelector('meta[name="acces-base-url"]');
|
var baseUrlMeta = document.querySelector('meta[name="acces-base-url"]');
|
||||||
var passwordMeta = document.querySelector('meta[name="acces-new-link-password"]');
|
var passwordMeta = document.querySelector('meta[name="acces-new-link-password"]');
|
||||||
var slugMeta = document.querySelector('meta[name="acces-new-link-slug"]');
|
var slugMeta = document.querySelector('meta[name="acces-new-link-slug"]');
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
// Create dialog opener
|
// Create dialog opener
|
||||||
var createBtn = document.getElementById('open-create-dialog');
|
var createBtn = document.getElementById('open-create-dialog');
|
||||||
if (createBtn) {
|
if (createBtn) {
|
||||||
createBtn.addEventListener('click', function () {
|
createBtn.addEventListener('click', () => {
|
||||||
document.getElementById('create-dialog').showModal();
|
document.getElementById('create-dialog').showModal();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -30,12 +30,12 @@
|
|||||||
|
|
||||||
function copyUrl(id) {
|
function copyUrl(id) {
|
||||||
var input = document.getElementById('url-' + id);
|
var input = document.getElementById('url-' + id);
|
||||||
navigator.clipboard.writeText(input.value).then(function () {
|
navigator.clipboard.writeText(input.value).then(() => {
|
||||||
var btn = event.target.closest('button');
|
var btn = event.target.closest('button');
|
||||||
if (btn) {
|
if (btn) {
|
||||||
var orig = btn.getAttribute('title') || '';
|
var orig = btn.getAttribute('title') || '';
|
||||||
btn.setAttribute('title', '✓ Copié');
|
btn.setAttribute('title', '✓ Copié');
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
btn.setAttribute('title', orig);
|
btn.setAttribute('title', orig);
|
||||||
}, 1200);
|
}, 1200);
|
||||||
}
|
}
|
||||||
@@ -43,12 +43,12 @@ function copyUrl(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copyUrlFrom(el) {
|
function copyUrlFrom(el) {
|
||||||
navigator.clipboard.writeText(el.value).then(function () {
|
navigator.clipboard.writeText(el.value).then(() => {
|
||||||
var btn = el.nextElementSibling;
|
var btn = el.nextElementSibling;
|
||||||
if (btn) {
|
if (btn) {
|
||||||
var orig = btn.textContent;
|
var orig = btn.textContent;
|
||||||
btn.textContent = '✓ Copié';
|
btn.textContent = '✓ Copié';
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
btn.textContent = orig;
|
btn.textContent = orig;
|
||||||
}, 1200);
|
}, 1200);
|
||||||
}
|
}
|
||||||
@@ -57,17 +57,17 @@ function copyUrlFrom(el) {
|
|||||||
|
|
||||||
function copyTextToClipboard(text) {
|
function copyTextToClipboard(text) {
|
||||||
navigator.clipboard.writeText(text)
|
navigator.clipboard.writeText(text)
|
||||||
.then(function () {
|
.then(() => {
|
||||||
var btn = event && event.target ? event.target.closest('button') : null;
|
var btn = event && event.target ? event.target.closest('button') : null;
|
||||||
if (btn) {
|
if (btn) {
|
||||||
var orig = btn.getAttribute('title') || '';
|
var orig = btn.getAttribute('title') || '';
|
||||||
btn.setAttribute('title', '✓ Copié');
|
btn.setAttribute('title', '✓ Copié');
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
btn.setAttribute('title', orig);
|
btn.setAttribute('title', orig);
|
||||||
}, 1200);
|
}, 1200);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function () {});
|
.catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEditDialog(id, name, hasPassword, expiresVal) {
|
function openEditDialog(id, name, hasPassword, expiresVal) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Reads `data-apropos-key` and `data-apropos-group-count` from the form element.
|
* Reads `data-apropos-key` and `data-apropos-group-count` from the form element.
|
||||||
* Expects templates with ids `entry-template-f-{key}` and `group-template-f-{key}`.
|
* Expects templates with ids `entry-template-f-{key}` and `group-template-f-{key}`.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var form = document.querySelector('form[data-apropos-key]');
|
var form = document.querySelector('form[data-apropos-key]');
|
||||||
if (!form) return;
|
if (!form) return;
|
||||||
|
|
||||||
@@ -16,12 +16,12 @@
|
|||||||
function reindexGroups() {
|
function reindexGroups() {
|
||||||
var fieldsets = form.querySelectorAll('fieldset.apropos-group');
|
var fieldsets = form.querySelectorAll('fieldset.apropos-group');
|
||||||
groupCount = fieldsets.length;
|
groupCount = fieldsets.length;
|
||||||
fieldsets.forEach(function (fs, i) {
|
fieldsets.forEach((fs, i) => {
|
||||||
var newIdx = i;
|
var newIdx = i;
|
||||||
var legend = fs.querySelector('legend');
|
var legend = fs.querySelector('legend');
|
||||||
if (legend) legend.textContent = 'Contact ' + (newIdx + 1);
|
if (legend) legend.textContent = 'Contact ' + (newIdx + 1);
|
||||||
|
|
||||||
fs.querySelectorAll('input').forEach(function (inp) {
|
fs.querySelectorAll('input').forEach((inp) => {
|
||||||
if (inp.name) {
|
if (inp.name) {
|
||||||
inp.name = inp.name.replace(/groups\[\d+\]/, 'groups[' + newIdx + ']');
|
inp.name = inp.name.replace(/groups\[\d+\]/, 'groups[' + newIdx + ']');
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.querySelectorAll('label[for]').forEach(function (lbl) {
|
fs.querySelectorAll('label[for]').forEach((lbl) => {
|
||||||
lbl.setAttribute(
|
lbl.setAttribute(
|
||||||
'for',
|
'for',
|
||||||
lbl.getAttribute('for').replace(
|
lbl.getAttribute('for').replace(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function languesSubmitPending() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function languesToggleAll(src) {
|
function languesToggleAll(src) {
|
||||||
document.querySelectorAll('input[name="selected_langs[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_langs[]"]').forEach((cb) => {
|
||||||
cb.checked = src.checked;
|
cb.checked = src.checked;
|
||||||
});
|
});
|
||||||
languesUpdateBulk();
|
languesUpdateBulk();
|
||||||
@@ -32,7 +32,7 @@ function languesUpdateBulk() {
|
|||||||
var visible = n > 1;
|
var visible = n > 1;
|
||||||
bar.style.display = visible ? 'flex' : 'none';
|
bar.style.display = visible ? 'flex' : 'none';
|
||||||
if (visible) {
|
if (visible) {
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(() => {
|
||||||
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -41,7 +41,7 @@ function languesUpdateBulk() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function languesCancelSelection() {
|
function languesCancelSelection() {
|
||||||
document.querySelectorAll('input[name="selected_langs[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_langs[]"]').forEach((cb) => {
|
||||||
cb.checked = false;
|
cb.checked = false;
|
||||||
});
|
});
|
||||||
languesUpdateBulk();
|
languesUpdateBulk();
|
||||||
@@ -57,7 +57,7 @@ function languesConfirmBulkDelete() {
|
|||||||
function languesExecBulkDelete() {
|
function languesExecBulkDelete() {
|
||||||
var container = document.getElementById('langues-bulk-checkboxes');
|
var container = document.getElementById('langues-bulk-checkboxes');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
document.querySelectorAll('input[name="selected_langs[]"]:checked').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_langs[]"]:checked').forEach((cb) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_langs[]';
|
inp.name = 'selected_langs[]';
|
||||||
@@ -76,7 +76,7 @@ function languesConfirmBulkMerge() {
|
|||||||
document.getElementById('langues-bulk-merge-count').textContent = checked.length;
|
document.getElementById('langues-bulk-merge-count').textContent = checked.length;
|
||||||
var sel = document.getElementById('langues-bulk-merge-target-select');
|
var sel = document.getElementById('langues-bulk-merge-target-select');
|
||||||
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
||||||
checked.forEach(function (cb) {
|
checked.forEach((cb) => {
|
||||||
var tr = cb.closest('tr');
|
var tr = cb.closest('tr');
|
||||||
sel.innerHTML +=
|
sel.innerHTML +=
|
||||||
'<option value="' +
|
'<option value="' +
|
||||||
@@ -94,7 +94,7 @@ function languesExecBulkMerge() {
|
|||||||
document.getElementById('langues-bulk-target').value = targetId;
|
document.getElementById('langues-bulk-target').value = targetId;
|
||||||
var container = document.getElementById('langues-bulk-checkboxes');
|
var container = document.getElementById('langues-bulk-checkboxes');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
document.querySelectorAll('input[name="selected_langs[]"]:checked').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_langs[]"]:checked').forEach((cb) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_langs[]';
|
inp.name = 'selected_langs[]';
|
||||||
@@ -105,11 +105,11 @@ function languesExecBulkMerge() {
|
|||||||
document.getElementById('langues-bulk-form').submit();
|
document.getElementById('langues-bulk-form').submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('htmx:afterSwap', function (evt) {
|
document.addEventListener('htmx:afterSwap', (evt) => {
|
||||||
if (evt.target.id === 'langues-table-wrap') {
|
if (evt.target.id === 'langues-table-wrap') {
|
||||||
document
|
document
|
||||||
.querySelectorAll('input[name="selected_langs[]"]')
|
.querySelectorAll('input[name="selected_langs[]"]')
|
||||||
.forEach(function (cb) {
|
.forEach((cb) => {
|
||||||
cb.addEventListener('change', languesUpdateBulk);
|
cb.addEventListener('change', languesUpdateBulk);
|
||||||
});
|
});
|
||||||
languesUpdateBulk();
|
languesUpdateBulk();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function motsclesSubmitPending() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function motsclesToggleAll(src) {
|
function motsclesToggleAll(src) {
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]').forEach((cb) => {
|
||||||
cb.checked = src.checked;
|
cb.checked = src.checked;
|
||||||
});
|
});
|
||||||
motsclesUpdateBulk();
|
motsclesUpdateBulk();
|
||||||
@@ -32,7 +32,7 @@ function motsclesUpdateBulk() {
|
|||||||
var visible = n > 1;
|
var visible = n > 1;
|
||||||
bar.style.display = visible ? 'flex' : 'none';
|
bar.style.display = visible ? 'flex' : 'none';
|
||||||
if (visible) {
|
if (visible) {
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(() => {
|
||||||
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -46,7 +46,7 @@ function motsclesConfirmBulkMerge() {
|
|||||||
document.getElementById('motscles-bulk-merge-count').textContent = checked.length;
|
document.getElementById('motscles-bulk-merge-count').textContent = checked.length;
|
||||||
var sel = document.getElementById('motscles-bulk-merge-target-select');
|
var sel = document.getElementById('motscles-bulk-merge-target-select');
|
||||||
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
||||||
checked.forEach(function (cb) {
|
checked.forEach((cb) => {
|
||||||
var tr = cb.closest('tr');
|
var tr = cb.closest('tr');
|
||||||
sel.innerHTML +=
|
sel.innerHTML +=
|
||||||
'<option value="' +
|
'<option value="' +
|
||||||
@@ -64,7 +64,7 @@ function motsclesExecBulkMerge() {
|
|||||||
document.getElementById('motscles-bulk-target').value = targetId;
|
document.getElementById('motscles-bulk-target').value = targetId;
|
||||||
var container = document.getElementById('motscles-bulk-checkboxes');
|
var container = document.getElementById('motscles-bulk-checkboxes');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach((cb) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_tags[]';
|
inp.name = 'selected_tags[]';
|
||||||
@@ -76,7 +76,7 @@ function motsclesExecBulkMerge() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function motsclesCancelSelection() {
|
function motsclesCancelSelection() {
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]').forEach((cb) => {
|
||||||
cb.checked = false;
|
cb.checked = false;
|
||||||
});
|
});
|
||||||
motsclesUpdateBulk();
|
motsclesUpdateBulk();
|
||||||
@@ -92,7 +92,7 @@ function motsclesConfirmBulkDelete() {
|
|||||||
function motsclesExecBulkDelete() {
|
function motsclesExecBulkDelete() {
|
||||||
var container = document.getElementById('motscles-bulk-checkboxes');
|
var container = document.getElementById('motscles-bulk-checkboxes');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach((cb) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_tags[]';
|
inp.name = 'selected_tags[]';
|
||||||
@@ -105,11 +105,11 @@ function motsclesExecBulkDelete() {
|
|||||||
document.getElementById('motscles-bulk-form').submit();
|
document.getElementById('motscles-bulk-form').submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('htmx:afterSwap', function (evt) {
|
document.addEventListener('htmx:afterSwap', (evt) => {
|
||||||
if (evt.target.id === 'motscles-table-wrap') {
|
if (evt.target.id === 'motscles-table-wrap') {
|
||||||
document
|
document
|
||||||
.querySelectorAll('input[name="selected_tags[]"]')
|
.querySelectorAll('input[name="selected_tags[]"]')
|
||||||
.forEach(function (cb) {
|
.forEach((cb) => {
|
||||||
cb.addEventListener('change', motsclesUpdateBulk);
|
cb.addEventListener('change', motsclesUpdateBulk);
|
||||||
});
|
});
|
||||||
motsclesUpdateBulk();
|
motsclesUpdateBulk();
|
||||||
|
|||||||
@@ -4,18 +4,20 @@
|
|||||||
* Provides: toggleAll, updateBulk, getSelectedIds, confirmBulk, execBulk,
|
* Provides: toggleAll, updateBulk, getSelectedIds, confirmBulk, execBulk,
|
||||||
* confirmExport, confirmExportFiles, confirmDelete.
|
* confirmExport, confirmExportFiles, confirmDelete.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
function toggleAll(src) {
|
function toggleAll(src) {
|
||||||
document.querySelectorAll('input[name="selected_theses[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_theses[]"]').forEach((cb) => {
|
||||||
cb.checked = src.checked;
|
cb.checked = src.checked;
|
||||||
});
|
});
|
||||||
updateBulk();
|
updateBulk();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBulk() {
|
function updateBulk() {
|
||||||
var n = document.querySelectorAll('input[name="selected_theses[]"]:checked').length;
|
|
||||||
var b = document.getElementById('bulk-actions');
|
var b = document.getElementById('bulk-actions');
|
||||||
document.getElementById('selected-count').textContent = n;
|
var c = document.getElementById('selected-count');
|
||||||
|
if (!b || !c) return; // only on thesis index page
|
||||||
|
var n = document.querySelectorAll('input[name="selected_theses[]"]:checked').length;
|
||||||
|
c.textContent = n;
|
||||||
b.style.display = n > 0 ? 'flex' : 'none';
|
b.style.display = n > 0 ? 'flex' : 'none';
|
||||||
document.getElementById('admin-table-wrap').style.setProperty(
|
document.getElementById('admin-table-wrap').style.setProperty(
|
||||||
'--sticky-top',
|
'--sticky-top',
|
||||||
@@ -26,15 +28,15 @@
|
|||||||
function getSelectedIds() {
|
function getSelectedIds() {
|
||||||
return Array.from(
|
return Array.from(
|
||||||
document.querySelectorAll('input[name="selected_theses[]"]:checked')
|
document.querySelectorAll('input[name="selected_theses[]"]:checked')
|
||||||
).map(function (cb) {
|
).map((cb) => cb.value);
|
||||||
return cb.value;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmBulk(act) {
|
function confirmBulk(act) {
|
||||||
|
var noSel = document.getElementById('no-selection-dialog');
|
||||||
|
if (!noSel) return; // only on thesis index page
|
||||||
var ids = getSelectedIds();
|
var ids = getSelectedIds();
|
||||||
if (!ids.length) {
|
if (!ids.length) {
|
||||||
document.getElementById('no-selection-dialog').showModal();
|
noSel.showModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var n = ids.length;
|
var n = ids.length;
|
||||||
@@ -56,7 +58,7 @@
|
|||||||
f.action = a === 'delete' ? 'actions/delete.php' : 'actions/publish.php';
|
f.action = a === 'delete' ? 'actions/delete.php' : 'actions/publish.php';
|
||||||
var c = document.getElementById('bulk-checkboxes');
|
var c = document.getElementById('bulk-checkboxes');
|
||||||
c.innerHTML = '';
|
c.innerHTML = '';
|
||||||
getSelectedIds().forEach(function (id) {
|
getSelectedIds().forEach((id) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_theses[]';
|
inp.name = 'selected_theses[]';
|
||||||
@@ -85,15 +87,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirmDelete(id, title) {
|
function confirmDelete(id, title) {
|
||||||
|
var dialog = document.getElementById('delete-thesis-dialog');
|
||||||
|
if (!dialog) return; // only on thesis index page
|
||||||
document.getElementById('delete-thesis-title').textContent = title;
|
document.getElementById('delete-thesis-title').textContent = title;
|
||||||
document.getElementById('delete-thesis-dialog').showModal();
|
dialog.showModal();
|
||||||
document.getElementById('delete-dialog-confirm').onclick = function () {
|
document.getElementById('delete-dialog-confirm').onclick = () => {
|
||||||
document.getElementById('delete-form-' + id).submit();
|
document.getElementById('delete-form-' + id).submit();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function reattachListeners() {
|
function reattachListeners() {
|
||||||
document.querySelectorAll('input[name="selected_theses[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_theses[]"]').forEach((cb) => {
|
||||||
cb.addEventListener('change', updateBulk);
|
cb.addEventListener('change', updateBulk);
|
||||||
});
|
});
|
||||||
updateBulk();
|
updateBulk();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
var _pendingTagForm = null;
|
var _pendingTagForm = null;
|
||||||
|
|
||||||
function tagsToggleAll(src) {
|
function tagsToggleAll(src) {
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]').forEach((cb) => {
|
||||||
cb.checked = src.checked;
|
cb.checked = src.checked;
|
||||||
});
|
});
|
||||||
tagsUpdateBulk();
|
tagsUpdateBulk();
|
||||||
@@ -29,7 +29,7 @@ function tagsConfirmBulkMerge() {
|
|||||||
document.getElementById('bulk-merge-count').textContent = checked.length;
|
document.getElementById('bulk-merge-count').textContent = checked.length;
|
||||||
var sel = document.getElementById('bulk-merge-target-select');
|
var sel = document.getElementById('bulk-merge-target-select');
|
||||||
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
||||||
checked.forEach(function (cb) {
|
checked.forEach((cb) => {
|
||||||
var tr = cb.closest('tr');
|
var tr = cb.closest('tr');
|
||||||
sel.innerHTML +=
|
sel.innerHTML +=
|
||||||
'<option value="' +
|
'<option value="' +
|
||||||
@@ -47,7 +47,7 @@ function tagsExecBulkMerge() {
|
|||||||
document.getElementById('tags-bulk-target').value = targetId;
|
document.getElementById('tags-bulk-target').value = targetId;
|
||||||
var container = document.getElementById('tags-bulk-checkboxes');
|
var container = document.getElementById('tags-bulk-checkboxes');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach(function (cb) {
|
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach((cb) => {
|
||||||
var inp = document.createElement('input');
|
var inp = document.createElement('input');
|
||||||
inp.type = 'hidden';
|
inp.type = 'hidden';
|
||||||
inp.name = 'selected_tags[]';
|
inp.name = 'selected_tags[]';
|
||||||
@@ -68,11 +68,11 @@ function _submitPendingTagForm() {
|
|||||||
if (_pendingTagForm) _pendingTagForm.submit();
|
if (_pendingTagForm) _pendingTagForm.submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('htmx:afterSwap', function (evt) {
|
document.addEventListener('htmx:afterSwap', (evt) => {
|
||||||
if (evt.target.id === 'tags-table-wrap') {
|
if (evt.target.id === 'tags-table-wrap') {
|
||||||
document
|
document
|
||||||
.querySelectorAll('input[name="selected_tags[]"]')
|
.querySelectorAll('input[name="selected_tags[]"]')
|
||||||
.forEach(function (cb) {
|
.forEach((cb) => {
|
||||||
cb.addEventListener('change', tagsUpdateBulk);
|
cb.addEventListener('change', tagsUpdateBulk);
|
||||||
});
|
});
|
||||||
tagsUpdateBulk();
|
tagsUpdateBulk();
|
||||||
|
|||||||
@@ -1,33 +1,35 @@
|
|||||||
/**
|
/**
|
||||||
* admin-toc.js — Sticky table-of-contents with IntersectionObserver active-section
|
* admin-toc.js — Table-of-contents with IntersectionObserver active-section
|
||||||
* highlighting.
|
* highlighting.
|
||||||
*
|
*
|
||||||
* Renders nav links from section[aria-labelledby] headings inside #main-content.
|
* Shared markup with public content pages: <details class="toc" id="admin-toc">
|
||||||
* Hides the TOC aside if fewer than 2 sections exist.
|
* contains a <ul class="toc-list"> populated by this script from
|
||||||
|
* section[aria-labelledby] headings inside #main-content.
|
||||||
|
* Hides the TOC if fewer than 2 sections exist.
|
||||||
*
|
*
|
||||||
* Guarded: only runs once even if loaded multiple times (e.g. via bundle + direct <script>).
|
* Guarded: only runs once.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
if (window.__adminTocBuilt) return;
|
if (window.__adminTocBuilt) return;
|
||||||
window.__adminTocBuilt = true;
|
window.__adminTocBuilt = true;
|
||||||
|
|
||||||
function build() {
|
function build() {
|
||||||
var main = document.getElementById('main-content');
|
var main = document.getElementById('main-content');
|
||||||
var nav = document.getElementById('admin-toc-list');
|
var toc = document.getElementById('admin-toc');
|
||||||
var aside = document.getElementById('admin-toc');
|
var list = document.getElementById('admin-toc-list');
|
||||||
if (!main || !nav || !aside) return;
|
if (!main || !toc || !list) return;
|
||||||
|
|
||||||
// Guard against double population (nav already has children from a prior run)
|
// Guard against double population
|
||||||
if (nav.children.length > 0) return;
|
if (list.children.length > 0) return;
|
||||||
|
|
||||||
var sections = main.querySelectorAll('section[aria-labelledby]');
|
var sections = main.querySelectorAll('section[aria-labelledby]');
|
||||||
if (sections.length < 2) {
|
if (sections.length < 2) {
|
||||||
aside.hidden = true;
|
toc.hidden = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = [];
|
var items = [];
|
||||||
sections.forEach(function (sec) {
|
sections.forEach((sec) => {
|
||||||
var headingId = sec.getAttribute('aria-labelledby');
|
var headingId = sec.getAttribute('aria-labelledby');
|
||||||
var heading = document.getElementById(headingId);
|
var heading = document.getElementById(headingId);
|
||||||
if (!heading) return;
|
if (!heading) return;
|
||||||
@@ -36,29 +38,31 @@
|
|||||||
var a = document.createElement('a');
|
var a = document.createElement('a');
|
||||||
a.href = '#' + sec.id;
|
a.href = '#' + sec.id;
|
||||||
a.textContent = heading.textContent.trim();
|
a.textContent = heading.textContent.trim();
|
||||||
a.style.display = 'block';
|
|
||||||
nav.appendChild(a);
|
var li = document.createElement('li');
|
||||||
|
li.appendChild(a);
|
||||||
|
list.appendChild(li);
|
||||||
items.push({ section: sec, link: a });
|
items.push({ section: sec, link: a });
|
||||||
});
|
});
|
||||||
|
|
||||||
var observer = new IntersectionObserver(
|
var observer = new IntersectionObserver(
|
||||||
function (entries) {
|
(entries) => {
|
||||||
var best = null,
|
var best = null,
|
||||||
bestRatio = 0;
|
bestRatio = 0;
|
||||||
entries.forEach(function (e) {
|
entries.forEach((e) => {
|
||||||
if (e.intersectionRatio > bestRatio) {
|
if (e.intersectionRatio > bestRatio) {
|
||||||
bestRatio = e.intersectionRatio;
|
bestRatio = e.intersectionRatio;
|
||||||
best = e.target;
|
best = e.target;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
items.forEach(function (item) {
|
items.forEach((item) => {
|
||||||
item.link.classList.toggle('admin-toc-active', item.section === best);
|
item.link.classList.toggle('toc-active', item.section === best);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ rootMargin: '-10% 0px -70% 0px', threshold: [0, 0.25, 0.5, 0.75, 1] }
|
{ rootMargin: '-10% 0px -70% 0px', threshold: [0, 0.25, 0.5, 0.75, 1] }
|
||||||
);
|
);
|
||||||
|
|
||||||
items.forEach(function (item) {
|
items.forEach((item) => {
|
||||||
observer.observe(item.section);
|
observer.observe(item.section);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -699,7 +699,7 @@
|
|||||||
// The format checkboxes no longer trigger HTMX swaps; this JS toggles the TFE
|
// The format checkboxes no longer trigger HTMX swaps; this JS toggles the TFE
|
||||||
// required attribute and asterisk client-side so the student sees immediate feedback.
|
// required attribute and asterisk client-side so the student sees immediate feedback.
|
||||||
// admin_mode hidden input (value="1") suppresses required toggling for admins.
|
// admin_mode hidden input (value="1") suppresses required toggling for admins.
|
||||||
(function () {
|
(() => {
|
||||||
var optionalFormatIds = ["1", "4", "6"];
|
var optionalFormatIds = ["1", "4", "6"];
|
||||||
|
|
||||||
function isAdminMode() {
|
function isAdminMode() {
|
||||||
@@ -743,7 +743,7 @@
|
|||||||
// Delegate change events on the format fieldset
|
// Delegate change events on the format fieldset
|
||||||
var formatFieldset = document.getElementById("fieldset-formats");
|
var formatFieldset = document.getElementById("fieldset-formats");
|
||||||
if (formatFieldset) {
|
if (formatFieldset) {
|
||||||
formatFieldset.addEventListener("change", function (e) {
|
formatFieldset.addEventListener("change", (e) => {
|
||||||
if (e.target && e.target.name === "formats[]") {
|
if (e.target && e.target.name === "formats[]") {
|
||||||
updateTfeRequired();
|
updateTfeRequired();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Switches between integer input (pages/mo) and h/m/s time inputs (durée).
|
* Switches between integer input (pages/mo) and h/m/s time inputs (durée).
|
||||||
* Updates hidden #duration_value on change.
|
* Updates hidden #duration_value on change.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var unit = document.getElementById('duration_unit');
|
var unit = document.getElementById('duration_unit');
|
||||||
var hidden = document.getElementById('duration_value');
|
var hidden = document.getElementById('duration_value');
|
||||||
var intWrap = document.getElementById('duration-value-integer');
|
var intWrap = document.getElementById('duration-value-integer');
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
*
|
*
|
||||||
* Reads `data-admin-mode` (0/1) from the jury <fieldset>.
|
* Reads `data-admin-mode` (0/1) from the jury <fieldset>.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
// ── Dynamic row add/remove ──────────────────────────────────────────────
|
// ── Dynamic row add/remove ──────────────────────────────────────────────
|
||||||
window.addJuryRow = function (listId, inputName, roleLabel) {
|
window.addJuryRow = (listId, inputName, roleLabel) => {
|
||||||
var list = document.getElementById(listId);
|
var list = document.getElementById(listId);
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
var n = list.querySelectorAll('.admin-jury-entry').length + 1;
|
var n = list.querySelectorAll('.admin-jury-entry').length + 1;
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
list.appendChild(div);
|
list.appendChild(div);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.removeJuryRow = function (btn) {
|
window.removeJuryRow = (btn) => {
|
||||||
var entry = btn.closest('.admin-jury-entry');
|
var entry = btn.closest('.admin-jury-entry');
|
||||||
if (entry) entry.remove();
|
if (entry) entry.remove();
|
||||||
};
|
};
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
ulbRow.style.display = show ? '' : 'none';
|
ulbRow.style.display = show ? '' : 'none';
|
||||||
if (ulbAsterisk) ulbAsterisk.style.display = show ? '' : 'none';
|
if (ulbAsterisk) ulbAsterisk.style.display = show ? '' : 'none';
|
||||||
var inputs = ulbRow.querySelectorAll('input[name="jury_promoteur_ulb_name[]"]');
|
var inputs = ulbRow.querySelectorAll('input[name="jury_promoteur_ulb_name[]"]');
|
||||||
inputs.forEach(function (inp, idx) {
|
inputs.forEach((inp, idx) => {
|
||||||
inp.required = adminMode ? false : show && idx === 0;
|
inp.required = adminMode ? false : show && idx === 0;
|
||||||
inp.disabled = !show;
|
inp.disabled = !show;
|
||||||
if (!show) inp.value = '';
|
if (!show) inp.value = '';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Reads the container id from data-search-container-id on the pill-search div.
|
* Reads the container id from data-search-container-id on the pill-search div.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var pillDiv = document.querySelector('[data-search-container-id]');
|
var pillDiv = document.querySelector('[data-search-container-id]');
|
||||||
if (!pillDiv) return;
|
if (!pillDiv) return;
|
||||||
var containerId = pillDiv.getAttribute('data-search-container-id');
|
var containerId = pillDiv.getAttribute('data-search-container-id');
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
*
|
*
|
||||||
* Loaded on all admin pages via footer.php.
|
* Loaded on all admin pages via footer.php.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
// Toast accessibility — auto-focus warning toasts
|
// Toast accessibility — auto-focus warning toasts
|
||||||
document.body.addEventListener('htmx:afterSettle', function (e) {
|
document.body.addEventListener('htmx:afterSettle', (e) => {
|
||||||
if (e.target && e.target.id === 'toast-region') {
|
if (e.target && e.target.id === 'toast-region') {
|
||||||
var warn = e.target.querySelector('.toast--warning');
|
var warn = e.target.querySelector('.toast--warning');
|
||||||
if (warn) {
|
if (warn) {
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Markdown cheatsheet: remove stale dialogs before a new one arrives
|
// Markdown cheatsheet: remove stale dialogs before a new one arrives
|
||||||
document.body.addEventListener('htmx:beforeRequest', function (e) {
|
document.body.addEventListener('htmx:beforeRequest', (e) => {
|
||||||
if (
|
if (
|
||||||
e.detail.requestConfig &&
|
e.detail.requestConfig &&
|
||||||
e.detail.requestConfig.path === '/admin/markdown-cheatsheet-fragment.php'
|
e.detail.requestConfig.path === '/admin/markdown-cheatsheet-fragment.php'
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Markdown cheatsheet: close on backdrop (dialog element) click
|
// Markdown cheatsheet: close on backdrop (dialog element) click
|
||||||
document.body.addEventListener('click', function (e) {
|
document.body.addEventListener('click', (e) => {
|
||||||
if (e.target.tagName === 'DIALOG' && e.target.id === 'md-cheatsheet-dialog') {
|
if (e.target.tagName === 'DIALOG' && e.target.id === 'md-cheatsheet-dialog') {
|
||||||
e.target.close();
|
e.target.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Single-open behavior on mobile (≤ 1025px). Re-initializes after HTMX swaps.
|
* Single-open behavior on mobile (≤ 1025px). Re-initializes after HTMX swaps.
|
||||||
* On desktop, all panels close when crossing the breakpoint.
|
* On desktop, all panels close when crossing the breakpoint.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var INDEX_SEL = '#repertoire-index';
|
var INDEX_SEL = '#repertoire-index';
|
||||||
var ACCORDION_SEL = '.rep-accordion';
|
var ACCORDION_SEL = '.rep-accordion';
|
||||||
var TOGGLE_SEL = '.rep-accordion__toggle';
|
var TOGGLE_SEL = '.rep-accordion__toggle';
|
||||||
@@ -17,18 +17,18 @@
|
|||||||
function initAccordions(root) {
|
function initAccordions(root) {
|
||||||
if (!isMobile()) return;
|
if (!isMobile()) return;
|
||||||
var toggles = root.querySelectorAll(TOGGLE_SEL);
|
var toggles = root.querySelectorAll(TOGGLE_SEL);
|
||||||
toggles.forEach(function (btn) {
|
toggles.forEach((btn) => {
|
||||||
// Skip students column — always visible, not an accordion
|
// Skip students column — always visible, not an accordion
|
||||||
if (btn.closest('[data-col="students"]')) return;
|
if (btn.closest('[data-col="students"]')) return;
|
||||||
if (btn._accordionBound) return;
|
if (btn._accordionBound) return;
|
||||||
btn._accordionBound = true;
|
btn._accordionBound = true;
|
||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', () => {
|
||||||
var section = btn.closest(ACCORDION_SEL);
|
var section = btn.closest(ACCORDION_SEL);
|
||||||
var panel = section.querySelector(PANEL_SEL);
|
var panel = section.querySelector(PANEL_SEL);
|
||||||
var isOpen = btn.getAttribute('aria-expanded') === 'true';
|
var isOpen = btn.getAttribute('aria-expanded') === 'true';
|
||||||
|
|
||||||
// Close all others (except students column)
|
// Close all others (except students column)
|
||||||
root.querySelectorAll(ACCORDION_SEL).forEach(function (s) {
|
root.querySelectorAll(ACCORDION_SEL).forEach((s) => {
|
||||||
if (s.dataset.col === 'students') return;
|
if (s.dataset.col === 'students') return;
|
||||||
var p = s.querySelector(PANEL_SEL);
|
var p = s.querySelector(PANEL_SEL);
|
||||||
var t = s.querySelector(TOGGLE_SEL);
|
var t = s.querySelector(TOGGLE_SEL);
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
initAccordions(document);
|
initAccordions(document);
|
||||||
|
|
||||||
// Re-bind after HTMX swaps (use live DOM since e.detail.target may be detached)
|
// Re-bind after HTMX swaps (use live DOM since e.detail.target may be detached)
|
||||||
document.body.addEventListener('htmx:afterSwap', function (e) {
|
document.body.addEventListener('htmx:afterSwap', (e) => {
|
||||||
if (
|
if (
|
||||||
e.detail.target &&
|
e.detail.target &&
|
||||||
e.detail.target.matches &&
|
e.detail.target.matches &&
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
// Re-bind on resize crossing the breakpoint
|
// Re-bind on resize crossing the breakpoint
|
||||||
var wasMobile = isMobile();
|
var wasMobile = isMobile();
|
||||||
window.addEventListener('resize', function () {
|
window.addEventListener('resize', () => {
|
||||||
var nowMobile = isMobile();
|
var nowMobile = isMobile();
|
||||||
if (nowMobile !== wasMobile) {
|
if (nowMobile !== wasMobile) {
|
||||||
wasMobile = nowMobile;
|
wasMobile = nowMobile;
|
||||||
@@ -75,12 +75,12 @@
|
|||||||
// Switching to desktop — close all panels
|
// Switching to desktop — close all panels
|
||||||
document
|
document
|
||||||
.querySelectorAll(INDEX_SEL + ' ' + TOGGLE_SEL)
|
.querySelectorAll(INDEX_SEL + ' ' + TOGGLE_SEL)
|
||||||
.forEach(function (btn) {
|
.forEach((btn) => {
|
||||||
btn.setAttribute('aria-expanded', 'false');
|
btn.setAttribute('aria-expanded', 'false');
|
||||||
});
|
});
|
||||||
document
|
document
|
||||||
.querySelectorAll(INDEX_SEL + ' ' + PANEL_SEL)
|
.querySelectorAll(INDEX_SEL + ' ' + PANEL_SEL)
|
||||||
.forEach(function (p) {
|
.forEach((p) => {
|
||||||
p.classList.remove('is-open');
|
p.classList.remove('is-open');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Shows a popover with HTMX-fetched student details on hover over links
|
* Shows a popover with HTMX-fetched student details on hover over links
|
||||||
* with `data-student-name` attribute.
|
* with `data-student-name` attribute.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var popover = document.getElementById('student-popover');
|
var popover = document.getElementById('student-popover');
|
||||||
var currentAnchor = null;
|
var currentAnchor = null;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
'mouseenter',
|
'mouseenter',
|
||||||
function (e) {
|
(e) => {
|
||||||
var a = e.target.closest('[data-student-name]');
|
var a = e.target.closest('[data-student-name]');
|
||||||
if (!a) return;
|
if (!a) return;
|
||||||
currentAnchor = a;
|
currentAnchor = a;
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
document.body.addEventListener('htmx:afterSwap', function (e) {
|
document.body.addEventListener('htmx:afterSwap', (e) => {
|
||||||
if (e.detail.target !== popover) return;
|
if (e.detail.target !== popover) return;
|
||||||
if (currentAnchor) position(currentAnchor);
|
if (currentAnchor) position(currentAnchor);
|
||||||
popover.hidden = false;
|
popover.hidden = false;
|
||||||
@@ -37,13 +37,13 @@
|
|||||||
|
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
'mouseleave',
|
'mouseleave',
|
||||||
function (e) {
|
(e) => {
|
||||||
if (
|
if (
|
||||||
!e.target.closest('[data-student-name]') &&
|
!e.target.closest('[data-student-name]') &&
|
||||||
!e.target.closest('#student-popover')
|
!e.target.closest('#student-popover')
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
setTimeout(function () {
|
setTimeout(() => {
|
||||||
if (
|
if (
|
||||||
!document.querySelector('[data-student-name]:hover') &&
|
!document.querySelector('[data-student-name]:hover') &&
|
||||||
!document.querySelector('#student-popover:hover')
|
!document.querySelector('#student-popover:hover')
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Operates on #sidebar-links-form and #sidebar-link-tpl.
|
* Operates on #sidebar-links-form and #sidebar-link-tpl.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var form = document.getElementById('sidebar-links-form');
|
var form = document.getElementById('sidebar-links-form');
|
||||||
var tpl = document.getElementById('sidebar-link-tpl');
|
var tpl = document.getElementById('sidebar-link-tpl');
|
||||||
if (!form || !tpl) return;
|
if (!form || !tpl) return;
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
function reindexLinks() {
|
function reindexLinks() {
|
||||||
var rows = form.querySelectorAll('.sidebar-link-row');
|
var rows = form.querySelectorAll('.sidebar-link-row');
|
||||||
rows.forEach(function (row, i) {
|
rows.forEach((row, i) => {
|
||||||
row.querySelectorAll('input').forEach(function (inp) {
|
row.querySelectorAll('input').forEach((inp) => {
|
||||||
if (inp.name) {
|
if (inp.name) {
|
||||||
inp.name = inp.name.replace(/links\[\d+\]/, 'links[' + i + ']');
|
inp.name = inp.name.replace(/links\[\d+\]/, 'links[' + i + ']');
|
||||||
}
|
}
|
||||||
@@ -20,14 +20,14 @@
|
|||||||
inp.id = inp.id.replace(/sl_\d+/, 'sl_' + i);
|
inp.id = inp.id.replace(/sl_\d+/, 'sl_' + i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
row.querySelectorAll('label[for]').forEach(function (lbl) {
|
row.querySelectorAll('label[for]').forEach((lbl) => {
|
||||||
lbl.setAttribute('for', lbl.getAttribute('for').replace(/sl_\d+/, 'sl_' + i));
|
lbl.setAttribute('for', lbl.getAttribute('for').replace(/sl_\d+/, 'sl_' + i));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event delegation for remove buttons
|
// Event delegation for remove buttons
|
||||||
form.addEventListener('click', function (e) {
|
form.addEventListener('click', (e) => {
|
||||||
if (!e.target.closest('.remove-sidebar-link-btn')) return;
|
if (!e.target.closest('.remove-sidebar-link-btn')) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.target.closest('.sidebar-link-row').remove();
|
e.target.closest('.sidebar-link-row').remove();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* smtp-error-focus.js — Scrolls to and focuses the SMTP field that caused a probe
|
* smtp-error-focus.js — Scrolls to and focuses the SMTP field that caused a probe
|
||||||
* error. Reads the field id from data-smtp-error-field on the SMTP form.
|
* error. Reads the field id from data-smtp-error-field on the SMTP form.
|
||||||
*/
|
*/
|
||||||
(function () {
|
(() => {
|
||||||
var form = document.querySelector('form[data-smtp-error-field]');
|
var form = document.querySelector('form[data-smtp-error-field]');
|
||||||
if (!form) return;
|
if (!form) return;
|
||||||
var fieldId = form.getAttribute('data-smtp-error-field');
|
var fieldId = form.getAttribute('data-smtp-error-field');
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
/**
|
/**
|
||||||
* admin-toc.php — sidebar table-of-contents for long admin pages.
|
* admin-toc.php — sidebar table-of-contents for long admin pages.
|
||||||
*
|
*
|
||||||
* Rendered as an <aside> inside <main>, before the <article> content.
|
* Uses the same <details class="toc"> markup as the public content pages
|
||||||
* Uses IntersectionObserver to highlight the active section.
|
* (about, charte, licence). The link list is populated by admin-toc.js
|
||||||
|
* at runtime.
|
||||||
*/
|
*/
|
||||||
?>
|
?>
|
||||||
<aside id="admin-toc" class="admin-toc" aria-label="Sur cette page">
|
<details class="toc" id="admin-toc" open aria-label="Sur cette page">
|
||||||
<nav class="admin-toc-list" id="admin-toc-list">
|
<summary><?= icon('caret-down', 0, 'toc-caret') ?> SUR CETTE PAGE</summary>
|
||||||
<!-- populated by JS (admin-toc.js, loaded via admin.min.js) -->
|
<ul class="toc-list" id="admin-toc-list">
|
||||||
</nav>
|
<!-- populated by admin-toc.js -->
|
||||||
</aside>
|
</ul>
|
||||||
|
</details>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ function renderEntries(array $entries): string
|
|||||||
<!-- Table of contents: collapsible on mobile, force-open on desktop -->
|
<!-- Table of contents: collapsible on mobile, force-open on desktop -->
|
||||||
<details class="toc" open>
|
<details class="toc" open>
|
||||||
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
||||||
<ul>
|
<ul class="toc-list">
|
||||||
<li><a href="#apropos-intro">À propos</a></li>
|
<li><a href="#apropos-intro">À propos</a></li>
|
||||||
<?php if (!empty($contacts)): ?>
|
<?php if (!empty($contacts)): ?>
|
||||||
<li><a href="#apropos-contacts">Contacts</a></li>
|
<li><a href="#apropos-contacts">Contacts</a></li>
|
||||||
@@ -54,6 +54,7 @@ function renderEntries(array $entries): string
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<article>
|
||||||
<!-- Intro text from DB -->
|
<!-- Intro text from DB -->
|
||||||
<section class="content-section" id="apropos-intro">
|
<section class="content-section" id="apropos-intro">
|
||||||
<?= $aboutHtml ?>
|
<?= $aboutHtml ?>
|
||||||
@@ -113,5 +114,6 @@ function renderEntries(array $entries): string
|
|||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</section>
|
</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<?php if (!empty($tocItems)): ?>
|
<?php if (!empty($tocItems)): ?>
|
||||||
<details class="toc" open>
|
<details class="toc" open>
|
||||||
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
||||||
<ul>
|
<ul class="toc-list">
|
||||||
<?php foreach ($tocItems as $item): ?>
|
<?php foreach ($tocItems as $item): ?>
|
||||||
<li><a href="<?= htmlspecialchars($item['href']) ?>"><?= htmlspecialchars($item['label']) ?></a></li>
|
<li><a href="<?= htmlspecialchars($item['href']) ?>"><?= htmlspecialchars($item['label']) ?></a></li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
</details>
|
</details>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="content">
|
<article>
|
||||||
<?php if (!empty(trim($content))): ?>
|
<?php if (!empty(trim($content))): ?>
|
||||||
<?= $html ?>
|
<?= $html ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p>Contenu à venir.</p>
|
<p>Contenu à venir.</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</article>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<?php if (!empty($tocItems)): ?>
|
<?php if (!empty($tocItems)): ?>
|
||||||
<details class="toc" open>
|
<details class="toc" open>
|
||||||
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
<summary><?= icon('caret-down', 0, 'toc-caret') ?> PARTIES</summary>
|
||||||
<ul>
|
<ul class="toc-list">
|
||||||
<?php foreach ($tocItems as $item): ?>
|
<?php foreach ($tocItems as $item): ?>
|
||||||
<li><a href="<?= htmlspecialchars($item['href']) ?>"><?= htmlspecialchars($item['label']) ?></a></li>
|
<li><a href="<?= htmlspecialchars($item['href']) ?>"><?= htmlspecialchars($item['label']) ?></a></li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
</details>
|
</details>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="content">
|
<article>
|
||||||
<?php if (!empty(trim($content))): ?>
|
<?php if (!empty(trim($content))): ?>
|
||||||
<?= $html ?>
|
<?= $html ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p>Contenu à venir.</p>
|
<p>Contenu à venir.</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</article>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
"!app/public/assets/js/htmx.min.js",
|
"!app/public/assets/js/htmx.min.js",
|
||||||
"!app/public/assets/js/overtype.min.js",
|
"!app/public/assets/js/overtype.min.js",
|
||||||
"!app/public/assets/js/sortable.min.js",
|
"!app/public/assets/js/sortable.min.js",
|
||||||
"!app/public/assets/js/vendor/**"
|
"!app/public/assets/js/vendor/**",
|
||||||
|
"!app/public/assets/css/filepond*.css",
|
||||||
|
"!app/public/assets/css/modern-normalize*.css",
|
||||||
|
"!app/public/assets/dist/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"css": {
|
"css": {
|
||||||
@@ -15,7 +18,7 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": false
|
"enabled": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
|
|||||||
4
justfile
4
justfile
@@ -74,6 +74,10 @@ build-js:
|
|||||||
build-install:
|
build-install:
|
||||||
@npm ci
|
@npm ci
|
||||||
|
|
||||||
|
[group('build')]
|
||||||
|
build-lint:
|
||||||
|
@npx biome lint app/public/assets/css/ app/public/assets/js/app/ scripts/
|
||||||
|
|
||||||
[group('build')]
|
[group('build')]
|
||||||
build-check:
|
build-check:
|
||||||
@echo "Checking if build output is up to date…"
|
@echo "Checking if build output is up to date…"
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ if (!existsSync(resolve(root, "node_modules"))) {
|
|||||||
execSync("npm ci", { cwd: root, stdio: "inherit" });
|
execSync("npm ci", { cwd: root, stdio: "inherit" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buildAll || onlyCss) {
|
||||||
|
run("Linting CSS + JS (biome)", "npx biome lint app/public/assets/css/ app/public/assets/js/app/ scripts/ || true");
|
||||||
|
}
|
||||||
|
|
||||||
if (buildAll || onlyCss) {
|
if (buildAll || onlyCss) {
|
||||||
run("Building CSS bundles", "node scripts/build-css.mjs");
|
run("Building CSS bundles", "node scripts/build-css.mjs");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user