# Stunden, Minuten, Tage und Zoom

> Skalen, Zelldauern und Zeitköpfe konfigurieren, Stunden- oder 15-Minuten-Zellen zeigen, Nicht-Geschäftszeit ausblenden und von Monaten bis Minuten zoomen.

Source: https://superscheduler.org/de/docs/time-scales-zoom/
Reviewed: 2026-10-07

Wählen Sie die Zellgröße mit scale ('Hour', 'Day', 'Week', 'Month', 'Year' oder 'CellDuration' mit cellDuration in Minuten), legen Sie mit cellWidth die Pixel pro Zelle und mit days die Länge fest, und beschreiben Sie die Kopfzeilen mit timeHeaders. Nächte und Wochenenden blenden Sie mit businessBeginsHour, businessEndsHour und showNonBusiness={false} aus. Für den Zoom listen Sie zoomLevels auf und wechseln mit control.zoom.setActive, animateTo oder step zwischen ihnen; Pinch-Geste und Strg/Cmd + Mausrad sind standardmäßig aktiv.

Die Zeitachse von SuperScheduler Pro wird durch eine Handvoll Optionen definiert: was eine Zelle darstellt (`scale`), wie breit sie ist (`cellWidth`), wo die Zeitleiste beginnt und wie lang sie ist (`startDate`, `days`) und wie die Kopfzeilen sie beschriften (`timeHeaders`). Zoom ist eine Liste solcher Konfigurationen, `zoomLevels`, die Nutzer per Geste und Ihr Code über `control.zoom` erreichen.

Dieser Leitfaden führt von festen Skalen bis zum stufenlosen Zoom. Lite hat eine feste Tagesachse; alles andere hier erfordert Pro.

## Skala, Zelldauer und Breite
| `scale` | Eine Zelle ist | Typische Verwendung |
|---|---|---|
| `'Minute'` | 1 Minute | Sendeabläufe, Laborläufe |
| `'CellDuration'` | `cellDuration` Minuten (Standard 60) | Slots zu 5, 15 oder 30 Minuten; Schichten zu 240 Minuten |
| `'Hour'` | 1 Stunde | Werkstätten, Besprechungsräume, Teams |
| `'Day'` | 1 Kalendertag | Hotels, Vermietung, Personalplanung |
| `'Week'` | 1 Kalenderwoche, beginnend mit `weekStarts` | Projekte, Kampagnen |
| `'Month'` | 1 Kalendermonat | Lange Einsätze, Kapazitätspläne |
| `'Year'` | 1 Kalenderjahr | Mehrjährige Übersichten |
| `'Manual'` | Die Zellen, die Sie in `timeline` auflisten | Unregelmäßige Zeiträume |

`cellWidth` ist in Pixeln **pro Zelle der aktuellen Skala** angegeben (Standard 40): 44 bedeutet auf einer Tagesachse 44 Pixel pro Tag, auf einer Stundenachse aber 44 Pixel pro Stunde. `startDate` (Standard heute, auf Mitternacht gekürzt) und `days` legen die Länge der Zeitleiste fest.

> **Behavior:**
> Die Standardwerte sind `scale: 'CellDuration'` mit `cellDuration: 60` und `days: 1`: Eine Komponente, die nur `resources` und `events` erhält, zeigt einen einzigen Tag in Stundenzellen. Setzen Sie `scale` und `days` immer explizit.

Einige typische Konfigurationen:

