Docyrus

ResourceSchedulerPanel

Preset-driven resource timeline — grouped multi-unit headers, a sticky resource column with synced scrolling, long-press drag to move events across time and resources, edge resize, and event detail bottom sheets.

iOSAndroid
Preview ResourceSchedulerPanel on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-resource-scheduler-panel
Required Packages(3 packages)
pnpm add date-fns react-native-gesture-handler react-native-reanimated

Usage

import { ResourceSchedulerPanel } from '@/components/docyrus-native/resource-scheduler-panel';
import type { SchedulerEvent, SchedulerResource } from '@/components/docyrus-native/resource-scheduler-panel';

const resources: SchedulerResource[] = [
  { id: 'room-a', name: 'Room A', subtitle: '12 seats', group: 'Meeting rooms' },
  { id: 'dr-smith', name: 'Dr. Smith', subtitle: 'Cardiology', group: 'Doctors', avatar: 'https://…' },
];

const [events, setEvents] = useState<SchedulerEvent[]>([
  { id: 'e1', resourceId: 'room-a', title: 'Standup', startDate: new Date(2026, 8, 24, 9), endDate: new Date(2026, 8, 24, 10), color: '#3b82f6' },
]);

<ResourceSchedulerPanel
  resources={resources}
  events={events}
  defaultPresetId="weeks-days"
  onEventMove={(event, start, end, newResourceId) =>
    setEvents(prev => prev.map(e => e.id === event.id
      ? { ...e, startDate: start, endDate: end, resourceId: newResourceId ?? e.resourceId }
      : e))}
  onEventResize={(event, start, end) =>
    setEvents(prev => prev.map(e => e.id === event.id ? { ...e, startDate: start, endDate: end } : e))}
  onSlotClick={(resourceId, date) => createBooking(resourceId, date)}
  quickSlotHours={[9, 11, 13, 15, 17]}
/>

Custom resource columns

import type { ResourceSchedulerColumn } from '@/components/docyrus-native/resource-scheduler-panel';

const columns: ResourceSchedulerColumn[] = [
  { id: 'name', header: 'Resource', getValue: r => r.name },
  { id: 'seats', header: 'Seats', getValue: r => String(r.seats ?? '—'), width: 56 },
];

<ResourceSchedulerPanel resources={resources} events={events} resourceColumns={columns} resourcePanelWidth={180} />

Behavior

  • Presets — every preset has an interval unit (column) and a group unit (header row above it). The built-in SCHEDULER_PRESETS are day-hours, weeks-days, months-days, months-weeks and months (Year / Months). Changing the preset (header select) resets the range to that preset's default range. Timeline math (lib/timeline-utils.ts, lib/presets.ts) is shared verbatim with web.
  • Navigation — Today resets to the preset's default range; the arrows shift the range by one group unit. onDateRangeChange fires for all three. When today falls inside the range, the timeline scrolls it into view.
  • Layout — the resource column stays fixed on the left while the lanes scroll horizontally; vertical scrolling of the lanes drives the resource column (Reanimated scrollTo), so rows stay aligned. Overlapping events stack into sub-rows and the lane grows to fit (the resource cell grows with it). Resources with a group get collapsible group header rows in both columns.
  • Drag & drop — long-press an event (300 ms), then drag: it moves in whole interval units (hour / day / week / month / year) and can be dropped on another resource row → onEventMove(event, start, end, newResourceId?). Long-press an edge handle (120 ms) to resize → onEventResize. A floating pill shows the snapped range and target resource during the drag; scrolling is locked while dragging. Drag is disabled when readOnly or when the matching callback is missing.
  • Tapping — tapping an event fires onEventClick and opens a detail bottom sheet (time, resource, location, description) unless showEventDetails={false} (web: popover). Tapping an empty lane area fires onSlotClick(resourceId, date) with the date under the finger.
  • Quick slots (native-only) — with quickSlotHours and onSlotClick, tapping a resource cell opens a sheet of hour chips for today (or the range start when today is outside the range); a chip calls onSlotClick.
  • Loading / empty — isLoading renders skeleton rows; no resources renders the "No events to display" state.
  • Formatting — the event sheet and drag pill format dates through useDateFormat().formatDateTime when a provider is mounted (else MMM d, HH:mm). Header / column labels use the preset's date-fns formats, like web. Copy goes through useUiTranslation() with the web ui.scheduler.* keys.

API Reference

