Pro-ModuleGilt fürSuperScheduler Pro
Minimap und abgeleitete Kennzahlen
Rendern Sie SchedulerMinimap aus super-scheduler/minimap mit dem Control aus useSchedulerControl(); ohne Optionen zeigt sie, wie viele Ereignisse sich an jedem Tag überschneiden. Für eine geschäftliche Kennzahl wie Auslastung oder Belegung übergeben Sie eine series-Funktion, die Ihre Anwendung pro Bucket berechnet, dazu peak: 'absolute', max: 1 und eine tone-Funktion für Warn- und Gefahrenfarben. Ziehen des Auswahlfensters verschiebt die Zeitleiste, Ziehen seiner Ränder zoomt, und per Tastatur funktioniert das Auswahlfenster als Schieberegler.
Ein Jahr voller Buchungen passt nicht auf den Bildschirm. Die Minimap ist ein schmaler Streifen unter (oder über) dem Planer, der die gesamte Zeitleiste auf einmal zeigt: ein Balken pro Zeitabschnitt (Bucket) und ein Auswahlfenster (Brush), das den sichtbaren Zeitraum markiert. Nutzer sehen, wo die vollen Wochen liegen, und springen direkt dorthin. Die Balken zeigen die Zahl, die Ihre Anwendung festlegt; damit wird der Streifen zu einem kompakten Diagramm für Auslastung, Belegung, Last oder Umsatz.
Die Minimap erfordert SuperScheduler Pro.
Minimap hinzufügen
SchedulerMinimap ist die React-Komponente. Sie braucht das Control-Objekt des Planers, das erst existiert, wenn der Planer gemountet ist; useSchedulerControl() liefert Ihnen control als State (zunächst null), und die Minimap akzeptiert null und wartet.
import { useMemo } from 'react'
import { SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
import { SchedulerMinimap } from 'super-scheduler/minimap'
import 'super-scheduler/styles.css'
export function PlanningWithOverview(props: {
resources: SuperScheduler.ResourceData[]
events: SuperScheduler.EventData[]
}) {
// `control` is null until the scheduler has mounted; the minimap waits for it.
const { controlRef, control } = useSchedulerControl()
const events = useMemo(() => props.events.slice(), [props.events])
return (
<>
<SuperSchedulerComponent
controlRef={controlRef}
startDate="2026-01-01"
days={365}
scale="Day"
cellWidth={40}
resources={props.resources}
events={events}
/>
{/* Without `series`, the strip shows how many events overlap each day. */}
<SchedulerMinimap control={control} height={32} className="planning-minimap" />
</>
)
}Sie sollten einen 32 px hohen Streifen sehen, mit den Anfangsbuchstaben der Monate, einer Markierung für heute, verblassten vergangenen Tagen und einem Auswahlfenster über den sichtbaren Wochen. Ziehen Sie das Auswahlfenster, und der Planer scrollt mit.
Ohne series verwendet der Streifen eventDensity(control): die Zahl der Ereignisse, die sich mit jedem Bucket überschneiden. Gezählt wird in kurzen Hintergrundaufgaben (bis zu 8 ms oder 15.000 Ereignisse pro Aufgabe); das Ergebnis wird zwischengespeichert und nach Abschluss neu gezeichnet, sodass auch ein großer Datenbestand die Seite nie blockiert.
Wie der Streifen entsteht
Die Minimap teilt einen Zeitraum in Buckets und zeichnet einen Wert pro Bucket:
- Zeitraum. Standardmäßig die Zeitleiste des Controls (ab
startDatefürdays); bei unendlichem Scrollen das aktuell erzeugte Fenster.range: { start, end }legt einen anderen Zeitraum fest, zum Beispiel ein ganzes Jahr, während der Planer einen Monat zeigt. - Buckets. Jeweils ein Tag; eine Stunde bei
scale: 'Hour'oder'Minute'; eine Woche, wenn der Zeitraum länger als 730 Tage ist. - Werte. Ihre Serie liefert eine Zahl pro Bucket. Gibt es mehr Buckets als Pixel, werden benachbarte Werte zu einem Balken pro Pixelspalte gemittelt, ausgerichtet an den Gerätepixeln.
- Höhe. Bei
peak: 'relative'(Standard) entspricht der höchste Balken dem größten Wert. Beipeak: 'absolute'werden die Balken anmaxgemessen (Standard 1), sodass ein voller Tag immer voll aussieht.
Eigene Kennzahl einspeisen
series ist entweder ein Float32Array, das den gesamten Zeitraum abdeckt, oder eine Funktion, die den Zeitraum (start, end, buckets, bucketMs) erhält und einen Wert pro Bucket zurückgibt. Die Funktionsform passt sich an, wenn der Nutzer zoomt und sich die Bucket-Größe ändert.
Die Bibliothek weiß nicht, was „ausgelastet“ für Ihr Geschäft bedeutet; die Kennzahl ist deshalb Ihr Code. Diese hier berechnet die Auslastung: den gebuchten Anteil der verfügbaren Zeit, für beliebig viele Ressourcen.
import { SuperScheduler } from 'super-scheduler'
import type { MinimapRange, MinimapSeries } from 'super-scheduler/minimap'
export interface Booking {
/** ISO wall-clock values with seconds; `end` is exclusive. */
readonly start: string
readonly end: string
}
/**
* Booked share of the available time in each bucket: 0 is idle, 1 is every resource busy
* for the whole bucket. The application decides what "capacity" means; here it is the
* number of bookable resources.
*/
export function utilizationSeries(bookings: readonly Booking[], capacity: number): MinimapSeries {
// Parse once; the series function runs again on every redraw.
const spans = bookings.map((booking) => ({
start: new SuperScheduler.Date(booking.start).getTime(),
end: new SuperScheduler.Date(booking.end).getTime(),
}))
return (range: MinimapRange) => {
const values = new Float32Array(range.buckets)
const origin = range.start.getTime()
const available = range.bucketMs * Math.max(1, capacity)
for (const span of spans) {
// Half-open [start, end): a booking ending at midnight does not touch the next day.
const first = Math.max(0, Math.floor((span.start - origin) / range.bucketMs))
const last = Math.min(range.buckets, Math.ceil((span.end - origin) / range.bucketMs))
for (let i = first; i < last; i++) {
const bucketStart = origin + i * range.bucketMs
const overlap =
Math.min(span.end, bucketStart + range.bucketMs) - Math.max(span.start, bucketStart)
if (overlap > 0) values[i] = (values[i] ?? 0) + overlap / available
}
}
return values
}
}Bei zwei Transportern, von denen einer den ganzen Tag und der andere ab Mittag gebucht ist, ergibt der Tag 0,75. Intervalle sind halboffen, wie im Planer: Eine Buchung, die um Mitternacht endet, berührt den nächsten Tag nicht.
import { useMemo } from 'react'
import { SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
import { SchedulerMinimap } from 'super-scheduler/minimap'
import type { MinimapLabels, MinimapTone } from 'super-scheduler/minimap'
import { utilizationSeries } from './utilization-series'
// Module-level: the minimap receives the same functions on every render.
const tone = (value: number): MinimapTone =>
value >= 0.95 ? 'danger' : value >= 0.8 ? 'warn' : 'base'
const LABELS: Partial<MinimapLabels> = {
label: 'Fleet utilization overview',
valueText: (start, end) =>
`Showing ${start.toString('d MMM yyyy')} to ${end.toString('d MMM yyyy')}`,
}
export function FleetPlanning(props: {
vehicles: SuperScheduler.ResourceData[]
bookings: SuperScheduler.EventData[]
}) {
const { controlRef, control } = useSchedulerControl()
const events = useMemo(() => props.bookings.slice(), [props.bookings])
// Recomputed only when the data changes; a new series function makes the strip redraw.
const series = useMemo(
() =>
utilizationSeries(
props.bookings.map((booking) => ({
start: String(booking.start),
end: String(booking.end),
})),
props.vehicles.length,
),
[props.bookings, props.vehicles.length],
)
return (
<>
<SuperSchedulerComponent
controlRef={controlRef}
startDate="2026-01-01"
days={365}
scale="Day"
cellWidth={40}
resources={props.vehicles}
events={events}
/>
<SchedulerMinimap
control={control}
series={series}
// 1 means full, whatever the busiest bucket is.
peak="absolute"
max={1}
tone={tone}
labels={LABELS}
marks={{ today: true, months: true, past: true }}
height={32}
className="fleet-minimap"
/>
</>
)
}Jetzt liest sich der Streifen wie ein Auslastungsdiagramm: ruhige Balken, die mit dem Wert kräftiger werden, Bernsteingelb ab 80 %, Rot ab 95 %, und ein Screenreader hört „Fleet utilization overview, Showing 1 Jan 2026 to 26 Jan 2026“.
Für den häufigen Fall, dass Sie Ereignisse gewichten statt zählen wollen, nimmt eventDensity(control, { weight }) eine Funktion der Ereignisdaten entgegen (Stunden, Einheiten, Gäste). Das imperative Snippet weiter unten nutzt sie.
Farbtöne, Skalierung, Markierungen und Beschriftungen
| Option | Standard | Wirkung |
|---|---|---|
height | 28 | Höhe des Streifens in Pixeln; die Monatsinitialen brauchen 24 oder mehr |
peak | 'relative' | 'absolute' misst die Balken an max |
max | 1 | Der Wert, der einen Balken füllt, bei peak: 'absolute' |
tone(value, index) | überall 'base' | 'base', 'warn' oder 'danger' pro Bucket |
marks | alle true | today (eine Markierung am aktuellen Datum des Browsers), months (Trennlinien und Initialen, im Januar die Jahreszahl), past (frühere Buckets verblasst) |
range | die Zeitleiste | Der Zeitraum, den der Streifen abdeckt |
labels | Englisch oder Spanisch | label (der barrierefreie Name des Auswahlfensters), zoom (Hinweis zum Ändern der Größe) und valueText(start, end) |
Die Standardbeschriftungen sind Englisch, oder Spanisch, wenn die locale des Planers mit es beginnt: „Visible period“, „Drag either edge to zoom, or use + and −“ und der sichtbare Zeitraum als yyyy-MM-dd – yyyy-MM-dd. Für jede andere Sprache, auch Deutsch, geben Sie labels an.
Die Farben kommen aus Tokens, die auf das Theme des Planers zurückfallen. Setzen Sie sie auf dem Container der Minimap oder einem beliebigen Vorfahren:
| Token | Fallback |
|---|---|
--super-scheduler-minimap-base | der Akzent |
--super-scheduler-minimap-warn | #f59e0b |
--super-scheduler-minimap-danger | #ef4444 |
--super-scheduler-minimap-past | gedämpfter Text |
--super-scheduler-minimap-today | der Akzent |
--super-scheduler-minimap-months | die Rahmenfarbe |
--super-scheduler-minimap-label | gedämpfter Text |
--super-scheduler-minimap-brush | der Akzent |
Der Streifen wird neu gezeichnet, wenn sich das Theme ändert: bei einer Änderung von class, data-theme oder data-color-scheme an <html>, an der Wurzel des Planers oder am Container, oder wenn sich das Farbschema des Systems ändert.
Bedienung des Auswahlfensters
| Eingabe | Wirkung |
|---|---|
| Auswahlfenster ziehen | Verschiebt die Zeitleiste |
| Einen der beiden Ränder ziehen | Zoomt: nach außen mehr Zeit, nach innen weniger; der gegenüberliegende Rand bleibt stehen, innerhalb von Minimum und Maximum aus zoomGesture |
| Außerhalb des Auswahlfensters auf den Streifen klicken | Scrollt so, dass dieses Datum in der Mitte steht (animiert, außer wenn reduzierte Bewegung aktiv ist) |
| Pfeil links / rechts, Pfeil unten / oben | Einen Tag früher oder später; mit Umschalt sieben Tage |
| Bild auf / Bild ab | Einen Monat früher oder später |
| Pos1 / Ende | Anfang oder Ende des Zeitraums |
+ oder =, - oder − | Um die Mitte herum hinein- oder herauszoomen |
Das Auswahlfenster ist ein fokussierbarer role="slider" mit aria-valuetext; das Canvas ist vor assistiven Technologien verborgen. Bei cellWidthSpec: 'Auto' sind die Griffe an den Rändern und die Zoomtasten deaktiviert, weil der Planer ohnehin die ganze Zeitleiste einpasst. Zeigerbewegungen werden einmal pro Animationsframe angewendet.
Imperative API und Freigabe
createMinimap(control, container, options) erzeugt den Streifen in einem beliebigen Element und gibt { element, update, refresh, dispose } zurück. Die Funktion wirft einen Fehler, wenn das Control noch nicht initialisiert ist; erzeugen Sie den Streifen in React daher in einem Effect, der von control abhängt:
import { useEffect, useMemo, useRef } from 'react'
import { SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
import { createMinimap, eventDensity } from 'super-scheduler/minimap'
type Order = { quantity: number }
export function ProductionOverview(props: {
lines: SuperScheduler.ResourceData[]
orders: SuperScheduler.EventData<Order>[]
}) {
const { controlRef, control } = useSchedulerControl()
const host = useRef<HTMLDivElement>(null)
const events = useMemo(() => props.orders.slice(), [props.orders])
useEffect(() => {
// createMinimap needs an initialized control: run it after mount, keyed on the control.
if (control === null || host.current === null) return
const minimap = createMinimap(control, host.current, {
height: 28,
// Each order weighs its quantity instead of counting 1.
series: eventDensity(control, {
weight: (e) => (e as SuperScheduler.EventData<Order>).quantity,
}),
labels: { label: 'Production load overview' },
})
// Releases observers and pending work; control.dispose() does it too.
return () => minimap.dispose()
}, [control])
return (
<>
<SuperSchedulerComponent
controlRef={controlRef}
startDate="2026-10-01"
days={90}
scale="Day"
resources={props.lines}
events={events}
/>
<div ref={host} className="production-minimap" />
</>
)
}update(partialOptions)ändert Optionen und zeichnet neu;refresh()fordert die Serie erneut an;dispose()entfernt den Streifen und gibt seine Observer, Listener und ausstehende Arbeit frei. Das Freigeben des Controls erledigt das ebenfalls.
SchedulerMinimap übernimmt all das für Sie: Die Komponente erzeugt den Streifen, sobald control verfügbar ist, erzeugt ihn neu, wenn sich das Control ändert, und gibt ihn beim Unmount frei.
Den Streifen aktuell halten
Die Minimap zeichnet neu und ruft eine series-Funktion erneut auf, wenn:
- sich die Ereignisse des Controls ändern (ein Ziehen, ein API-Aufruf, ein Ladevorgang);
- ein Zoom endet, der Container seine Größe ändert oder das Theme wechselt;
- Sie
refresh()oderupdate()aufrufen.
Solange eine Geste läuft, warten Neuzeichnungen; sie erfolgen, sobald die Geste endet. Eine series-Funktion, die die Ereignisse des Controls selbst liest, ist daher immer aktuell. Eine Serie, die aus den Daten Ihrer Anwendung berechnet wird, ist aktuell, wenn Sie nach jeder Änderung dieser Daten eine neue Funktion übergeben, wie es useMemo im Auslastungs-Snippet tut.
SchedulerMinimap ruft bei jedem Rendern der Elternkomponente update mit seinen Props auf. Memoisieren Sie series, tone und labels (oder definieren Sie sie auf Modulebene), damit ein erneutes Rendern die Serie nicht unnötig neu berechnet.
Was Ihre Anwendung verantwortet
- Die Kennzahl. Was als Kapazität gilt, welche Ereignisse zählen (vorläufige, stornierte, Sperren) und wie sie gewichtet werden.
- Daten, die Sie nicht geladen haben. Die Serie sieht nur, was Ihr Code ihr übergibt. Mit bereichsweisem Laden ist womöglich nur ein Teil des Jahres im Speicher: Für eine Jahresansicht holen Sie Tagesaggregate von Ihrem Backend und übergeben sie zusammen mit einem festen
rangeals Serie. - Schwellenwerte und Formulierungen. Grenzen der Farbtöne, Beschriftungen und ihre Übersetzungen.
Verwandte Themen
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. Planung von FertigungsaufträgenDie Wartung wurde vorgezogen. Verschieben Sie den Auftrag und halten Sie die Arbeitsfolge ein. Liegeplatzplanung im HafenEin Schiff kommt zwölf Stunden zu spät. Verschieben Sie sein Liegefenster und nehmen Sie Schlepper und Kräne mit. Zimmerplanung im HotelIn Zimmer 104 tropft die Dusche. Buchen Sie den nächsten Gast um, sperren Sie das Zimmer für den Installateur und finden Sie die ausgebuchten Nächte.
- Zeitskalen und Zoom für die Zoomgrenzen, die die Ränder des Auswahlfensters einhalten.
- Themes, Tokens, Tailwind und Dark Mode für die Tokens, auf die die Minimap zurückfällt.
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.
- ForgePlanung von FertigungsaufträgenDie Wartung wurde vorgezogen. Verschieben Sie den Auftrag und halten Sie die Arbeitsfolge ein.
- HarborworksLiegeplatzplanung im HafenEin Schiff kommt zwölf Stunden zu spät. Verschieben Sie sein Liegefenster und nehmen Sie Schlepper und Kräne mit.
- Casa NomaZimmerplanung im HotelIn Zimmer 104 tropft die Dusche. Buchen Sie den nächsten Gast um, sperren Sie das Zimmer für den Installateur und finden Sie die ausgebuchten Nächte.