Pro-ModuleGilt fürSuperScheduler Pro
Abhängigkeiten und Verknüpfungen zwischen Ereignissen
Übergeben Sie links als Array von Objekten { id, from, to, type }, wobei from und to Ereignis-IDs sind; FinishToStart ist der Standardtyp, und gekrümmte Linien sind die Standarddarstellung. Gestalten Sie Verknüpfungen mit color, width, style und cssClass oder den Tokens --super-scheduler-link, und behandeln Sie Klicks mit onLinkClick. Die Bibliothek zeichnet Verknüpfungen, setzt sie aber nicht durch: Prüfen Sie Abhängigkeiten in onEventMoving und onEventMove. Verknüpfungen durch Ziehen zwischen Ereignissen anzulegen ist reserviert und in 0.1.0 nicht implementiert.
Fertigungsschritte, Drehs und Kampagnen hängen voneinander ab: Der Zuschnitt beginnt, wenn das Design fertig ist, zwei Teams starten gemeinsam. SuperScheduler zeichnet diese Abhängigkeiten als Verknüpfungen zwischen Ereignissen und hält sie verbunden, während Nutzer scrollen, zoomen und ziehen. Was eine Abhängigkeit für die Planung bedeutet (ob eine Verschiebung erlaubt ist, ob Nachfolger mitwandern), bleibt in Ihrem Code, dort, wo Ihre Regeln leben.
Verknüpfungen erfordern SuperScheduler Pro.
Verknüpfungen aus Daten zeichnen
Eine Verknüpfung ist ein einfaches Objekt in der Prop links. from und to sind Ereignis-IDs (die id von EventData); id identifiziert die Verknüpfung selbst.
import type { SuperScheduler } from 'super-scheduler'
// `from` and `to` are event ids. Links between ids that are not loaded are not drawn.
export const LINKS: SuperScheduler.LinkData[] = [
// Cutting can start once design has finished (the default type).
{ id: 'design-cut', from: 'design', to: 'cutting', type: 'FinishToStart' },
// Painting and drying start together.
{ id: 'paint-dry', from: 'painting', to: 'drying', type: 'StartToStart', style: 'dashed' },
// Inspection and packing finish together; drawn below the events, in a fixed color.
{
id: 'qa-pack',
from: 'inspection',
to: 'packing',
type: 'FinishToFinish',
color: '#7c3aed',
layer: 'Below',
},
// A heavier line, styled further by CSS through its class.
{ id: 'ship', from: 'packing', to: 'shipping', width: 3, cssClass: 'link--critical' },
]
export const LINK_OPTIONS = {
linkShape: 'Curved', // default; 'RightAngled' draws segments
linkBottomMargin: 12, // clearance for links that run below events (8 px minimum for curves)
linkLayer: 'Above', // default layer of links without their own `layer`
links: LINKS,
} satisfies SuperScheduler.SchedulerConfigSie sollten vier Linien sehen: eine durchgezogene Kurve vom Design zum Zuschnitt, eine gestrichelte zwischen Lackieren und Trocknen, eine violette unter den Ereignissen Prüfung und Verpackung und eine dickere in den Versand.
Verknüpfungen kommen aus Ihrem Datenmodell: einer Spalte dependsOn, einer Tabelle mit Abhängigkeiten oder einer Regel wie „gleicher Auftrag, nächster Schritt“. Leiten Sie das Array mit useMemo ab, damit sich seine Identität nur ändert, wenn sich die Daten ändern. Im Code bietet control.links die Methoden list, add(link), update(link), remove(link or id), find(id), findByFromTo(from, to) und findAllByFromTo(from, to); mit React-State aktualisieren Sie besser Ihr Array.
Verknüpfungstypen
type | Gezeichnet ab | Bis | Typische Bedeutung |
|---|---|---|---|
'FinishToStart' (Standard) | dem Ende von from | zum Anfang von to | to beginnt, nachdem from beendet ist |
'StartToStart' | dem Anfang von from | zum Anfang von to | beide beginnen gemeinsam, oder to nach dem Beginn von from |
'FinishToFinish' | dem Ende von from | zum Ende von to | beide enden gemeinsam |
'StartToFinish' | dem Anfang von from | zum Ende von to | to endet, wenn from beginnt |
Der Typ ändert, wo die Linie ansetzt und wie sie gezeichnet wird. Er ändert nicht, was Nutzer tun dürfen; siehe Abhängigkeiten durchsetzen.
Form und Linienführung
| Option | Standard | Wirkung |
|---|---|---|
linkShape | 'Curved' | Weiche Kurven mit einem kleinen Ring am Ursprung und einer Pfeilspitze. 'RightAngled' zeichnet gerade Segmente. |
linkBottomMargin | 17 | Abstand für Verknüpfungen, die unter Ereignissen verlaufen (bei Kurven mindestens 8 px) |
linkLayer | 'Above' | Standardebene für Verknüpfungen ohne eigenes layer: über oder unter den Ereignissen |
linkWidth | 'Auto' | Standardstrichstärke in Pixeln (Kurven haben 2 px) |
Verknüpfungen zwischen Enden auf derselben Seite wölben sich mit begrenzter Ausladung nach außen, sodass Verknüpfungen zu weit entfernten Zeilen keine übergroßen Schleifen bilden. Rückwärtsgerichtete Verknüpfungen nutzen den Zwischenraum zwischen den Zeilen und verlaufen unter beiden Ereignissen, wenn sich diese Zeilen überlappen. Die Linienführung weicht den beiden verknüpften Ereignissen aus, nicht jedem Ereignis dazwischen: Es handelt sich nicht um ein Routing mit Hindernisvermeidung.
Verknüpfungen gestalten
Pro Verknüpfung ändern color, width, style ('solid', 'dashed', 'dotted'), layer und cssClass eine einzelne Verknüpfung; ein explizites width hat Vorrang vor dem CSS-Token für die Breite. Für alles Übrige verwenden Sie Tokens auf einem beliebigen Vorfahren oder auf einer Verknüpfungsklasse:
.planning {
--super-scheduler-link: #8b5cf6;
--super-scheduler-link-hover: #6d28d9;
--super-scheduler-link-width: 2;
}
.dark .planning {
--super-scheduler-link: #b89afa;
--super-scheduler-link-hover: #d8c6ff;
}
/* A class from your data or from onBeforeLinkRender */
.link--late {
--super-scheduler-link: var(--super-scheduler-danger);
}Verknüpfungen sind SVG-Elemente mit der Klasse super-scheduler__link und data-super-scheduler-part="link"; Pfeil und Ursprungsring übernehmen die Farbe der Verknüpfung.
Um abhängig vom Zustand der verknüpften Ereignisse zu gestalten, verwenden Sie onBeforeLinkRender. Der Hook erhält eine Kopie der Verknüpfung als args.data sowie die beiden Ereignisse als args.from und args.to. Ändern Sie color, width, style, cssClass, layer oder type an der Kopie; die Enden lassen sich nicht ändern. Der Hook läuft einmal pro gezeichneter Verknüpfung und erneut nur dann, wenn sich das Aussehen eines ihrer Ereignisse ändert.
Wann Verknüpfungen gezeichnet werden
Eine Verknüpfung wird gezeichnet, wenn beide Ereignisse geladen sind und im selben Abschnitt des Rasters liegen (in den scrollenden Zeilen oder in denselben fixierten Zeilen oben oder unten). Sie wird nicht gezeichnet, wenn:
- eine der beiden IDs nicht geladen ist, zum Beispiel außerhalb des abgerufenen Zeitraums;
- ein Ende durch einen Filter ausgeblendet ist oder in einer zugeklappten Baumzeile liegt;
- die Zoomstufe ein Ende als schmalen Übersichtsbalken zeichnet;
- sie vollständig außerhalb des gerenderten Bereichs liegt (sie erscheint wieder, sobald sie ins Bild gescrollt wird).
Während ein Ereignis gezogen wird, folgen seine Verknüpfungen der Vorschau mit derselben Linienführung.
Klick, Hover und Menüs
onLinkClickläuft bei einem Klick, mitargs.link(einSuperScheduler.Link:data,id(),from(),to(),type()) undargs.originalEvent;args.preventDefault()überspringtonLinkClicked, das danach läuft.linkBubblezeigt beim Überfahren mit dem Zeiger dasbubbleHtmleiner Verknüpfung.contextMenuLinkoder das eigenecontextMenueiner Verknüpfung öffnet sich per Rechtsklick.
Verknüpfungen nehmen Zeigereingaben nur an, wenn eine dieser Optionen konfiguriert ist; standardmäßig kommen sie dem Ziehen von Ereignissen also nie in die Quere. Ihre unsichtbare Trefferfläche ist breiter als die sichtbare Linie.
Abhängigkeiten in Ihrer Anwendung durchsetzen
Verknüpfungen sind Zeichnungen. Nichts hindert einen Nutzer daran, den Zuschnitt vor das Ende des Designs zu verschieben, solange Ihr Code das nicht prüft. Schreiben Sie die Regel einmal als einfache Funktion:
import { SuperScheduler } from 'super-scheduler'
export interface Bounds {
/** Ticks of the civil wall clock, as SuperScheduler.Date.getTime() returns them. */
readonly start: number
/** Exclusive end. */
readonly end: number
}
export const boundsOf = (event: SuperScheduler.EventData): Bounds => ({
start: new SuperScheduler.Date(event.start).getTime(),
end: new SuperScheduler.Date(event.end).getTime(),
})
/**
* Finish-to-start rule: a task may not start before each of its predecessors ends.
* Returns the first link that `proposed` times for `taskId` would break, or null.
* Other link types need rules of their own; links only draw what the data says.
*/
export function brokenFinishToStart(
taskId: SuperScheduler.EventId,
proposed: Bounds,
links: readonly SuperScheduler.LinkData[],
find: (id: SuperScheduler.EventId) => Bounds | null,
): SuperScheduler.LinkData | null {
for (const link of links) {
if ((link.type ?? 'FinishToStart') !== 'FinishToStart') continue
if (link.to === taskId) {
const predecessor = find(link.from)
if (predecessor !== null && proposed.start < predecessor.end) return link
} else if (link.from === taskId) {
const successor = find(link.to)
if (successor !== null && successor.start < proposed.end) return link
}
}
return null
}
/** Ids of the links the current data already breaks, to draw them differently. */
export function lateLinks(
links: readonly SuperScheduler.LinkData[],
events: readonly SuperScheduler.EventData[],
): ReadonlySet<SuperScheduler.LinkData> {
const byId = new Map(events.map((event) => [event.id, boundsOf(event)]))
const late = new Set<SuperScheduler.LinkData>()
for (const link of links) {
const from = byId.get(link.from)
const to = byId.get(link.to)
if ((link.type ?? 'FinishToStart') === 'FinishToStart' && from && to && to.start < from.end)
late.add(link)
}
return late
}Rufen Sie sie dann aus den Hooks auf, die während und vor einer Änderung laufen:
import { useCallback, useMemo, useState } from 'react'
import { SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerEventsChangeArgs, SchedulerProps, SuperScheduler } from 'super-scheduler'
import { boundsOf, brokenFinishToStart, lateLinks } from './finish-to-start'
import type { Bounds } from './finish-to-start'
export function ProductionPlan(props: {
resources: SuperScheduler.ResourceData[]
initialTasks: SuperScheduler.EventData[]
/** Dependencies come from your data: the library draws them, it does not invent them. */
initialLinks: SuperScheduler.LinkData[]
}) {
const [tasks, setTasks] = useState(props.initialTasks)
const [links, setLinks] = useState(props.initialLinks)
const owned = useMemo(() => tasks.slice(), [tasks])
// Links the data already breaks get a class; the color comes from CSS.
const drawnLinks = useMemo(() => {
const late = lateLinks(links, tasks)
return links.map((link) => (late.has(link) ? { ...link, cssClass: 'link--late' } : link))
}, [links, tasks])
const onEventsChange = useCallback(
(args: SchedulerEventsChangeArgs) => setTasks([...args.events]),
[],
)
const config = useMemo<SchedulerProps>(() => {
// The other tasks as the control holds them right now.
const finder =
(control: SuperScheduler.SchedulerApi) =>
(id: SuperScheduler.EventId): Bounds | null => {
const event = control.events.find(id)
return event === null ? null : boundsOf(event.data)
}
return {
linkShape: 'Curved',
// While dragging: refuse and explain, frame by frame.
onEventMoving(args) {
const proposed = { start: args.start.getTime(), end: args.end.getTime() }
const broken = brokenFinishToStart(args.e.id(), proposed, links, finder(this))
if (broken !== null) {
args.allowed = false
args.message = 'This breaks a finish-to-start dependency'
}
},
onEventResizing(args) {
const proposed = { start: args.start.getTime(), end: args.end.getTime() }
if (brokenFinishToStart(args.e.id(), proposed, links, finder(this)) !== null)
args.allowed = false
},
// Before the commit: also covers keyboard moves.
onEventMove(args) {
const proposed = { start: args.newStart.getTime(), end: args.newEnd.getTime() }
if (brokenFinishToStart(args.e.id(), proposed, links, finder(this)) !== null)
args.preventDefault()
},
// Clicking a link offers to remove the dependency.
onLinkClick(args) {
const link = args.link.data
void confirmWithUser('Remove this dependency?').then((ok) => {
if (ok) setLinks((current) => current.filter((item) => item.id !== link.id))
})
},
}
}, [links])
return (
<SuperSchedulerComponent
{...config}
events={owned}
links={drawnLinks}
onEventsChange={onEventsChange}
resources={props.resources}
startDate="2026-10-05"
days={14}
scale="Day"
/>
)
}Sie sollten Folgendes sehen:
- Wenn Sie einen Schritt vor das Ende seines Vorgängers ziehen, zeigt die Ziehkarte „Not allowed · This breaks a finish-to-start dependency“, und das Ablegen wird abgelehnt;
- Verknüpfungen, die Ihre Daten bereits verletzen, erscheinen in der Gefahrenfarbe;
- ein Klick auf eine Verknüpfung fragt, ob diese Abhängigkeit entfernt werden soll.
onEventMoving und onEventResizing laufen bei jedem Frame eines Ziehvorgangs; halten Sie die Prüfung daher günstig: Schlagen Sie nur die Verknüpfungen des gezogenen Ereignisses nach, wie es brokenFinishToStart tut. onEventMove läuft einmal vor dem Übernehmen der Änderung und deckt auch Verschiebungen per Tastatur und von außen abgelegte Ereignisse ab.
Reserviert: interaktives Anlegen von Verknüpfungen
Damit Nutzer schon heute Abhängigkeiten anlegen können, bieten Sie das in Ihrer eigenen Oberfläche an (zum Beispiel „Abhängigkeit hinzufügen“ im Detailbereich oder Kontextmenü eines Ereignisses, wobei das andere Ereignis aus einer Liste oder der aktuellen Auswahl gewählt wird) und hängen die Verknüpfung an Ihr Array an.
Verwandte Themen
Planung von FertigungsaufträgenDie Wartung wurde vorgezogen. Verschieben Sie den Auftrag und halten Sie die Arbeitsfolge ein. Planung einer VideoproduktionEin Dreh dauert länger. Verschieben Sie den abhängigen Schnitt, verstehen Sie warum, und nehmen Sie es zurück. Liegeplatzplanung im HafenEin Schiff kommt zwölf Stunden zu spät. Verschieben Sie sein Liegefenster und nehmen Sie Schlepper und Kräne mit.
- Ziehen, Dauer ändern und Geschäftsregeln zu
onEventMoving,onEventMoveund Ablehnungsmeldungen. - Rückgängig, Wiederholen und Verlauf, um Umplanungen rückgängig machen zu können.
Verwandte Beispiele
- ForgePlanung von FertigungsaufträgenDie Wartung wurde vorgezogen. Verschieben Sie den Auftrag und halten Sie die Arbeitsfolge ein.
- FramehousePlanung einer VideoproduktionEin Dreh dauert länger. Verschieben Sie den abhängigen Schnitt, verstehen Sie warum, und nehmen Sie es zurück.
- HarborworksLiegeplatzplanung im HafenEin Schiff kommt zwölf Stunden zu spät. Verschieben Sie sein Liegefenster und nehmen Sie Schlepper und Kräne mit.
- Studio NorthRessourcenplanung für eine KreativagenturEine Designerin ist doppelt gebucht. Übergeben Sie zwei Aufgaben mit einem Ziehen, prüfen Sie, was sie freigeben, und nehmen Sie es zurück.