@bimetal/headless
v0.17.0
Published
Framework-agnostic headless calendar adapter: event actions, drag preview logic
Maintainers
Readme
@bimetal/headless
Der kanonische, framework-agnostische Kalender-Controller. createCalendarController besitzt die gesamte Interaktionssemantik: Navigation, Dialog-Routing (inkl. Recurrence-Instanzen), Drag & Drop, Selection, Kategorie-/Ressourcen-Filter, Undo, Rule-Fehler und Tastatur. Die UI-Stacks (React, Vue, Svelte, Angular, Vanilla) sind reine Bindings.
Die Schnittlinie ist scharf: DOM-Messung & Listener-Registrierung = Framework. Jedes if = Controller. Damit ist Cross-Framework-Drift strukturell ausgeschlossen statt eine Frage der Disziplin. Das Paket kennt kein React/Vue/Svelte/Angular und kein DOM jenseits struktureller Drag-Typen — es läuft in Tests ohne Browser, in Workers und in Node.
Installation
npm install @bimetal/headless @bimetal/calendar-datacreateCalendarController
import { createCalendarController } from '@bimetal/headless';
import { createCalendarStore } from '@bimetal/calendar-data';
const controller = createCalendarController({
store: createCalendarStore(),
config: { core: { timezone: 'Europe/Berlin' } },
rules: [], // optionale CalendarRules
customViews: [], // optionale agnostische View-Specs
});Signatur (gekürzt):
function createCalendarController(opts: CalendarControllerOptions): CalendarController;
interface CalendarControllerOptions {
store: CalendarStore; // Basis-Store; intern withRules-gewrappt
config?: CalendarConfigInput; // core/locale/categories/resources
rules?: readonly CalendarRule[];
customViews?: readonly CustomViewSpec[];
eventDialogKind?: 'builtin' | 'none' | 'custom';
}Snapshot / subscribe-Vertrag
Der Controller folgt dem useSyncExternalStore-Kontrakt:
getSnapshot()liefert eine identitäts-stabile, immutable Sicht aller Interaktions-State-Felder (currentDate,currentView,events,dialog,selection,dragPreview,lastAction,hiddenCategories, …). Dieselbe Referenz wird zurückgegeben, bis ein Intent tatsächlich etwas ändert.subscribe(listener)registriert einen Listener und gibt eineUnsubscribe-Funktion zurück.destroy()räumt Store-Subscriptions und Listener ab.
Reaktive Option-Updates ohne Neuerzeugung: setConfig, setRules, setCustomViews. Intents sind direkte Methoden — prev/next/today/setView, openEdit/openCreate/saveDialog/deleteDialog, dropAllDay/dropTimed/dropResource/dropTimeline, selectEvent, toggleCategory/toggleResource, undo, handleKeydown.
Architektur
Vertiefung: doc/02-architecture/06-headless.md.
