GrundbegriffeGilt fürLite und Pro
Ressourcen, Ereignisse und Intervalle
Zeilen sind ResourceData-Objekte mit einer id und einem name; Balken sind EventData-Objekte mit id, text, start, end und der ID der Ressource, zu der sie gehören. IDs sind Strings oder Zahlen und werden strikt verglichen, 1 und '1' sind also verschieden. Intervalle sind halboffen, [start, end), und Datumsangaben sind bürgerliche Zeitwerte (lokale Datum-Uhrzeit), geschrieben als ISO-Strings mit Sekunden; die Bibliothek rechnet nie Zeitzonen um. Eigene Felder ergänzen Sie mit EventData<YourFields> und grenzen sie ein, wenn sie vom Control zurückkommen.
SuperScheduler zeichnet zwei Arrays: Ressourcen, die Zeilen, und Ereignisse, die Balken in diesen Zeilen. Beides sind einfache Objekte, die Sie aus Ihren eigenen Daten erzeugen. Wer vier Regeln beachtet, vermeidet fast jedes Problem der Art „mein Ereignis erscheint nicht“: IDs sind strikt typisiert, Intervalle schließen ihr Ende aus, Datumsstrings enthalten Sekunden, und Zeiten sind lokale Uhrzeiten ohne Zeitzone.
Die Regeln auf dieser Seite gelten für beide Editionen, sofern ein Abschnitt nichts anderes sagt. Lite akzeptiert eine Teilmenge der Felder; siehe Schnellstart mit Lite.
Ressourcen
Eine Ressource braucht eine id und einen name. In Pro ist ResourceData offen: Sie können eigene Felder (floor, kind, capacity) im selben Objekt behalten und sie in Callbacks und Render-Hooks wieder auslesen.
Die Felder, die Sie in Pro am häufigsten verwenden:
| Feld | Zweck |
|---|---|
id | String oder Zahl; eindeutig unter den Ressourcen |
name | Text im Zeilenkopf |
backColor, cssClass, html, toolTip | Aussehen des Zeilenkopfs (html ist vertrauenswürdiges Markup) |
minHeight, eventHeight | Geometrie nur dieser Zeile |
cellsDisabled | Jede Zelle der Zeile lehnt Ablegen und Auswahl ab |
columns | Zellen für zusätzliche Spalten im Zeilenkopf (mit rowHeaderColumns) |
children, expanded | Ein Ressourcenbaum (mit treeEnabled) |
frozen | 'top' oder 'bottom': Die Zeile bleibt beim Scrollen sichtbar |
Ressourcenbäume
Um Zeilen zu gruppieren, verschachteln Sie Ressourcen in children und setzen treeEnabled am Planer. Ohne treeEnabled werden Kinder ignoriert, und die Liste bleibt flach. Ein Elternknoten startet eingeklappt, sofern sein Feld expanded nicht true ist. Elternzeilen können wie jede Zeile Ereignisse enthalten; setzen Sie treePreventParentUsage, damit sie reine Gruppenüberschriften bleiben. Bäume, Zeilenspalten und Zeilenauswahl behandelt Bäume, Spalten und Auswahl. Lite akzeptiert nur flache Listen.
Ereignisse
Ein Ereignis braucht id, text, start, end und, um in einer Zeile zu erscheinen, resource. Optionale Felder ändern Aussehen und Verhalten:
| Feld | Zweck |
|---|---|
backColor, fontColor, borderColor, barColor | Farben des Balkens, seines Texts, seines Rahmens und des Dauerbalkens |
cssClass | Klassen für Ihr eigenes CSS |
html | Inhalt als vertrauenswürdiges HTML (Nutzerdaten mit SuperScheduler.Util.escapeHtml escapen) |
toolTip, bubbleHtml | Nativer Tooltip oder Inhalt der Hover-Blase |
moveDisabled, resizeDisabled | Dieses Ereignis gegen Verschieben oder Dauer ändern sperren |
moveHDisabled, moveVDisabled | Verschieben nur zwischen Zeilen oder nur in der Zeit erlauben |
clickDisabled, deleteDisabled | Dieses Ereignis von Klicks oder Löschen ausnehmen |
tags | Ein beliebiger Wert für Ihre eigene Verwendung |
Alle Felder dieser Tabelle gibt es nur in Pro, außer backColor, fontColor, cssClass, toolTip und tags, die auch Lite akzeptiert.
IDs sind Strings oder Zahlen und werden strikt verglichen
ResourceId und EventId sind string | number, und Vergleiche berücksichtigen Wert und Typ. Die Zahl 101 und der String '101' sind verschiedene IDs. Ein Ereignis mit resource: '101' wird nicht in einer Zeile mit der ID 101 gezeichnet, und control.events.find('7') findet das Ereignis mit der ID 7 nicht.
Das ist vor allem wichtig, wenn Daten aus mehreren Quellen kommen: Ein Datenbanktreiber liefert vielleicht numerische Zimmer-IDs, während ein Formular oder eine URL Strings liefert. Normalisieren Sie IDs an der Stelle, an der Daten in Ihre Anwendung gelangen, und verwenden Sie pro Art von ID einen einzigen Typ.
Intervalle sind halboffen
Ein Ereignis belegt [start, end): Der Anfangszeitpunkt gehört dazu, der Endzeitpunkt nicht. Drei Folgen:
- Direkt aufeinanderfolgende Ereignisse überlappen sich nicht. Ein Aufenthalt, der um 11:00 Uhr endet, und der nächste, der um 11:00 Uhr im selben Zimmer beginnt, sind vereinbar, auch wenn Überlappungen abgelehnt werden.
- Ein reines Datum als Ende ist der erste freie Tag.
start: '2026-10-02',end: '2026-10-05'umfasst den 2., 3. und 4. Oktober. Soll auch der 5. erscheinen, ist das Ende'2026-10-06'. - Dauern sind einfache Differenzen.
end - startist die Dauer, ohne Korrekturen um „plus einen Tag“.
Ihr Backend sollte dieselbe Regel verwenden. Zwei Intervalle überlappen sich, wenn a.start < b.end && b.start < a.end; eine Abfrage nach dem, was zwischen from und to sichtbar ist, lautet start < to AND end > from.
In Pro schaltet eventEndSpec: 'Date' für tagesbasierte Planungen auf inklusive Enden ohne Uhrzeit um: Ein Ereignis, das auf '2026-10-05' endet, umfasst dann den 5. Die Bibliothek rechnet den Wert intern um und gibt ihn in derselben Konvention zurück. Verwenden Sie pro Planer eine einzige Konvention.
Datumsstrings
start, end und jede Datumsoption akzeptieren ein SuperScheduler.Date oder einen ISO-8601-String:
| Eingabe | Akzeptiert | Gelesen als |
|---|---|---|
'2026-10-02' | ja | Mitternacht zu Beginn des 2. Oktober |
'2026-10-02T14:00:00' | ja | 14:00 Uhr |
'2026-10-02 14:00:00' | ja | 14:00 Uhr (ein Leerzeichen statt T) |
'2026-10-02T14:00:00.250' | ja | Mit Millisekunden |
'2026-10-02T14:00' | nein, wirft einen Fehler | Sekunden sind Pflicht |
'2026-10-02T14:00:00+02:00' | ja, mit Vorsicht | 12:00 Uhr: Ein Offset rechnet den Wert in die UTC-Uhrzeit um |
new Date() | nein | Ein natives Date besteht die Typprüfung als start oder end nicht |
Zwei Regeln verhindern die meisten Überraschungen: Geben Sie immer Sekunden an, und senden Sie keine Offsets oder Z, es sei denn, Sie wollen die UTC-Uhrzeit. Formatieren Sie Werte als yyyy-MM-ddTHH:mm:ss in der Zeitzone des Ortes, der geplant wird.
Bürgerliche Zeit, keine Zeitzonen
SuperScheduler arbeitet mit bürgerlicher Zeit (lokale Datum-Uhrzeit): 2026-10-25T02:30:00 ist „halb drei am 25.“, genau so, wie es dasteht, ohne Zeitzone und ohne Sommerzeitverschiebungen. Der Planer zeigt an, was Sie ihm geben, und gibt Werte in derselben Form zurück.
Genau das braucht eine Planungstafel: Ein Hotel in Madrid zeigt den Check-in für alle Nutzer um 14:00 Uhr Ortszeit an, egal wo deren Browser steht. Es bedeutet aber auch, dass die Umrechnungen Ihre Sache sind:
- Speichert Ihr Backend Zeitpunkte (UTC-Zeitstempel), rechnen Sie sie vor der Übergabe in die Ortszeit des Standorts der Ressource um und beim Speichern zurück in Zeitpunkte.
- Liegen verschiedene Ressourcen in verschiedenen Zeitzonen, entscheiden Sie, welche Ortszeit die Ansicht zeigt; der Planer hat eine einzige Zeitachse.
- Wiederkehrende Ereignisse (jeden Montag um 9:00 Uhr) müssen den Planer als konkrete Vorkommen erreichen, die Ihre Anwendung expandiert hat.
Eigene Felder mit EventData<T>
Ihre Ereignisse tragen meist mehr als eine Beschriftung: einen Gastcode, einen Status, einen Preis. In Pro hat EventData keine Indexsignatur, daher scheitert ein Objektliteral mit zusätzlichen Eigenschaften an der Excess-Property-Prüfung von TypeScript. Deklarieren Sie Ihre Felder einmal und verwenden Sie den generischen Typ SuperScheduler.EventData<YourFields>:
import type { SuperScheduler } from 'super-scheduler'
/** Fields your application adds to every booking. */
export interface BookingFields {
guestCode: string
status: 'tentative' | 'confirmed' | 'checkedIn'
adults: number
}
export type BookingEvent = SuperScheduler.EventData<BookingFields>
// ResourceData accepts extra properties: keep your own row fields next to id and name.
export const rooms: SuperScheduler.ResourceData[] = [
{ id: 101, name: 'Room 101', floor: 1, kind: 'double' },
{ id: 102, name: 'Room 102', floor: 1, kind: 'suite' },
]
// EventData has no index signature: type the array so literals may carry custom fields.
export const bookings: BookingEvent[] = [
{
id: 'bk-1042',
resource: 101, // the same type as the room id: 101 and '101' are different ids
start: '2026-10-02T14:00:00',
end: '2026-10-05T11:00:00',
text: 'Booking 1042',
guestCode: 'G-1042',
status: 'confirmed',
adults: 2,
},
]
const STATUSES: ReadonlySet<string> = new Set(['tentative', 'confirmed', 'checkedIn'])
/**
* Objects that come back from the control (handler arguments, onEventsChange) are typed as plain
* EventData. Narrow them instead of casting, so a malformed object is caught where it appears.
*/
export function isBooking(data: SuperScheduler.EventData): data is BookingEvent {
return (
'guestCode' in data &&
typeof data.guestCode === 'string' &&
'status' in data &&
typeof data.status === 'string' &&
STATUSES.has(data.status) &&
'adults' in data &&
typeof data.adults === 'number'
)
}Daten, die vom Control zurückkommen, sind als einfaches EventData typisiert: Handler-Argumente, onEventsChange, control.events.list. Die Bibliothek behält Ihre Felder, aber TypeScript kann nicht wissen, dass sie da sind. Grenzen Sie mit einem Type Guard wie isBooking ein, statt zu casten; ein Guard fängt auch Objekte ab, die Ihr eigener Code fehlerhaft gebaut hat. Render-Hooks erhalten eine offene Kopie (args.data in onBeforeEventRender), dort können Sie Felder also direkt lesen, aber ein Guard hält die Typen exakt:
import { useMemo } from 'react'
import { SuperScheduler, SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerProps } from 'super-scheduler'
import { bookings, isBooking, rooms } from './typed-data'
export function TypedPlanning({ onOpen }: { onOpen: (guestCode: string) => void }) {
const owned = useMemo(() => bookings.slice(), [])
const config = useMemo<SchedulerProps>(
() => ({
startDate: '2026-10-01',
days: 31,
scale: 'Day',
onBeforeEventRender: (args) => {
// args.data is a per-render copy: start and end are always SuperScheduler.Date here.
if (!isBooking(args.data)) return
const nights = Math.round(
(args.data.end.getTime() - args.data.start.getTime()) / 86_400_000,
)
// html is trusted markup: escape every value that came from users.
const code = SuperScheduler.Util.escapeHtml(args.data.guestCode)
args.data.html = `${code} · ${nights} night${nights === 1 ? '' : 's'}`
args.data.cssClass = `booking booking--${args.data.status}`
},
onEventClick: (args) => {
// args.e is a wrapper: id(), start(), text() are methods; data is the raw object.
const data = args.e.data
if (isBooking(data)) onOpen(data.guestCode)
},
}),
[onOpen],
)
return <SuperSchedulerComponent {...config} resources={rooms} events={owned} />
}Sie sollten jede Buchung mit ihrem Gastcode und der Anzahl der Nächte beschriftet sehen, und beim Klick wird onOpen mit dem Gastcode aufgerufen.
Das EventData von Lite ist nicht generisch und akzeptiert in TypeScript keine zusätzlichen Felder: Legen Sie Anwendungsdaten in tags ab, die onEventClick in e.data.tags zurückgibt.
Werte nach dem Ziehen oder Ändern der Dauer
Wenn ein Nutzer in Pro ein Ereignis verschiebt oder seine Dauer ändert, bearbeitet die Bibliothek Ihr Objekt nicht. Sie ersetzt es durch ein neues Objekt, { ...old, start, end, resource }, in dem start und end Instanzen von SuperScheduler.Date sind. Ereignisse, die niemand angefasst hat, behalten die Strings, die Sie übergeben haben. Code, der Ereignisse liest, muss daher beide Formen akzeptieren:
import { SuperScheduler } from 'super-scheduler'
/**
* After a drag or a resize, the committed event holds SuperScheduler.Date values in start and end;
* events nobody touched keep the strings you passed. Normalize both to one canonical string.
*/
export function toIso(value: SuperScheduler.DateInput): string {
// `value` is `yyyy-MM-ddTHH:mm:ss` (plus `.fff` when milliseconds are not zero).
return typeof value === 'string' ? new SuperScheduler.Date(value).value : value.value
}
/** The part of an event your backend stores. */
export function toSavePayload(event: SuperScheduler.EventData) {
if (event.resource === undefined) throw new Error(`Event ${String(event.id)} has no resource`)
return {
id: String(event.id),
resource: String(event.resource),
start: toIso(event.start),
end: toIso(event.end),
}
}
/** Half-open overlap, the rule the scheduler applies: touching intervals do not overlap. */
export function overlaps(a: SuperScheduler.EventData, b: SuperScheduler.EventData): boolean {
const date = (value: SuperScheduler.DateInput) => new SuperScheduler.Date(value)
return SuperScheduler.Util.overlaps(date(a.start), date(a.end), date(b.start), date(b.end))
}Einige Details helfen, wenn Sie diese Werte speichern oder vergleichen:
JSON.stringifyschreibt einSuperScheduler.Datealsyyyy-MM-ddTHH:mm:ss, ein ganzes Ereignisobjekt wird also zu gültigen ISO-Strings serialisiert.String(date)unddate.valueliefern denselben Text;date.toString('d MMM HH:mm', 'en-us')formatiert mit einem Muster und einer Locale.- Vergleichen Sie Datumsangaben mit
a.equals(b)oder übergetTime().===vergleicht die Objektidentität. getDay()ist der Tag im Monat (1 bis 31), anders als beim nativenDate. Den Wochentag lieferngetDayOfWeek()(0 ist Sonntag) oderdayOfWeekISO()(1 ist Montag).- Prüfen Sie den Typ mit
value instanceof SuperScheduler.Date, nie über den Namen des Konstruktors. Verwenden Sie in Lite das eigeneSuperScheduler.Datevon Lite; sind beide Editionen installiert, tauschen Sie ISO-Strings zwischen ihnen aus.
Planung einer MietwagenflotteEin Kleinwagen fällt am Abholtag aus. Die Miete auf ein anderes Auto legen, die Aufbereitung einhalten und sehen, wo die Flotte knapp wird. Gerätebuchung im LaborWer ein Gerät bucht, bekommt die Kalibrierung mit. Eine Sitzung aus dem Servicefenster holen, dann den eigenen Lauf verlängern.
Nächste Schritte
- Ereignisse im React-State halten und Änderungen speichern: Kontrollierte Ereignisse und Callbacks.
- Stunden und Minuten statt Tagen anzeigen: Stunden, Minuten, Tage und Zoom.
- Die Balken mit Ihren Feldern anpassen: React-Render-Slots und Themes.
Verwandte Beispiele
- FleetlinePlanung einer MietwagenflotteEin Kleinwagen fällt am Abholtag aus. Die Miete auf ein anderes Auto legen, die Aufbereitung einhalten und sehen, wo die Flotte knapp wird.
- BenchlabGerätebuchung im LaborWer ein Gerät bucht, bekommt die Kalibrierung mit. Eine Sitzung aus dem Servicefenster holen, dann den eigenen Lauf verlängern.