84c41f80b1
Add a global modal/popup system driven entirely from the CMS. A new `modal` config collection self-targets routes via a glob/regex urlPattern (optional time window), and its `content` array of block references renders through the existing BlockRenderer — so markdown, HTML and YouTube blocks work exactly like a page row. A `modals` list collection holds the active set behind an enable flag (analogous to `campaigns`). - modal-match.ts: glob (`*`/`**`) + `~regex` pathname matcher, time window and modal selection helpers (+ unit tests) - ContentModal.svelte: immediate/delay/scroll/exit_intent triggers, session vs. permanent dismiss (sessionStorage + localStorage, keyed by slug+version) - +layout.server.ts: load `modals-global` in the bootstrap batch, resolve block images server-side - +layout.svelte: mount on non-embed routes, match against the current path Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
/**
|
|
* URL-Pattern-Matching für Modals (siehe modal.json5 `urlPattern`).
|
|
*
|
|
* Glob: '*' = ein Pfad-Segment (kein '/')
|
|
* '**' = beliebig tief (auch leer)
|
|
* '/aktion' → exakt /aktion
|
|
* '/aktion/**' → /aktion und alles darunter
|
|
* '/aktion/*' → direkte Kinder von /aktion (nicht /aktion selbst)
|
|
* '**' oder '/*' → alles
|
|
* Regex: Muster mit '~' beginnen, Rest ist echte Regex.
|
|
*
|
|
* Match gegen den Pfad (ohne Query), case-insensitive, trailing-Slash egal.
|
|
*/
|
|
|
|
/** Trailing-Slash normalisieren (Root bleibt "/"). */
|
|
function normPath(p: string): string {
|
|
const noQuery = p.split('?')[0].split('#')[0];
|
|
if (noQuery.length > 1 && noQuery.endsWith('/')) return noQuery.slice(0, -1);
|
|
return noQuery || '/';
|
|
}
|
|
|
|
/** Glob-Pattern → RegExp. `**` matcht über Segmente, `*` innerhalb. */
|
|
function globToRegExp(glob: string): RegExp {
|
|
const DOUBLE = ' |