```ts
// src/scales.ts
import type { SchedulerProps } from 'super-scheduler'

// A month of day cells: the classic booking chart.
export const monthOfDays = {
  scale: 'Day',
  startDate: '2026-10-01',
  days: 31,
  cellWidth: 44,
  timeHeaders: [{ groupBy: 'Month' }, { groupBy: 'Day', format: 'ddd d' }],
} satisfies SchedulerProps

// One working day in 15-minute cells; nights are removed from the axis.
export const quarterHours = {
  scale: 'CellDuration',
  cellDuration: 15,
  startDate: '2026-10-12',
  days: 1,
  cellWidth: 36,
  businessBeginsHour: 7,
  businessEndsHour: 19,
  showNonBusiness: false,
  timeHeaders: [
    { groupBy: 'Hour', format: 'HH:mm' },
    { groupBy: 'Cell', format: 'mm' },
  ],
} satisfies SchedulerProps

// A work week of hours, Monday to Friday, with 12-hour labels.
export const workWeekOfHours = {
  scale: 'Hour',
  startDate: '2026-10-12',
  days: 5,
  cellWidth: 48,
  timeFormat: 'Clock12Hours',
  businessBeginsHour: 8,
  businessEndsHour: 18,
  showNonBusiness: false,
  timeHeaders: [{ groupBy: 'Day', format: 'dddd d MMMM' }, { groupBy: 'Hour' }],
} satisfies SchedulerProps

// A year in month cells, for long-running assignments.
export const yearOfMonths = {
  scale: 'Month',
  startDate: '2026-01-01',
  days: 365,
  cellWidth: 90,
  timeHeaders: [{ groupBy: 'Year' }, { groupBy: 'Month', format: 'MMM' }],
} satisfies SchedulerProps
```
`cellDuration` legt auch das Standard-Einrasten fest: Bei 15-Minuten-Zellen rasten Verschiebungen, Dauer-Änderungen und Auswahlen auf Viertelstunden ein. Die Optionsfamilie `snapToGrid` schaltet das Einrasten pro Geste ab. Auf einer Tagesachse werden Ereignisse standardmäßig als ganze Zellen gezeichnet (`useEventBoxes: 'Always'`); setzen Sie `useEventBoxes="Never"`, um sie zu ihren exakten Zeiten zu zeichnen, sodass ein Aufenthalt von 14:00 bis 11:00 Uhr innerhalb seiner Tageszellen beginnt und endet.

## Zeitköpfe
`timeHeaders` listet die Kopfzeilen von oben nach unten. Jede Zeile gruppiert die Zeit nach einer Einheit und kann ein Beschriftungsformat (`format`) und eine Höhe (`height`) festlegen:

| `groupBy` | Gruppiert nach |
|---|---|
| `'Year'`, `'Quarter'`, `'Month'`, `'Week'`, `'Day'`, `'Hour'`, `'Minute'` | Dieser Kalendereinheit |
| `'Cell'` | Eine Beschriftung pro Zelle |
| `'Default'` | `cellGroupBy` (Standard `'Day'`) |
| `'None'` | Eine Beschriftung für die ganze Zeile |

Der Standard ist `[{ groupBy: 'Default' }, { groupBy: 'Cell' }]`: Tage über Zellen. Jede Kopfzeile ist `headerHeight` Pixel hoch (Standard 30), sofern sie keine eigene `height` festlegt.

### Format-Tokens
Formate verwenden diese Tokens; jedes andere Zeichen wird unverändert ausgegeben. Die Beispiele formatieren `2026-10-05T14:30:00` mit der Locale `en-us`.

| Token | Ausgabe | Token | Ausgabe |
|---|---|---|---|
| `yyyy` | 2026 | `HH` | 14 |
| `yy` | 26 | `H` | 14 |
| `MMMM` | October | `hh` | 02 |
| `MMM` | Oct | `h` | 2 |
| `MM` | 10 | `mm` | 30 |
| `M` | 10 | `m` | 30 |
| `dddd` | Monday | `ss`, `s` | 00, 0 |
| `ddd` | Mo | `tt` | PM |
| `dd`, `d` | 05, 5 | `%d` | 5 |

Namen folgen der `locale` des Planers (Standard `'en-us'`): `'dddd d MMMM'` ergibt mit `locale="es-es"` „lunes 5 octubre“. In mehreren Locales ist `ddd` eine Abkürzung aus einem oder zwei Buchstaben („Mo“, „L“); verwenden Sie `dddd` oder schreiben Sie Ihre eigene Beschriftung in `onBeforeTimeHeaderRender`, wenn Sie drei Buchstaben wollen. Beschriftungen, Styles, Tooltips und Areas der Köpfe lassen sich alle in diesem Hook anpassen.

### 12- oder 24-Stunden-Beschriftungen
`timeFormat` steuert die Standardbeschriftung der Stunden: `'Auto'` (Standard) folgt der Locale (12 Stunden für `en-us`, 24 Stunden für die meisten europäischen Locales), `'Clock12Hours'` und `'Clock24Hours'` erzwingen eines davon. Ein explizites `format` an einer Kopfzeile hat immer Vorrang: `'h:mm tt'` für 12-Stunden-Beschriftungen, `'HH:mm'` für 24-Stunden-Beschriftungen. Ein anderes Uhrzeitformat ändert nur die Beschriftungen; die Zeiten der Ereignisse verschieben sich nie.

