# Minimap und abgeleitete Kennzahlen

> Mit super-scheduler/minimap eine Übersicht der Zeitleiste ergänzen, sie mit Auslastung oder eigenen Kennzahlen speisen, gestalten, beschriften und sauber freigeben.

Source: https://superscheduler.org/de/docs/minimap-metrics/
Reviewed: 2026-10-07

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.

```tsx
// src/PlanningWithOverview.tsx
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 `startDate` für `days`); 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. Bei `peak: 'absolute'` werden die Balken an `max` gemessen (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.

```ts
// src/utilizationSeries.ts
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.

```tsx
// src/FleetPlanning.tsx
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“.

> **Tip:**
> Bei Hotelaufenthalten (Check-in 14:00, Check-out 11:00) erreicht die zeitgewichtete Auslastung auch in einer vollen Nacht nie 1. Zählen Sie stattdessen Nächte: Ein Zimmer ist an einem Tag belegt, wenn ein Aufenthalt die Nacht dieses Tages abdeckt, und der Wert ist die Zahl der belegten Zimmer geteilt durch die Zahl der Zimmer.

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:

```tsx
// src/ProductionOverview.tsx
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()` oder `update()` 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](https://superscheduler.org/de/docs/range-loading/) 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 `range` als Serie.
- **Schwellenwerte und Formulierungen.** Grenzen der Farbtöne, Beschriftungen und ihre Übersetzungen.

## Verwandte Themen
→ https://superscheduler.org/de/examples/fleet-rentals/
→ https://superscheduler.org/de/examples/manufacturing-orders/
→ https://superscheduler.org/de/examples/port-berths/
→ https://superscheduler.org/de/examples/hotel-rooms/
- [Zeitskalen und Zoom](https://superscheduler.org/de/docs/time-scales-zoom/) für die Zoomgrenzen, die die Ränder des Auswahlfensters einhalten.
- [Themes, Tokens, Tailwind und Dark Mode](https://superscheduler.org/de/docs/theming/) für die Tokens, auf die die Minimap zurückfällt.
