@primeui/vue-scheduler
v1.0.0
Published
Vue Scheduler component for PrimeUI.
Readme
@primeui/vue-scheduler
@primeui/vue-scheduler is PrimeUI's Vue 3 scheduler package. Scheduler.Root owns scheduler state and context; visible structure is composed with public shell parts and engine parts so consumers can keep scheduler-owned behavior while controlling the product shell.
Install
pnpm add @primeui/vue-schedulerImport the namespace export and the scheduler styles once:
import { Scheduler } from '@primeui/vue-scheduler';
import '@primeui/vue-scheduler/style.css';
import '@primeui/vue-scheduler/themes/primeone.css';Usage Modes
1. Scheduler.Root + built-in parts
Render Scheduler.Root with explicit built-in children when the package header, content routing, and overlays already fit the product.
2. Scheduler.Root + shell parts
Render shell parts such as Scheduler.Header, Scheduler.Navigation, Scheduler.Title, Scheduler.ViewSelector, Scheduler.SelectionToolbar, Scheduler.CategoryLegend, Scheduler.Content, and overlay parts when you want a custom layout without taking over scheduler state.
3. Scheduler.Root + engine parts
Render a public engine directly, such as Scheduler.Timeline.Root, when the product is pinned to a specific scheduler mode and needs a custom frame around that engine.
Custom rendering is compound-only in the first-version API. Scheduler.Root provides state and props; shell parts compose the scheduler layout, and routed content uses semantic child outlet parts inside Scheduler.Content.
Outlet wrappers declare the runtime surface. Their child components receive the runtime props from the owning scheduler engine, so prop-dependent UI should live in real Vue components rather than inline markup on the wrapper.
Built-In Parts Quick Start
<script setup lang="ts">
import { ref } from 'vue';
import { Scheduler } from '@primeui/vue-scheduler';
import {
SchedulerAgendaDateHeaderUI,
SchedulerAgendaEventUI,
SchedulerAllDayEventUI,
SchedulerContextMenuUI,
SchedulerEventPopoverUI,
SchedulerMonthEventUI,
SchedulerMorePopoverUI,
SchedulerQuickInfoUI,
SchedulerSelectionToolbarUI,
SchedulerTimeGridEventUI,
SchedulerTimelineEventUI,
SchedulerToolbarUI
} from '@ui/scheduler';
import '@primeui/vue-scheduler/style.css';
import '@primeui/vue-scheduler/themes/primeone.css';
const currentDate = ref(new Date());
const currentView = ref<'week' | 'day'>('week');
const events = ref([
{
id: 'standup',
title: 'Team Standup',
start: new Date(),
end: new Date(Date.now() + 60 * 60 * 1000)
}
]);
</script>
<template>
<Scheduler.Root v-model:date="currentDate" v-model:view="currentView" :events="events" now-indicator height="640px">
<Scheduler.Header v-slot="toolbar">
<SchedulerToolbarUI v-bind="toolbar" />
</Scheduler.Header>
<Scheduler.SelectionToolbar v-slot="props">
<SchedulerSelectionToolbarUI v-bind="props" />
</Scheduler.SelectionToolbar>
<Scheduler.Content>
<Scheduler.TimeGridEvent>
<SchedulerTimeGridEventUI />
</Scheduler.TimeGridEvent>
<Scheduler.AllDayEvent>
<SchedulerAllDayEventUI />
</Scheduler.AllDayEvent>
<Scheduler.MonthEvent>
<SchedulerMonthEventUI />
</Scheduler.MonthEvent>
<Scheduler.TimelineEvent>
<SchedulerTimelineEventUI />
</Scheduler.TimelineEvent>
<Scheduler.AgendaDateHeader>
<SchedulerAgendaDateHeaderUI />
</Scheduler.AgendaDateHeader>
<Scheduler.AgendaEvent>
<SchedulerAgendaEventUI />
</Scheduler.AgendaEvent>
</Scheduler.Content>
<Scheduler.Popover v-slot="props">
<SchedulerEventPopoverUI v-bind="props" />
</Scheduler.Popover>
<Scheduler.QuickInfo v-slot="props">
<SchedulerQuickInfoUI v-bind="props" />
</Scheduler.QuickInfo>
<Scheduler.MorePopover v-slot="props">
<SchedulerMorePopoverUI v-bind="props" />
</Scheduler.MorePopover>
<Scheduler.ContextMenu v-slot="props">
<SchedulerContextMenuUI v-bind="props" />
</Scheduler.ContextMenu>
</Scheduler.Root>
</template>Shell Parts Example
This is the same composition pattern shown in apps/playgrounds/vue-playground/src/pages/scheduler/CompoundShellDemo.vue: custom product chrome around Scheduler.Content, while Scheduler.Root still owns date, view, navigation, and event interaction state.
<script setup lang="ts">
import { ref } from 'vue';
import { Scheduler } from '@primeui/vue-scheduler';
const currentDate = ref(new Date());
const currentView = ref<'month' | 'week' | 'day'>('week');
const views = ['month', 'week', 'day'] as const;
const events = ref([
{
id: 'review',
title: 'Ops Review',
start: new Date(),
end: new Date(Date.now() + 60 * 60 * 1000)
}
]);
</script>
<template>
<Scheduler.Root v-model:date="currentDate" v-model:view="currentView" :views="views" :events="events" height="auto">
<div class="space-y-4">
<Scheduler.Header>
<div class="product-shell">
<Scheduler.Title />
<div class="custom-actions">Stats and filters live here.</div>
<Scheduler.Navigation v-slot="{ items }">
<button v-for="item in items" :key="item.name" type="button" :aria-label="item.ariaLabel" @click="item.action">
{{ item.name === 'prev' ? 'Previous' : item.name === 'next' ? 'Next' : item.label }}
</button>
</Scheduler.Navigation>
<Scheduler.ViewSelector v-slot="{ view, viewOptions, changeView }">
<button v-for="option in viewOptions" :key="option.value" type="button" :aria-pressed="view === option.value" @click="changeView(option.value)">
{{ option.label }}
</button>
</Scheduler.ViewSelector>
</div>
</Scheduler.Header>
<div class="scheduler-frame">
<Scheduler.Content />
</div>
</div>
</Scheduler.Root>
</template>Content Child Outlets
Use child outlet parts inside Scheduler.Content for event, cell, resource, and agenda customization across routed views. Each outlet renders nothing where it is declared; it captures its default child and the scheduler later renders that child with runtime props from the owning surface. Scheduler event owners and agenda date headers are headless by default; import the semantic PrimeOne UI components from @ui/scheduler when you want the PrimeOne default visuals.
Specific event outlets take precedence over generic Scheduler.Event: Scheduler.TimeGridEvent, Scheduler.AllDayEvent, Scheduler.MonthEvent, Scheduler.TimelineEvent, and Scheduler.AgendaEvent render first for their surfaces. Scheduler.Event remains an intentional broad override across event surfaces, including agenda, when no matching specific outlet is declared.
<script setup lang="ts">
import { Scheduler } from '@primeui/vue-scheduler';
import EventCard from './EventCard.vue';
import CapacityHeatmap from './CapacityHeatmap.vue';
import MonthDaySurface from './MonthDaySurface.vue';
import TimeGridBackdrop from './TimeGridBackdrop.vue';
import TechnicianRow from './TechnicianRow.vue';
import YearDayBadge from './YearDayBadge.vue';
</script>
<template>
<Scheduler.Content>
<Scheduler.TimeGridEvent>
<EventCard />
</Scheduler.TimeGridEvent>
<Scheduler.AllDayEvent>
<EventCard />
</Scheduler.AllDayEvent>
<Scheduler.MonthEvent>
<EventCard />
</Scheduler.MonthEvent>
<Scheduler.TimelineEvent>
<EventCard />
</Scheduler.TimelineEvent>
<Scheduler.TimeGridCell>
<TimeGridBackdrop />
</Scheduler.TimeGridCell>
<Scheduler.MonthDayCell>
<MonthDaySurface />
</Scheduler.MonthDayCell>
<Scheduler.TimelineCell>
<CapacityHeatmap />
</Scheduler.TimelineCell>
<Scheduler.Resource>
<TechnicianRow />
</Scheduler.Resource>
<Scheduler.MiniMonthCell>
<YearDayBadge />
</Scheduler.MiniMonthCell>
</Scheduler.Content>
</template>Compound outlets customize semantic scheduler surfaces. Outlet children receive runtime props from the scheduler, such as event, date, startDate, endDate, resource, resourceId, events, eventCount, selection flags, business-hour flags, and disabled/range state where that surface owns them. Routed content customization uses child outlet parts inside Scheduler.Content.
Concise routed customization example:
<Scheduler.Content>
<Scheduler.TimeGridEvent>
<WorkOrderCard />
</Scheduler.TimeGridEvent>
<Scheduler.TimeGridCell>
<AvailabilityOverlay />
</Scheduler.TimeGridCell>
<Scheduler.Resource>
<TechnicianRow />
</Scheduler.Resource>
</Scheduler.Content>Available outlet parts include Scheduler.Event, Scheduler.TimeGridEvent, Scheduler.AllDayEvent, Scheduler.MonthEvent, Scheduler.TimelineEvent, Scheduler.AgendaEvent, Scheduler.TimeGridCell, Scheduler.AllDayCell, Scheduler.MonthDayCell, Scheduler.TimelineCell, Scheduler.TimelineHeaderCell, Scheduler.Resource, Scheduler.ResourceAreaHeader, Scheduler.AgendaDateHeader, Scheduler.MiniMonthCell, and Scheduler.MiniMonthHeader.
Timeline Engine Example
This follows the fixed-timeline pattern from apps/playgrounds/vue-playground/src/pages/scheduler/CompoundTimelineDemo.vue: keep Scheduler.Root for shared scheduler state, but render public timeline parts directly instead of routing through Scheduler.Content.
<script setup lang="ts">
import { computed, ref } from 'vue';
import { Scheduler } from '@primeui/vue-scheduler';
const currentView = ref<'resourceTimelineWeek'>('resourceTimelineWeek');
const currentDate = ref(new Date());
const resourceSearch = ref('');
const resources = ref([
{ id: 'ops', title: 'Ops Pod' },
{ id: 'platform', title: 'Platform Pod' }
]);
const visibleResources = computed(() => {
const query = resourceSearch.value.trim().toLowerCase();
return query ? resources.value.filter((resource) => resource.title.toLowerCase().includes(query)) : resources.value;
});
const events = ref([
{
id: 'release',
title: 'Release Readiness',
start: new Date(),
end: new Date(Date.now() + 2 * 60 * 60 * 1000),
resourceId: 'ops'
}
]);
</script>
<template>
<Scheduler.Root v-model:date="currentDate" :view="currentView" :views="[currentView]" :events="events" :resources="visibleResources" height="auto" :resource-area-width="'min(240px, 45vw)'" :timeline-slot-width="92" :timeline-slot-duration="60">
<div class="space-y-4">
<Scheduler.Header>
<div class="timeline-shell">
<Scheduler.Navigation v-slot="{ items }">
<button v-for="item in items" :key="item.name" type="button" :aria-label="item.ariaLabel" @click="item.action">
{{ item.name === 'prev' ? 'Previous' : item.name === 'next' ? 'Next' : item.label }}
</button>
</Scheduler.Navigation>
<Scheduler.Title />
<input v-model="resourceSearch" type="text" placeholder="Filter resources..." />
</div>
</Scheduler.Header>
<Scheduler.Timeline.Root class="timeline-frame">
<div class="flex h-full overflow-hidden">
<Scheduler.Timeline.ResourceArea />
<div class="flex min-w-0 flex-1 flex-col overflow-hidden">
<Scheduler.Timeline.Header />
<Scheduler.Timeline.Body />
</div>
</div>
</Scheduler.Timeline.Root>
</div>
</Scheduler.Root>
</template>Decision Guide
- Use
Scheduler.Rootwith built-in parts when the default scheduler shell is already close to the product. - Use shell parts when you need to rearrange the header or add product controls, but still want
Scheduler.Contentto select and render the active engine. - Use engine parts when the product is fixed to one mode, such as a timeline layout, and you need custom framing around that engine.
- Use child outlets inside
Scheduler.Contentfor routed events, cells, and resources. Keep part-owned slots for shell/overlay chrome such asScheduler.Header,Scheduler.QuickInfo,Scheduler.Popover,Scheduler.MorePopover, andScheduler.ContextMenu. Event, cell, resource, agenda, and mini-month customization belongs on the semantic child outlet parts. - If an application wants one-line usage, create that wrapper in application code by composing these public parts.
License
Licensed under the PrimeUI Pro License - Copyright (c) PrimeTek Informatics
