ProductionS’applique àLite et Pro
Langues, dates civiles et fuseaux horaires
Définissez `locale` avec n’importe quel identifiant de locale Intl, comme `fr-fr` : les noms des mois et des jours, l’horloge sur 12 ou 24 heures et le premier jour de la semaine en découlent, et `timeFormat` et `weekStarts` remplacent ces deux derniers. Les dates sont des valeurs civiles d’horloge murale : le planificateur ne convertit jamais de fuseau horaire et ne développe jamais les événements récurrents. Convertissez les instants dans le fuseau horaire de l’activité avant de passer les événements, reconvertissez-les à l’enregistrement, et développez les séries en occurrences dans votre application.
Un planificateur montre des dates à des personnes : deux préoccupations distinctes se rencontrent donc ici. La localisation décide de la façon d’écrire une date : noms, ordre, horloge et premier jour de la semaine. La sémantique du temps décide de quelle date il s’agit : la bibliothèque travaille avec des valeurs civiles d’horloge murale et laisse les fuseaux horaires et la récurrence à votre application. Ce guide couvre les deux, pour Pro et, quand c’est indiqué, pour Lite.
Définir la locale
locale accepte n’importe quel identifiant de locale que Intl comprend, écrit à la manière de SuperScheduler, en minuscules : en-us (par défaut), en-gb, fr-fr, de-de, es-es, pt-br, nl-nl, ja-jp, etc. Il n’y a aucune liste à enregistrer. en_US est normalisé en en-us, et un identifiant que Intl ne sait pas résoudre se rabat sur en-us.
Dans Pro, la locale détermine :
- les noms des mois et des jours dans les en-têtes de temps par défaut et dans chaque motif
formatd’en-tête ; - l’horloge par défaut quand
timeFormatvaut'Auto'(12 heures pouren-us, 24 heures pour la plupart des locales européennes) ; - le premier jour de la semaine quand
weekStartsvaut'Auto'(dimanche pouren-usetpt-br, lundi pour la plupart des locales européennes) ; - les motifs de date par défaut des en-têtes de jour et les dates de la carte de glissement ;
- la langue des annonces clavier et de certains libellés intégrés (voir Chaînes intégrées).
Lite accepte les mêmes identifiants locale pour ses en-têtes de jour.
import { useCallback, useState } from 'react'
import { SuperScheduler, SuperSchedulerComponent } from 'super-scheduler'
import type { SchedulerEventClickArgs } from 'super-scheduler'
// One constant for the scheduler and for every date you format yourself.
const LOCALE = 'fr-fr'
const WEEK_START = SuperScheduler.Date.today().firstDayOfWeek(1)
const TIME_HEADERS: SuperScheduler.TimeHeaderData[] = [
{ groupBy: 'Day', format: 'dddd d MMMM' }, // "lundi 5 octobre"
{ groupBy: 'Hour' }, // default labels follow timeFormat: "0" to "23" here, "2 PM" in 12-hour mode
]
interface Props {
readonly rooms: SuperScheduler.ResourceData[]
readonly events: SuperScheduler.EventData[]
}
export function FrenchPlanning({ rooms, events }: Props) {
const [summary, setSummary] = useState('')
// SuperScheduler.Date#toString does not read the scheduler's locale: pass it explicitly.
const onEventClick = useCallback((args: SchedulerEventClickArgs) => {
const start = args.e.start().toString('dddd d MMMM, HH:mm', LOCALE)
const end = args.e.end().toString('HH:mm', LOCALE)
setSummary(`${args.e.text()} : ${start} – ${end}`)
}, [])
return (
<>
<p aria-live="polite">{summary}</p>
<SuperSchedulerComponent
locale={LOCALE}
// 'Auto' already gives Monday and a 24-hour clock for fr-fr; explicit values ignore the locale.
weekStarts={1}
timeFormat="Clock24Hours"
startDate={WEEK_START}
days={7}
scale="Hour"
cellWidth={48}
timeHeaders={TIME_HEADERS}
resources={rooms}
events={events}
onEventClick={onEventClick}
/>
</>
)
}Vous devriez voir des en-têtes comme « lundi 5 octobre » au-dessus de colonnes d’heures numérotées de 0 à 23, une semaine qui commence le lundi, et un résumé en français quand vous cliquez sur un événement.
Format d’horloge et premier jour de la semaine
timeFormat accepte 'Auto', 'Clock12Hours' ou 'Clock24Hours'. Il ne modifie que les libellés d’heures par défaut ; un format explicite dans timeHeaders l’emporte toujours ('HH:mm' pour 24 heures, 'h:mm tt' pour 12 heures). C’est un réglage d’affichage, pas une conversion horaire : le changer ne déplace jamais un événement.
weekStarts accepte 'Auto' ou un numéro de jour de 0 (dimanche) à 6 (samedi). Il agit sur les cellules et groupes d’en-tête Week, sur les lignes de semaine tracées en vue dézoomée et sur les numéros de semaine par défaut : numéros ISO quand les semaines commencent le lundi, numéros américains sinon. Dans votre propre code, date.firstDayOfWeek() prend le dimanche par défaut : passez donc la même valeur (firstDayOfWeek(1)) ou l’identifiant de locale (firstDayOfWeek('fr-fr')).
Ce que donne 'Auto' pour quelques identifiants :
| Locale | ddd d MMM | Horloge | Début de semaine |
|---|---|---|---|
en-us | Mo 5 Oct | 12 heures | dimanche |
en-gb | Mo 5 Oct | 24 heures | lundi |
es-es | L 5 oct | 24 heures | lundi |
de-de | Mo 5 Okt | 24 heures | lundi |
fr-fr | lu 5 oct. | 24 heures | lundi |
pt-br | se 5 out. | 24 heures | dimanche |
Formater les dates dans votre propre interface
SuperScheduler.Date formate avec les motifs de SuperScheduler : yyyy, yy, MMMM, MMM, MM, M, dddd, ddd, dd, d, HH, H, hh, h, mm, m, ss, s et tt (AM/PM). Tout le reste est du texte littéral.
ddd est le nom court du jour fourni par Intl, qui fait une ou deux lettres dans plusieurs locales : « Mo » en anglais, « L » en espagnol, « lu » en français, « dl » en catalan. Utilisez dddd pour le nom complet, enregistrez vos propres noms courts (voir plus bas), ou formatez directement avec Intl. date.toDate() renvoie une Date native avec les mêmes ticks, à lire en UTC : formatez-la donc avec timeZone: 'UTC' pour afficher exactement la valeur civile sur n’importe quel appareil :
import type { SuperScheduler } from 'super-scheduler'
// toDate() returns a native Date with the same ticks, meant to be read in UTC.
// Formatting it with timeZone 'UTC' shows exactly the civil value, on any device.
const dayFormat = new Intl.DateTimeFormat('fr-FR', {
weekday: 'short',
day: 'numeric',
month: 'short',
timeZone: 'UTC',
})
const stayFormat = new Intl.DateTimeFormat('en-GB', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
timeZone: 'UTC',
})
export function formatDay(date: SuperScheduler.Date): string {
return dayFormat.format(date.toDate()) // "lun. 5 oct."
}
export function formatStay(start: SuperScheduler.Date, end: SuperScheduler.Date): string {
return stayFormat.formatRange(start.toDate(), end.toDate())
}Noms et motifs personnalisés
SuperScheduler.Locale.register() remplace les noms et les motifs d’un identifiant pour tous les planificateurs et tous les appels de toString qui l’utilisent. Un SuperScheduler.Locale passé directement dans l’option locale est enregistré automatiquement.
import { SuperScheduler } from 'super-scheduler'
// Start from the Intl data for the id: fields you leave out fall back to US English, not to French.
const base = SuperScheduler.Locale.find('fr-fr')
// Run once at startup, before the first scheduler mounts.
SuperScheduler.Locale.register(
new SuperScheduler.Locale('fr-fr', {
...base,
// `ddd` gives two-letter Intl abbreviations ("lu"); these read "lun.".
dayNamesShort: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
}),
)
// Every scheduler with locale="fr-fr" and every toString(pattern, 'fr-fr') now uses these names.
export const sample = new SuperScheduler.Date('2026-10-05').toString('ddd d MMM', 'fr-fr') // "lun. 5 oct."Chaînes intégrées et leurs langues
La bibliothèque écrit elle-même quelques chaînes. La langue est la première partie de l’identifiant de locale (ca-es donne ca).
| Chaînes | Langues | Personnalisation |
|---|---|---|
| Annonces clavier et libellés de focus | anglais, espagnol, catalan, basque, galicien, allemand, français, italien, portugais | Non configurable |
| Durées et refus de la carte de glissement (« 2 nights », « Overlaps », « Not allowed ») | Les neuf mêmes langues | dragCard={{ labels: { ... } }} |
| Nom de la grille, textes de chargement, d’état vide et d’erreur | anglais, ou espagnol pour les locales es | emptyState, errorState et loadingLabelText ; le nom accessible de la grille est fixe |
| Libellés des entrées d’historique (« Move », « Resize ») | anglais, ou espagnol pour les locales es | createHistory({ labels }) |
| Libellés de la minimap | anglais, ou espagnol pour les locales es | createMinimap(control, element, { labels }) ou la prop labels de SchedulerMinimap |
| Badge du niveau de détail | anglais, ou espagnol pour les locales es | troisième argument de createLodBadge |
| Lite : libellé de la grille et texte d’état vide | anglais | ariaLabel, emptyState |
Toute autre langue affiche l’anglais : une application en français ou en allemand doit donc passer ses propres chaînes pour l’historique, la minimap, le badge et les textes d’état. Le texte des événements, les noms des ressources et tout HTML que vous affichez sont à traduire par vous.
Dates civiles
Dans SuperScheduler, chaque date est une valeur civile d’horloge murale, sans fuseau horaire. '2026-10-01T10:00:00' signifie dix heures sur le planning, où que la page soit ouverte. Les conséquences :
- Les chaînes exigent les secondes.
'2026-10-01'et'2026-10-01T10:00:00'sont valides ;'2026-10-01T10:00'lève l’erreur « is not an ISO 8601 date ». Les objetsDatenatifs ne passent pas la vérification de types pourstartouend. - Les fuseaux sont convertis en UTC. Une chaîne avec
Zou un décalage est convertie en heure murale UTC :'2026-10-01T10:00:00+02:00'devient08:00:00. Ne retirez les fuseaux qu’après avoir vous-même converti dans le fuseau horaire de l’activité. - Pas de surprise au changement d’heure.
2026-03-29T02:30:00existe, et lui ajouter une heure donne03:30, quel que soit le fuseau du navigateur. Les durées sont de simples différences d’horloge murale. - Les intervalles sont semi-ouverts. Un événement de
14:00à16:00se termine avant une réservation qui commence à16:00. - Dates natives.
new SuperScheduler.Date(date)lit les champs UTC d’uneDatenative ;new SuperScheduler.Date(date, true)lit ses champs locaux.toDate()renvoie uneDateà lire en UTC ;toDateLocal()en renvoie une dont les champs locaux affichent l’heure murale. - « Aujourd’hui » est celui de l’appareil du visiteur.
SuperScheduler.Date.today(), lestartDatepar défaut, la mise en évidence du jour et la ligne de l’heure actuelle utilisent l’horloge de l’appareil. Une personne à New York qui consulte le planning d’un hôtel à Madrid voit la date du jour à New York. Si cela compte, calculez vous-même la date du jour de l’activité et passez-la dansstartDateou àscrollTo.
Les fuseaux horaires relèvent de votre application
Si votre backend stocke des instants (horodatages UTC), choisissez le fuseau horaire que représente chaque planificateur, en général celui du site ou du responsable de la ressource, et convertissez aux frontières. Intl.DateTimeFormat avec un timeZone vous donne l’heure murale de n’importe quel instant, sans dépendance supplémentaire :
import { SuperScheduler } from 'super-scheduler'
const formatters = new Map<string, Intl.DateTimeFormat>()
function partsFormatter(timeZone: string): Intl.DateTimeFormat {
let formatter = formatters.get(timeZone)
if (formatter === undefined) {
formatter = new Intl.DateTimeFormat('en-US', {
timeZone,
hourCycle: 'h23',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})
formatters.set(timeZone, formatter)
}
return formatter
}
/** What a wall clock in `timeZone` shows at `instant`, as a civil ISO string: "2026-10-01T10:00:00". */
export function toWallClock(instant: Date, timeZone: string): string {
const part: Record<string, string> = {}
for (const { type, value } of partsFormatter(timeZone).formatToParts(instant)) part[type] = value
return `${part.year}-${part.month}-${part.day}T${part.hour}:${part.minute}:${part.second}`
}
function offsetAt(ms: number, timeZone: string): number {
return Date.parse(`${toWallClock(new Date(ms), timeZone)}Z`) - ms
}
/**
* The instant at which a wall clock in `timeZone` shows `wall`. Times skipped or repeated by a
* daylight-saving change have no single answer: this picks a neighbouring instant, so validate
* them in your application if they matter.
*/
export function fromWallClock(wall: SuperScheduler.DateInput, timeZone: string): Date {
const asUtc = Date.parse(`${new SuperScheduler.Date(wall).value}Z`)
const guess = asUtc - offsetAt(asUtc, timeZone)
return new Date(asUtc - offsetAt(guess, timeZone))
}
/** API instants to scheduler events on the property's wall clock. */
export async function loadBookings(fromUtc: string, toUtc: string, timeZone: string) {
const rows = await fetchBookingInstants(fromUtc, toUtc)
return rows.map((row): SuperScheduler.EventData => ({
id: row.id,
resource: row.roomId,
text: row.guest,
start: toWallClock(new Date(row.startUtc), timeZone),
end: toWallClock(new Date(row.endUtc), timeZone),
}))
}
// Saving goes the other way: fromWallClock(event.start, 'Europe/Madrid').toISOString()Convertissez à l’entrée (toWallClock quand vous transformez les lignes de l’API en événements) et à la sortie (fromWallClock quand vous enregistrez start et end, qui sont des objets SuperScheduler.Date après un glissement). Pour le chargement par plages, convertissez de la même façon le start et le end civils de la tranche avant d’interroger une API en UTC.
Les heures sautées ou répétées lors des changements d’heure sont une règle métier, pas un détail de formatage. Une réservation à 02:30 la nuit du passage à l’heure d’été n’existe pas à Madrid ; décidez s’il faut la refuser, la déplacer ou la stocker autrement.
Événements récurrents
Le planificateur n’a pas de moteur de récurrence. Les champs recurrent et recurrentMasterId sont typés, mais control.events.findRecurrent() est réservé et renvoie null. Stockez les séries dans votre application et développez-les en événements ordinaires pour les dates affichées. Donnez à chaque occurrence un id stable d’une requête à l’autre, par exemple l’id de la série suivi de sa date, pour que le chargement par plages puisse la fusionner :
import { SuperScheduler } from 'super-scheduler'
/** A weekly series as your application stores it. */
export interface WeeklySeries {
readonly id: string
readonly resource: string
readonly text: string
/** First occurrence, civil date-time with seconds. */
readonly start: string
readonly durationMinutes: number
/** 0 = Sunday ... 6 = Saturday. */
readonly weekdays: readonly number[]
/** Last day of the series, inclusive, as yyyy-MM-dd. */
readonly until: string
/** Days removed from the series, as yyyy-MM-dd. */
readonly exceptions: readonly string[]
}
/** The occurrences that overlap [from, to), each with an id that is stable across requests. */
export function expandWeekly(
series: WeeklySeries,
from: SuperScheduler.Date,
to: SuperScheduler.Date,
) {
const first = new SuperScheduler.Date(series.start)
const firstDay = first.getDatePart().ticks
const afterLastDay = new SuperScheduler.Date(series.until).addDays(1).ticks
// Look back far enough to catch an occurrence that started earlier and is still running.
const lookBack = Math.ceil(series.durationMinutes / 1440)
const events: SuperScheduler.EventData[] = []
for (
let day = from.getDatePart().addDays(-lookBack);
day.ticks < to.ticks;
day = day.addDays(1)
) {
if (day.ticks < firstDay || day.ticks >= afterLastDay) continue
if (!series.weekdays.includes(day.getDayOfWeek())) continue
const key = day.toString('yyyy-MM-dd')
if (series.exceptions.includes(key)) continue
const start = day.addTime(first.getTimePart())
const end = start.addMinutes(series.durationMinutes)
if (end.ticks <= from.ticks) continue
events.push({
id: `${series.id}:${key}`,
resource: series.resource,
text: series.text,
start,
end,
})
}
return events
}La modification vous revient aussi. « Cette occurrence uniquement » signifie en général ajouter la date aux exceptions de la série et créer un événement indépendant ; « Cette occurrence et les suivantes » scinde la série ; « Toutes les occurrences » modifie la série et la développe à nouveau. Faites correspondre les modifications de onEventsChange à ces opérations grâce à l’id de l’occurrence. Développer les séries dans la fonction load d’un chargeur de plages garde les longues séries peu coûteuses : seules les tranches visibles sont développées.
Liste de vérification
- Une seule constante
LOCALE, passée au composant et à chaque appel detoString. weekStartsetfirstDayOfWeek()concordent.- Vos propres chaînes pour l’historique, la minimap, le badge et les textes d’état dans toute langue autre que l’anglais et l’espagnol.
- Des dates d’événements sous forme de chaînes civiles avec secondes, converties dans le fuseau horaire de l’activité avant d’arriver au planificateur.
- L’enregistrement reconvertit en instants si votre backend les stocke ainsi.
- Des séries développées par plage visible, avec des id d’occurrence stables.
Planification de production audiovisuelleUn tournage déborde. Déplacez le montage qui en dépendait, comprenez pourquoi, puis revenez en arrière. Guides associés : Ressources, événements et intervalles, Échelles de temps et zoom, Clavier, accessibilité et tactile et Chargement des données par plage de dates.