## Geschäftszeiten und ausgeblendete Zeit
Die Geschäftszeit wird durch `businessBeginsHour` (Standard 9), `businessEndsHour` (Standard 18; `0` bedeutet Mitternacht am Ende des Tages) und `businessWeekends` (Standard `false`) definiert. Mit `showNonBusiness` auf dem Standardwert `true` werden Zellen der Nicht-Geschäftszeit schattiert. Mit `showNonBusiness={false}` werden sie aus der Achse entfernt:

- Auf einer Tagesachse verschwinden die Wochenendtage (aus 14 Tagen werden 10 Spalten).
- Auf einer Achse innerhalb des Tages verschwinden die Stunden außerhalb der Geschäftszeit; eine Arbeitswoche in Stunden zeigt dann pro Tag nur 08:00 bis 18:00 Uhr.

Für alles Speziellere wird `onIncludeTimeCell` beim Aufbau der Zeitleiste für jede Kandidatenzelle aufgerufen: Setzen Sie `args.cell.visible = false`, um eine Zelle wegzulassen, oder `args.cell.width`, um ihre Breite zu ändern. `scale: 'Manual'` mit einem `timeline`-Array aus `{ start, end, width }`-Zellen gibt Ihnen die volle Kontrolle.

> **Limitation:**
> Das Ausblenden von Zeit ändert nur die Achse. Es verschiebt, kürzt oder validiert keine Ereignisse, die in ausgeblendete Zeiträume fallen; wenn Nutzer dort nicht planen dürfen, lehnen Sie diese Positionen in Ihren Regeln ab oder [sperren Sie die Zellen](https://superscheduler.org/de/docs/drag-resize-rules/#disabled-cells), statt sie auszublenden.

## Zoomstufen
Eine Zoomstufe ist ein benannter Satz von Optionen, die gemeinsam angewendet werden: typischerweise `scale`, `cellDuration`, `cellWidth` und `timeHeaders`. Definieren Sie die Stufenleiter einmal, auf Modulebene:

```ts
// src/zoom-levels.ts
import type { SuperScheduler } from 'super-scheduler'

/**
 * From the most detailed view to the widest. Each level is a set of options applied together;
 * cellWidth is in px per cell of that level (per 15 minutes, per hour, per day, per week).
 */
export const ZOOM_LEVELS: SuperScheduler.ZoomLevel[] = [
  {
    id: 'quarter-hours',
    properties: {
      scale: 'CellDuration',
      cellDuration: 15,
      cellWidth: 40,
      timeHeaders: [
        { groupBy: 'Day', format: 'dddd d MMMM' },
        { groupBy: 'Cell', format: 'HH:mm' },
      ],
    },
  },
  {
    id: 'hours',
    properties: {
      scale: 'Hour',
      cellWidth: 56,
      timeHeaders: [
        { groupBy: 'Day', format: 'dddd d MMMM' },
        { groupBy: 'Hour', format: 'HH:mm' },
      ],
    },
  },
  {
    id: 'days',
    properties: {
      scale: 'Day',
      cellWidth: 80,
      timeHeaders: [{ groupBy: 'Month' }, { groupBy: 'Day', format: 'ddd d' }],
    },
  },
  {
    id: 'weeks',
    properties: {
      scale: 'Week',
      cellWidth: 120,
      timeHeaders: [{ groupBy: 'Month' }, { groupBy: 'Week' }],
    },
  },
]

export type ZoomLevelId = 'quarter-hours' | 'hours' | 'days' | 'weeks'
export const ZOOM_LEVEL_IDS: readonly ZoomLevelId[] = ['quarter-hours', 'hours', 'days', 'weeks']
```
Übergeben Sie sie als `zoomLevels` und wählen Sie die Anfangsstufe mit `zoom` (ein Index oder eine `id`). `zoomPosition` (standardmäßig `'left'`, sonst `'middle'`, `'right'`) bestimmt, welcher Teil des Viewports beim Wechsel der Stufe an seinem Platz bleibt.

Eine Eigenschaft kann auch eine Funktion des Ankerdatums sein, `({ date, level }) => value`, zum Beispiel um das Jahr im Monatskopf nur rund um den Jahreswechsel anzuzeigen.

Während einer stufenlosen Geste wählt der Planer die Stufe, die der aktuellen Zeit pro Pixel am nächsten liegt, und wendet ihre Achse und ihre Köpfe an, sobald der Nutzer in sie hineinzoomt. Eigenschaften, die das Zeitfenster zurücksetzen würden, etwa `days` und `startDate`, gelten nur, wenn Ihr Code eine Stufe explizit auswählt. Für Gesten spielt die Reihenfolge des Arrays keine Rolle, denn sie messen jede Stufe.

## Den Zoom aus Code ändern
`control.zoom` hat drei Methoden und eine Eigenschaft:

| Member | Was es tut |
|---|---|
| `setActive(level, position?, anchorDate?)` | Wendet eine Stufe (Index oder ID) sofort an, einschließlich `days` und `startDate` |
| `animateTo(target, options?)` | Animiert zu `{ level }` oder zu einer freien `{ cellWidth }`; gibt ein Promise zurück, das sich erfüllt, wenn die Animation zur Ruhe kommt |
| `step(delta, options?)` | Bewegt sich um `delta` Positionen durch `zoomLevels`, in der Reihenfolge des Arrays und begrenzt auf dessen Enden; ohne `zoomLevels` multipliziert es die Zellbreite pro Schritt mit 1,6 |
| `active` | Index der aktiven Stufe, `-1`, bevor eine Stufe angewendet wurde |

`animateTo` und `step` akzeptieren `{ duration, position, anchorDate }`: `duration` in Millisekunden (Standard 300, `0` für keine Animation) und `anchorDate` als Datum, `'center'` oder `'today'`, um diesen Moment an seinem Platz zu halten. Animationen erfolgen sofort, wenn der Nutzer reduzierte Bewegung bevorzugt, und bewirken nichts, während eine Zoom-Geste läuft. Eine unbekannte Stufen-ID wirft einen Fehler.

```tsx
// src/ZoomablePlanning.tsx
import { useCallback, useMemo, useState } from 'react'
import { SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
import { ZOOM_LEVELS, ZOOM_LEVEL_IDS, type ZoomLevelId } from './zoom-levels'

// Stable objects: a new one per render would be re-applied on every render.
const ZOOM_GESTURE: SuperScheduler.ZoomGestureOptions = {
  // The default maximum (400 px per cell) is too narrow to cross from days into hours.
  max: 1024,
}

interface Props {
  readonly rooms: SuperScheduler.ResourceData[]
  readonly bookings: SuperScheduler.EventData[]
}

export function ZoomablePlanning({ rooms, bookings }: Props) {
  const { controlRef, control } = useSchedulerControl()
  const [level, setLevel] = useState<ZoomLevelId>('days')
  const owned = useMemo(() => bookings.slice(), [bookings])

  // One React update when a gesture or an animation settles, never one per frame.
  const onZoom = useCallback((args: SuperScheduler.ZoomArgs) => {
    if (args.phase !== 'end') return
    const id = ZOOM_LEVEL_IDS[args.level]
    if (id !== undefined) setLevel(id)
  }, [])

  const show = (id: ZoomLevelId) =>
    void control?.zoom.animateTo({ level: id }, { anchorDate: 'center' })
  // step() walks the zoomLevels array in its order: here -1 is more detail, +1 a wider view.
  const zoomIn = () => void control?.zoom.step(-1)
  const zoomOut = () => void control?.zoom.step(1)

  return (
    <>
      <div role="toolbar" aria-label="Zoom">
        {ZOOM_LEVEL_IDS.map((id) => (
          <button key={id} type="button" aria-pressed={level === id} onClick={() => show(id)}>
            {id}
          </button>
        ))}
        <button type="button" aria-label="Zoom in" onClick={zoomIn}>
          +
        </button>
        <button type="button" aria-label="Zoom out" onClick={zoomOut}>
          −
        </button>
      </div>
      <SuperSchedulerComponent
        controlRef={controlRef}
        startDate="2026-10-12"
        days={14}
        zoomLevels={ZOOM_LEVELS}
        zoom="days"
        zoomPosition="middle"
        zoomGesture={ZOOM_GESTURE}
        onZoom={onZoom}
        resources={rooms}
        events={owned}
      />
    </>
  )
}
```
Sie sollten über der Planung vier Stufen-Buttons sowie Plus- und Minus-Buttons sehen. Ein Druck auf „hours“ animiert die Achse von Tagen zu Stunden um die Mitte der Ansicht, und der gedrückte Zustand folgt auch Pinch-Gesten, weil `onZoom` am Ende jedes Zoomvorgangs die Stufe meldet.

`onZoom` erhält `phase` (`'start'`, `'change'`, `'end'`), `origin` (`'gesture'` oder `'api'`), `level`, `cellWidth`, `scale`, `cellDuration`, das Ankerdatum, den Beginn des Viewports und die Detailstufe. Gesten und `animateTo` melden jeden Frame; reagieren Sie auf `phase === 'end'`, es sei denn, Sie aktualisieren pro Frame direkt das DOM. Den React-State aktualisieren Sie nie pro Frame.

## Gesten
Zoom-Gesten sind standardmäßig aktiv: Strg oder Cmd mit dem Mausrad (was in Chrome, Edge und Firefox auch die Pinch-Geste auf dem Trackpad abdeckt), die Pinch-Geste auf dem Trackpad in Safari und das Zwei-Finger-Pinch auf Touchscreens. Der Zoom ist stufenlos und unter dem Zeiger verankert. Feinjustieren Sie ihn mit `zoomGesture`:

| Option | Standard | Bedeutung |
|---|---|---|
| `min` | `cellWidthMin` (mindestens 1) | Kleinste Zellbreite in Pixeln |
| `max` | `400` | Größte Zellbreite in Pixeln |
| `wheel` | `'ctrl'` | `'always'` zoomt bei jeder vertikalen Mausradbewegung (Umschalt + Mausrad scrollt); `false` zoomt nie mit dem Mausrad |
| `pinch` | `true` | Pinch-Geste auf dem Trackpad in Safari und auf Touchscreens |
| `sensitivity` | `1` | Geschwindigkeitsfaktor |
| `scales` | `'zoomLevels'` | Wechselt zwischen Ihren Stufen; `'auto'` verwendet eine Leiter aus Stunde, Tag, Woche, Monat; `false` behält die aktuelle Skala |
| `link` | keiner | Planer mit derselben Link-ID zoomen gemeinsam |

`zoomGesture={false}` entfernt alle Gesten-Listener. Weil `cellWidth` pro Zelle gilt, braucht der Übergang von einer Tagesstufe zu einer Stundenstufe Platz: Ein Tag mit 400 Pixeln sind nur etwa 17 Pixel pro Stunde. Erhöhen Sie also `max` (das Beispiel verwendet 1024), wenn Ihre Leiter von Tagen bis in Stunden reicht.

`keyboardOptions={{ zoomKeys: true }}` ergänzt zusammen mit `keyboardEnabled` Strg/Cmd mit `=` oder `+` (`step(1)`), `-` (`step(-1)`) und `0` (zurück zur Anfangsstufe), solange der Fokus im Planer liegt.

> **Behavior:**
> `step()` und die Zoom-Tasten folgen der Reihenfolge Ihres `zoomLevels`-Arrays. Bei einer Leiter, die wie oben von der detailliertesten zur weitesten Ansicht geordnet ist, wechseln `step(1)` und Strg/Cmd + `+` zu einer weiteren Ansicht. Wenn Sie `zoomKeys` mit `zoomLevels` aktivieren, ordnen Sie die Stufen von der weitesten zur detailliertesten Ansicht, damit `+` hineinzoomt.

## Zoom-Widgets
`super-scheduler/zoom-ui` bietet drei optionale DOM-Widgets, die sich ohne React-Renderings aktualisieren:

- **`createZoomHud(control, options)`**: eine Anzeige im Raster, sichtbar während Gesten und 700 ms lang nach einem Zoom über die API; `format` legt ihren Text fest.
- **`createZoomSlider(control, container, options)`**: ein natives Range-Input mit Tastaturunterstützung, logarithmischer oder linearer Skala und Rastpunkten an Ihren Zoomstufen oder an expliziten Breiten. Sein Wert ist in Pixeln pro Zelle angegeben; am besten passt er daher zu einer Achse mit einer einzigen Skala.
- **`createLodBadge(control, container, labels)`**: zeigt an, ob sich die Ansicht im Detail-, Kompakt- oder Übersichtsmodus befindet.

```tsx
// src/PlanningWithZoomWidgets.tsx
import { useEffect, useRef } from 'react'
import { SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
import { createLodBadge, createZoomHud, createZoomSlider } from 'super-scheduler/zoom-ui'

export function PlanningWithZoomWidgets({ rooms }: { rooms: SuperScheduler.ResourceData[] }) {
  const { controlRef, control } = useSchedulerControl()
  const toolbar = useRef<HTMLDivElement>(null)

  // Widgets need a mounted control. The cleanup matters: Strict Mode mounts twice in development.
  useEffect(() => {
    const host = toolbar.current
    if (control === null || host === null) return
    // A pill inside the grid while zooming (no container needed).
    const hud = createZoomHud(control, {
      format: ({ cellWidth }) => `${Math.round(cellWidth)} px per day`,
    })
    // A native range input; its value is px per cell, so it suits a single-scale axis like this one.
    const slider = createZoomSlider(control, host, {
      min: 4,
      max: 160,
      scale: 'log',
      label: 'Day width',
    })
    // Detail, Compact or Overview, following the level of detail.
    const badge = createLodBadge(control, host)
    return () => {
      hud.dispose()
      slider.dispose()
      badge.dispose()
    }
  }, [control])

  return (
    <>
      <div ref={toolbar} role="toolbar" aria-label="Zoom" />
      <SuperSchedulerComponent
        controlRef={controlRef}
        startDate="2026-10-01"
        days={120}
        scale="Day"
        cellWidth={44}
        resources={rooms}
      />
    </>
  )
}
```
Jedes Widget hat `element` und `dispose()`. Erzeugen Sie sie in einem Effect, der vom Control abhängt, und geben Sie sie in dessen Cleanup frei; das Freigeben des Controls entfernt sie ebenfalls.

## Detailstufe
Wenn Nutzer weit herauszoomen, wäre es unlesbar und langsam, jede Beschriftung in voller Größe zu zeichnen. Die Detailstufe (`lod`, standardmäßig aktiv) passt das Rendering an den Platz auf dem Bildschirm an und ändert ab 40 Pixeln pro Tag nichts:

- **Ereignisse** passen sich unter 40 Pixeln pro Tag ihrer eigenen Breite an: voller Inhalt ab 80 Pixeln, eine Textzeile ab 66 Pixeln, darunter ein einfacher Block. Unter 8 Pixeln pro Tag werden Blöcke zu vollflächigen Füllungen und schmale Ereignisse zu dünnen Balken.
- **Zellen** zeigen ihren Inhalt (HTML, Text, Areas) ab 24 Pixeln pro Zelle. Unter 2 Pixeln pro Zelle gibt es überhaupt keine Zellelemente, und `onBeforeCellRender` wird nicht aufgerufen.
- **Rasterlinien** halten mindestens 8 Pixel Abstand, die Schattierung von Wochenenden und Nicht-Geschäftszeit braucht 6 Pixel pro Tag, und Beschriftungen der Köpfe werden kürzer oder gröber, wenn sie nicht mehr passen.

Jeder Schwellenwert lässt sich über `lod={{ ... }}` ändern (`zoomedOut`, `eventFull`, `eventText`, `eventSolid`, `cellContent`, `cellBackground`, `gridLines`, `shading`, `dayLabel`, `dayNumber`, `weekLabel`, `hysteresis`), und `lod={false}` rendert auf jeder Zoomstufe wörtlich. Der aktuelle Zustand steht in `control.levelOfDetail` (`level` ist `'full'`, `'compact'` oder `'overview'`) und wird als `data-lod`-Attribute an die Wurzel geschrieben, für Ihr CSS.

→ https://superscheduler.org/de/examples/clinic-appointments/
→ https://superscheduler.org/de/examples/festival-stages/
→ https://superscheduler.org/de/examples/fleet-rentals/
## Nächste Schritte
- Auf einer langen Zeitleiste zeigen, wo der Viewport steht: [Minimap und Kennzahlen](https://superscheduler.org/de/docs/minimap-metrics/).
- Zoom und Scrollposition pro Nutzer speichern: [Bereiche und gespeicherte Ansichten](https://superscheduler.org/de/docs/panes-saved-views/).
- Scrollen und Zoomen auch bei großen Datenmengen schnell halten: [Performance und Virtualisierung](https://superscheduler.org/de/docs/performance-virtualization/).