PropTypeDefaultDescription
resourcesSchedulerResource[]—Required. Rows of the scheduler.
eventsSchedulerEvent[]—Required. Events placed on the resource lanes.
presetsTimelinePreset[]SCHEDULER_PRESETSPresets offered in the header select.
defaultPresetIdstringpresets[0].idInitially active preset.
startDateDatepreset default range startInitial range start.
endDateDatepreset default range endInitial range end.
resourceColumnsResourceSchedulerColumn[]—Light column list for the resource panel (web: TanStack ColumnDef[]). The first column's header labels the panel.
renderResource(resource: SchedulerResource) => ReactNode—Native-only. Custom resource cell; wins over resourceColumns.
onEventClick(event: SchedulerEvent) => void—Fires when an event is tapped.
onEventMove(event: SchedulerEvent, newStart: Date, newEnd: Date, newResourceId?: string) => void—Fires after a drag; newResourceId is set only when the resource changed. Enables move drag.
onEventResize(event: SchedulerEvent, newStart: Date, newEnd: Date) => void—Fires after an edge resize. Enables resize handles.
onSlotClick(resourceId: string, date: Date) => void—Fires when an empty lane area (or a quick-slot chip) is tapped.
onDateRangeChange(start: Date, end: Date) => void—Fires when the visible range changes.
onPresetChange(presetId: string) => void—Fires when the preset changes.
isLoadingbooleanfalseRender skeleton rows.
readOnlybooleanfalseDisable drag, resize, slot taps and quick slots.
showTodayIndicatorbooleantrueShow the today line and header pill.
showResourceCountbooleantrueShow the resource count badge.
heightnumber | 'auto'480Panel height (web default 600). 'auto' grows with the rows and disables vertical scrolling.
resourcePanelWidthnumber140Resource column width (web default 280).
rowHeightnumber48Minimum lane height; lanes grow with stacked events.
showEventDetailsbooleantrueNative-only. Open the event detail sheet on tap.
quickSlotHoursnumber[]—Native-only. Hours (0–23) offered as quick-slot chips when a resource is tapped.
classNamestring—Additional classes for the root container.

SchedulerResource

PropertyTypeDescription
idstringUnique id.
namestringDisplay name.
avatarstringOptional avatar image (initials otherwise).
subtitlestringOptional second line.
groupstringOptional group; grouped resources get collapsible header rows.
[key: string]unknownAny extra data (for resourceColumns / renderResource).

SchedulerEvent

PropertyTypeDescription
idstringUnique id.
resourceIdstringResource the event belongs to.
titlestringBar label.
startDateDateStart.
endDateDateEnd.
colorstringBar color (defaults to the theme primary).
variant'solid' | 'outlined' | 'subtle'Bar style. Default 'solid'.
iconReactNodeOptional icon before the title.
descriptionstringShown in the detail sheet.
locationstringShown in the detail sheet.
metadataRecord<string, unknown>Free-form data.

ResourceSchedulerColumn

PropertyTypeDescription
idstringColumn id.
headerstringHeader label (the first column's header labels the panel).
getValue(resource: SchedulerResource) => ReactNodeCell content; strings / numbers are wrapped in Text.
widthnumberOptional fixed width; columns without one share the rest.

TimelinePreset / TimelinePresetConfig

PropertyTypeDescription
idstringPreset id.
namestringLabel in the preset select.
config.unitWidthnumberColumn width in px.
config.interval{ unit: TimeUnit; format: string }Column unit + date-fns label format.
config.groupsArray<{ unit: TimeUnit; format: string }>Group header unit + format (the first entry is rendered).

Exports

ExportDescription
ResourceSchedulerPanelThe component.
SCHEDULER_PRESETSBuilt-in presets.
getPresetById(presets, id)Find a preset (falls back to the first).
eventBarVariantstv() slots (base, label) for the event bar (web: CVA).

Type Exports

TypeDescription
ResourceSchedulerPanelPropsProps for ResourceSchedulerPanel.
SchedulerResourceResource row.
SchedulerEventTimeline event.
ResourceSchedulerColumnNative-only. Resource panel column.
TimelinePresetPreset definition.
TimelinePresetConfigPreset config (unit width, interval, groups).
TimeUnit'hour' | 'day' | 'week' | 'month' | 'year'.
TimelineColumnGenerated interval column (start, end, label, index).
TimelineGroupHeaderGenerated group header (start, end, label, spanCount).

On this page