Sidebar Plugin
The Sidebar plugin adds a calendar management sidebar to your DayFlow calendar. It includes a mini calendar for date navigation, a calendar list with visibility toggles, and full calendar CRUD operations (create, rename, recolor, merge, delete, import/export).
Installation
Install the plugin package:
Usage
import { useCalendarApp, DayFlowCalendar } from '@dayflow/react'; // or '@dayflow/vue', '@dayflow/svelte', '@dayflow/angular'
import { createSidebarPlugin } from '@dayflow/plugin-sidebar';
function MyCalendar() {
const sidebarPlugin = createSidebarPlugin({
width: 280,
createCalendarMode: 'modal',
});
const calendar = useCalendarApp({
views: [
/* your views */
],
plugins: [sidebarPlugin],
});
return <DayFlowCalendar calendar={calendar} />;
}Configuration
| Property | Type | Default | Description |
|---|---|---|---|
width | number | string | '240px' | Width of the sidebar (e.g. 280 or '20rem') |
miniWidth | string | '50px' | Width of the sidebar when collapsed |
initialCollapsed | boolean | false | Whether the sidebar starts collapsed |
createCalendarMode | 'inline' | 'modal' | 'inline' | How the "create calendar" form is displayed |
colorPickerMode | 'blossom' | 'default' | 'default' | Which color picker component to use |
onSubscribeCalendar | (calendar, events) => Promise<void> | undefined | Callback triggered after a new subscription |
onLoadSubscription | (calendar) => Promise<void> | undefined | Custom loader for existing subscriptions |
onReorder | (calendars: CalendarType[]) => void | Promise<void> | undefined | Callback triggered after calendar reordering |
render | (props: CalendarSidebarRenderProps) => TNode | undefined | Full override for the sidebar UI |
renderSidebarHeader | (args: SidebarHeaderSlotArgs) => TNode | undefined | Custom sidebar header renderer |
renderCalendarContextMenu | (calendar, onClose) => TNode | undefined | Custom context menu renderer for calendar items |
renderCreateCalendarDialog | (props) => TNode | undefined | Custom create-calendar dialog renderer |
Features
The sidebar provides a ready-made layout with visibility toggles, calendar color swatches, and collapse controls. It also supports calendar creation and context menus, allowing users to add, edit colors, and delete calendars.
Built-in features include:
- Mini Calendar - A compact month view for quick date navigation
- Calendar List - Toggle visibility of individual calendars with color-coded checkboxes
- Create Calendar - Add new calendars with custom name and color
- Rename / Recolor - Right-click a calendar to rename or change its color
- Calendar Reordering - Drag and drop calendars in the list to reorder them. Use the
onReordercallback to persist the new order to your backend. - Merge Calendars - Move all events from one calendar into another
- Delete Calendar - Remove a calendar with a confirmation step (or merge first)
- Import / Export - Import
.icsfiles or export calendars to.ics - Subscribe to Calendar - Subscribe to remote
.icsfeeds by URL; subscribed calendars display a badge in the sidebar list
Live Showcase
Explore the built-in sidebar UI and a custom framework-native implementation below.
What to explore
- Visibility Toggles: Hide and show calendars to see the views update instantly.
- Drag & Sync: Drag events between views; the sidebar keeps upcoming highlights in sync.
- Collapse: Toggle the sidebar to reclaim space when focusing on dense week views.
Subscribe to Calendar
The sidebar includes a built-in subscribe feature that lets users add remote ICS calendar feeds. To trigger it, right-click any calendar or the empty area in the sidebar to open the context menu and select Subscribe to Calendar, then enter any publicly accessible .ics URL.
DayFlow fetches the file, parses the events, and creates a new calendar automatically. Subscribed calendars display a small badge icon in the sidebar list to distinguish them from locally created calendars.
Automatic Loading & Deduplication
When you provide a calendar with a subscription.url in the initial configuration, the Sidebar plugin automatically fetches the latest events on mount.
To prevent visual duplicates when combining locally cached events with fresh subscription data, DayFlow implements ID-based deduplication:
- If an event from a subscription has the same
idas an existing event in the core store, the subscription version takes precedence. - This ensures your UI always shows the most up-to-date information without "doubling up" on events after a page reload.
Persisting Subscribed Calendars
DayFlow identifies subscribed calendars by the presence of a subscription object on the CalendarType.
Default Behavior for Subscriptions:
- Read-only: UI mutations (editing title, changing calendar, deleting) are disabled via
canMutateFromUI(). - Non-draggable: Drag-and-drop actions (moving, resizing) are disabled.
- Hidden Notes: If an event has no description, the "Note" field is hidden in the detail panel to keep the UI clean.
To allow users to modify subscribed events or move them around, you can explicitly override these protections by setting readOnly: false on the calendar object:
const calendar = useCalendarApp({
calendars: [
{
id: 'team-ics',
name: 'Team Calendar',
subscription: {
url: 'https://example.com/calendar.ics',
status: 'ready',
},
// Override default protections:
readOnly: false, // Enables both UI mutations and drag-and-drop
colors: {
/* ... */
},
},
],
});When saving calendar state to a backend, preserve the subscription field and restore it on the next load.
Note: DayFlow does not auto-refresh subscribed feeds. To keep events up to date, implement periodic fetching in your app and update events via
app.addEvent()/app.removeEvent().
Fully Custom Sidebar
You can replace the sidebar with your own component by passing a render function to createSidebarPlugin. It receives real-time calendar state and helper actions.
import {
createSidebarPlugin,
type CalendarSidebarRenderProps,
} from '@dayflow/plugin-sidebar';
const CustomSidebar = ({
app,
calendars,
toggleCalendarVisibility,
toggleAll,
isCollapsed,
setCollapsed,
}: CalendarSidebarRenderProps) => {
if (isCollapsed) {
return (
<div className='p-2'>
<button onClick={() => setCollapsed(false)}>ā</button>
</div>
);
}
return (
<aside className='flex h-full flex-col gap-4 p-4 bg-slate-50 border-r'>
<header className='flex items-center justify-between'>
<h3 className='font-semibold'>My Workspace</h3>
<button onClick={() => setCollapsed(true)}>ā</button>
</header>
<nav className='space-y-1'>
{calendars.map(calendar => (
<label
key={calendar.id}
className='flex items-center gap-2 cursor-pointer'
>
<input
type='checkbox'
checked={calendar.isVisible}
onChange={e =>
toggleCalendarVisibility(calendar.id, e.target.checked)
}
/>
<span
className='w-3 h-3 rounded-full'
style={{ backgroundColor: calendar.colors.lineColor }}
/>
{calendar.name}
</label>
))}
</nav>
<div className='mt-auto pt-4 border-t text-xs text-slate-500'>
Total Events: {app.getEvents().length}
</div>
</aside>
);
};
const sidebarPlugin = createSidebarPlugin({
render: props => <CustomSidebar {...props} />,
});Custom Context Menu
You can replace the default right-click menu for calendar items:
createSidebarPlugin({
renderCalendarContextMenu: (calendar, onClose) => (
<div className='bg-white shadow-lg border rounded p-2'>
<button
onClick={() => {
console.log('Custom action');
onClose();
}}
>
Custom Action for {calendar.name}
</button>
</div>
),
});Custom Create Calendar Dialog
You can replace the default create-calendar dialog:
createSidebarPlugin({
renderCreateCalendarDialog: ({ onCreate, onClose }) => (
<MyCustomDialog onSave={onCreate} onCancel={onClose} />
),
});Custom Sidebar Header
You can replace the default sidebar header (the area with the "Calendars" title and collapse toggle):
createSidebarPlugin({
renderSidebarHeader: ({ isCollapsed, onCollapseToggle }) => (
<div className='flex items-center justify-between p-4 border-b'>
{!isCollapsed && <span className='font-bold text-lg'>My Calendars</span>}
<button
onClick={onCollapseToggle}
className='p-1 hover:bg-slate-100 rounded'
>
{isCollapsed ? 'ā' : 'ā'}
</button>
</div>
),
});