Hotwire — HTML-driven UI Layer
Hotwire je interaktivní UI vrstva admin panelu. Umožňuje AJAX navigaci, odesílání formulářů a JSON akce bez psaní JavaScriptu — vše se řídí data-* atributy přímo v HTML.
Analogie s HTMX a Hotwire (Rails)
Název vychází z podobné filosofie jako populární knihovny, ale implementace je vlastní a minimální.
| Koncept | HTMX | Hotwire (Rails) | Petrovo CMS Hotwire |
|---|---|---|---|
| Navigace bez JS | hx-get |
Turbo Drive | data-content |
| JSON akce | hx-post + trigger |
Turbo Streams | data-json |
| Formuláře | hx-post na form |
Turbo Frames | data-form |
| Filtry | hx-trigger="change" |
— | data-filter |
| Callback | hx-on / events |
Stimulus controller | data-after |
| Potvrzení | hx-confirm |
data-confirm |
data-confirm |
| Cílový element | hx-target |
Frame ID | vždy #content |
Klíčový rozdíl: HTMX a Hotwire jsou obecné knihovny s desítkami atributů. Petrovo CMS Hotwire je záměrně minimální — pouze atributy, které admin panel reálně potřebuje. Žádná abstrakce navíc.
Filosofie
- HTML-driven: Logika je v HTML atributech, ne v JS souborech
- Progressive: Stránka funguje i bez JS (fallback na
href) - No layers: Přímé fetch callsites bez interceptorů nebo middleware
- Transparent: Co vidíš v HTML je přesně to, co se stane
Architektura
src/admin/js/hotwire/
├── index.js # Centrální re-export
├── setup.js # Inicializace a orchestrace
├── actions.js # Event delegation — všechny data-* handlery
├── fetch_content.js # GET/POST → HTML → inject do #content
├── fetch_json.js # POST/DELETE → JSON + message + callback
├── callbacks.js # Window-level callback funkce
└── core/
├── request.js # Low-level fetch wrapper (CSRF, timeout, session)
└── response.js # JSON parsing a normalizace
Tok dat
HTML (data-* atributy)
↓
setupContentLoading() ← zavolá se jednou při startu
↓
actions.js
event delegation
↓
┌──────┼──────────────┐
↓ ↓ ↓
data- data-json data-form
content data-filter
↓ ↓ ↓
└──────┴──────────────┘
↓
fetchContent() nebo fetchJson()
↓
request() ← CSRF header, timeout, session check
↓
HTML → inject #content
JSON → message + callback
Inicializace
setupContentLoading() se volá jednou v main.js při startu admin panelu:
import { setupContentLoading } from './hotwire/index.js';
setupContentLoading();
Co setup provede:
- Inicializuje
history.replaceState()pro aktuální URL - Zavolá
setupActions()— registruje všechny event listenery - Přidá
popstatelistener (tlačítka Zpět/Vpřed →location.reload())
Po každém načtení HTML do #content se automaticky zavolá afterContentLoad(), která inicializuje tab systém a autofocus na formulářích.
Moduly
actions.js — Event delegation
Jedno místo pro všechny HTML atributy. Registruje tři globální listenery na document:
- Click — sleduje poslední stisknuté
button[name](pro CRUD formy) - Click — zpracovává
[data-content]a[data-json] - Change — auto-submit pro
[data-filter] select - Submit — zpracovává
[data-filter]a[data-form]
fetch_content.js — HTML akce
GET nebo POST → HTML odpověď → inject do #content.
Používá se pro navigaci (list, detail, formulář). Po úspěšném načtení volá afterContentLoad() a aktualizuje URL přes history.pushState().
fetch_json.js — JSON akce
POST nebo DELETE → JSON odpověď → zobrazí message, volá callback.
Používá se pro akce bez přechodu na jinou stránku (smazání záznamu, toggle stavu).
Pokud odpověď obsahuje data.listUrl, automaticky načte HTML a aktualizuje #content.
core/request.js — HTTP vrstva
Low-level fetch wrapper. Přidává:
- CSRF header z
state.csrfToken - Automatický timeout (konfigurovatelný přes
data-timeoutnebodata-unlimited) - Detekci vypršení session — pokud odpověď obsahuje login formulář, přesměruje na
/ADMIN/{lang}/login - Auto-detekci JSON vs HTML odpovědi
core/response.js — Normalizace
Normalizuje odpověď do jednotného formátu {type: 'html', html} nebo {type: 'json', ...}.
Pokud JSON parse selže, vrátí raw HTML (graceful fallback).