Slider mit Autorotation, Beitragsliste mit Blaettern
Zwei Wuensche, zwei Bausteine. Die Autorotation ist Sekunden, kein Haekchen: 0 heisst aus, ab 3 laeuft sie. Sie braucht ein Skript - das zweite auf einer oeffentlichen Seite nach den Tabs - und es kommt nur auf Seiten mit einem solchen Slider, sonst bleibt script-src 'none'. Es laeuft nicht bei "weniger Bewegung", hoert beim ersten Tippen, Klicken, Wischen oder Scrollen endgueltig auf und pausiert bei Zeiger, Fokus, Hintergrundtab und ausserhalb des Bildes. Ohne Skript bleibt der Slider vollstaendig bedienbar. Die Beitragsliste kann blaettern statt abzuschneiden. Eine Auswahl auf der Startseite zeigt die neuesten und hoert auf; eine Uebersichtsseite blaettert und sagt, wie viele es sind. Das war die eigene Regel aus Arbeitspaket M, die fuer diesen Baustein noch nicht galt. Beim Bauen zweimal dieselbe Falle wie vorgestern: eine Variable war in der Schliessung nicht gebunden. Einmal das Sprungziel des Sliders (-78 statt 666), einmal die Seitenzahl - die zweite fiel sofort auf, weil die Seite mit 500 antwortete. Geprueft: 395 Tests unter PHP 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b2c4f45474
commit
5a5933f8cd
12 files changed
+229
-9
No files matched your search
@@ -137,6 +137,12 @@ final class DocumentValidator
|
||||
// und Kategorie gehören in ein Blogarchiv und lassen eine Projektseite falsch
|
||||
// aussehen. Voreinstellung ist der alte Zustand, damit bestehende Seiten sich nicht
|
||||
// ändern, wenn niemand etwas einstellt.
|
||||
if (isset($props['paginate'])) {
|
||||
// Eine Auswahl zeigt die ersten n und hört auf. Eine Übersicht blättert - sonst
|
||||
// verschweigt sie, dass es mehr gibt. Das ist die eigene Regel aus Arbeitspaket M.
|
||||
$this->require($schemaVersion >= 17, 'Das Blättern einer Beitragsliste braucht Schema v17.');
|
||||
$this->require(is_bool($props['paginate']), 'Das Blättern ist ja oder nein.');
|
||||
}
|
||||
foreach (['showMeta', 'showExcerpt', 'showTerms'] as $switch) {
|
||||
if (isset($props[$switch])) {
|
||||
$this->require($schemaVersion >= 17, 'Die Anzeigeoptionen der Beitragsliste brauchen Schema v17.');
|
||||
@@ -235,6 +241,12 @@ final class DocumentValidator
|
||||
$this->require(is_int($props[$key] ?? null) && $props[$key] >= 1 && $props[$key] <= 6, sprintf('"%s" liegt zwischen 1 und 6.', $key));
|
||||
}
|
||||
$this->require(is_bool($props['snap'] ?? null), 'Das Einrasten ist anzugeben.');
|
||||
if (isset($props['autoplay'])) {
|
||||
// Sekunden bis zur nächsten Folie. 0 heißt aus, und unter drei Sekunden wäre es
|
||||
// keine Hilfe mehr, sondern Hektik.
|
||||
$this->require($schemaVersion >= 17, 'Das automatische Weiterblättern braucht Schema v17.');
|
||||
$this->require(is_int($props['autoplay']) && (0 === $props['autoplay'] || ($props['autoplay'] >= 3 && $props['autoplay'] <= 30)), 'Das Weiterblättern liegt bei 0 (aus) oder 3 bis 30 Sekunden.');
|
||||
}
|
||||
if (isset($props['dots'])) {
|
||||
// Punkte und Pfeile zeichnet der Browser aus dem Scrollbereich selbst; das
|
||||
// geht erst ab v17, weil es vorher keine Eigenschaft dafür gab.
|
||||
|
||||
@@ -65,9 +65,12 @@ final class PageController extends AbstractController
|
||||
'page' => $page,
|
||||
'canonical' => rtrim($this->siteOrigin, '/').$this->settings->publicPath($path, $home),
|
||||
'navigation' => $this->navigation($path),
|
||||
'postLists' => $this->postLists($page['document']),
|
||||
'postLists' => $this->postLists($page['document'], max(1, $request->query->getInt('seite', 1))),
|
||||
'postMeta' => 'post' === $page['type'] ? $this->posts->termsFor($page['id']) : null,
|
||||
'needsTabs' => $this->hasType($page['document'], 'tabs'),
|
||||
// Ein Slider mit automatischem Weiterblättern ist der zweite Baustein, der
|
||||
// dafür ein Skript braucht - nach demselben Muster wie die Tabs.
|
||||
'needsSlider' => $this->hasAutoplaySlider($page['document']),
|
||||
// Own scripts arrive as one file per page, never inline, so the policy can stay
|
||||
// hash based and no page without a script loses its strict setting.
|
||||
'pageScript' => [] === $this->code->scriptsOf($page['document']) ? null : '/assets/seite/'.$page['id'].'.js',
|
||||
@@ -83,7 +86,7 @@ final class PageController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
$wantsScript = null !== $page && ($this->hasType($page['document'], 'tabs') || [] !== $this->code->scriptsOf($page['document']));
|
||||
$wantsScript = null !== $page && ($this->hasType($page['document'], 'tabs') || $this->hasAutoplaySlider($page['document']) || [] !== $this->code->scriptsOf($page['document']));
|
||||
$frames = null === $page ? [] : $this->frameHosts($request, $page['document'], ...array_values($areas), ...array_values($sections));
|
||||
|
||||
return $this->shell($response, $wantsScript, $styles, null !== $googleFonts, $frames);
|
||||
@@ -233,6 +236,23 @@ final class PageController extends AbstractController
|
||||
}
|
||||
|
||||
/** True when the stored document contains at least one block of that type. */
|
||||
/** True when a slider on this page asked to advance by itself. */
|
||||
private function hasAutoplaySlider(array $document): bool
|
||||
{
|
||||
$found = false;
|
||||
$walk = function (array $blocks) use (&$walk, &$found): void {
|
||||
foreach ($blocks as $block) {
|
||||
if ('slider' === ($block['type'] ?? null) && ($block['props']['autoplay'] ?? 0) >= 3) {
|
||||
$found = true;
|
||||
}
|
||||
$walk($block['children'] ?? []);
|
||||
}
|
||||
};
|
||||
$walk($document['blocks'] ?? []);
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
private function hasType(array $document, string $type): bool
|
||||
{
|
||||
$found = false;
|
||||
@@ -297,10 +317,10 @@ final class PageController extends AbstractController
|
||||
*
|
||||
* @return array<string, list<array>>
|
||||
*/
|
||||
private function postLists(array $document): array
|
||||
private function postLists(array $document, int $page = 1): array
|
||||
{
|
||||
$lists = [];
|
||||
$walk = function (array $blocks) use (&$walk, &$lists): void {
|
||||
$walk = function (array $blocks) use (&$walk, &$lists, $page): void {
|
||||
foreach ($blocks as $block) {
|
||||
if ('postlist' === ($block['type'] ?? null)) {
|
||||
$props = $block['props'] ?? [];
|
||||
@@ -310,7 +330,12 @@ final class PageController extends AbstractController
|
||||
$term = $this->posts->findTerm($kind, $props[$kind]);
|
||||
}
|
||||
}
|
||||
$lists[$block['id']] = $this->posts->published(1, $term['id'] ?? null, $props['count'])['posts'];
|
||||
// Eine blätternde Liste nimmt die Seite aus der Anfrage, eine Auswahl
|
||||
// immer die erste - sonst würden zwei Listen auf einer Seite einander
|
||||
// umblättern.
|
||||
$paginate = (bool) ($props['paginate'] ?? false);
|
||||
$result = $this->posts->published($paginate ? $page : 1, $term['id'] ?? null, $props['count']);
|
||||
$lists[$block['id']] = $paginate ? $result : $result['posts'];
|
||||
}
|
||||
$walk($block['children'] ?? []);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user