侧边栏插件
侧边栏插件为 DayFlow 日历添加日历管理侧边栏。包含用于日期导航的迷你日历、带可见性切换的日历列表,以及完整的日历 CRUD 操作(创建、重命名、更改颜色、合并、删除、导入/导出)。
安装
安装插件包:
npm install @dayflow/plugin-sidebar
pnpm add @dayflow/plugin-sidebar
yarn add @dayflow/plugin-sidebar
bun add @dayflow/plugin-sidebar
使用
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: [
/* 视图 */
],
plugins: [sidebarPlugin],
});
return <DayFlowCalendar calendar={calendar} />;
}属性参数
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
width | number | string | '240px' | 侧边栏宽度(如 280 或 '20rem') |
miniWidth | string | '50px' | 折叠状态下的侧边栏宽度 |
initialCollapsed | boolean | false | 是否以折叠状态启动侧边栏 |
createCalendarMode | 'inline' | 'modal' | 'inline' | 创建日历表单的显示方式 |
colorPickerMode | 'blossom' | 'default' | 'default' | 使用的颜色选择器组件 |
onSubscribeCalendar | (calendar, events) => Promise<void> | undefined | 成功订阅新日历后的回调 |
onLoadSubscription | (calendar) => Promise<void> | undefined | 针对现有订阅的自定义加载逻辑 |
onReorder | (calendars: CalendarType[]) => void | Promise<void> | undefined | 日历排序变更后的回调 |
render | (props: CalendarSidebarRenderProps) => TNode | undefined | 完全自定义侧边栏 UI |
renderSidebarHeader | (args: SidebarHeaderSlotArgs) => TNode | undefined | 自定义侧边栏头部渲染器 |
renderCalendarContextMenu | (calendar, onClose) => TNode | undefined | 日历项的自定义右键菜单渲染器 |
renderCreateCalendarDialog | (props) => TNode | undefined | 自定义创建日历对话框渲染器 |
功能
侧边栏插件提供包含颜色点、可见性开关和折叠按钮的侧栏布局。它会自动与日历状态同步,无需额外代码。同时内置了日历管理与右键菜单功能。
内置功能包括:
- 迷你日历 - 用于快速日期导航的紧凑月视图
- 日历列表 - 通过彩色复选框切换各个日历的可见性
- 创建日历 - 使用自定义名称和颜色添加新日历
- 重命名 / 更改颜色 - 右键点击日历以重命名或更改颜色
- 日历排序 - 通过在列表中拖拽日历来调整排序。使用
onReorder回调将新顺序持久化到后端。 - 合并日历 - 将一个日历的所有事件移动到另一个日历
- 删除日历 - 带确认步骤的日历删除(也可先合并)
- 导入 / 导出 - 导入
.ics文件或将日历导出为.ics - 订阅日历 - 通过 ICS URL 订阅远程日历,已订阅的日历在侧边栏列表中会显示标识图标
在线示例
这一页用两个示例展示侧边栏的不同形态:一个是开箱即用的默认布局,另一个是使用框架(如 React)完全定制的版本。
可以尝试
- 可见性切换:在两个示例中切换日历可见性,观察 UI 如何瞬时同步
- 拖拽同步:拖拽或创建事件,侧栏的"即将到来"卡片会实时更新
- 收起侧栏:折叠侧栏,看看在周视图等高密度布局里如何回收更多空间
订阅日历
侧边栏内置了订阅远程 ICS 日历源的功能。在侧边栏的日历列表或空白区域右键点击打开菜单,选择订阅日历,然后输入任意公开的 .ics 地址即可。
DayFlow 会自动获取该文件、解析事件并创建一个新日历。已订阅的日历在侧边栏列表中会显示一个小图标,以便与本地创建的日历区分。
自动加载与去重
如果你在初始配置中提供了带有 subscription.url 的日历,侧边栏插件会在挂载时自动获取最新事件。
为了防止将本地缓存的事件与新鲜的订阅数据结合时出现视觉重复,DayFlow 实现了基于 ID 的去重机制:
- 如果来自订阅的事件与核心存储中已有的事件具有相同的
id,订阅版本的事件将优先显示。 - 这确保了你的 UI 始终显示最新信息,而不会在页面刷新后出现“双重”事件。
持久化订阅日历
DayFlow 通过 CalendarType 对象上是否存在 subscription 字段来识别订阅日历。
订阅日历的默认行为:
- 只读:UI 修改操作(编辑标题、更换日历、删除)会通过
canMutateFromUI()禁用。 - 不可拖拽:禁用拖放操作(移动、调整大小)。
- 隐藏备注:如果事件没有描述,详情面板中的“备注”字段将被隐藏,以保持界面整洁。
如果您希望允许用户修改订阅事件或移动它们,可以通过在日历对象上设置 readOnly: false 来显式覆盖这些默认保护:
const calendar = useCalendarApp({
calendars: [
{
id: 'team-ics',
name: '团队日历',
subscription: {
url: 'https://example.com/calendar.ics',
status: 'ready',
},
// 覆盖默认保护:
readOnly: false, // 同时启用 UI 修改和拖放操作
colors: {
/* ... */
},
},
],
});将日历状态保存到后端时,请保留 subscription 字段,并在下次加载时恢复。
},
],
});
> **注意:** DayFlow 不会自动刷新已订阅的日历源。如需保持事件最新,请在应用中自行实现定期拉取逻辑,并通过 `app.addEvent()` / `app.removeEvent()` 更新事件。
## 完全自定义侧边栏
你可以通过向 `createSidebarPlugin` 传入 `render` 函数来完全替换侧边栏。它会接收实时的日历状态和辅助操作。
```tsx
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'>我的工作区</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'>
当前日期:{app.getCurrentDate().toLocaleDateString()}
</div>
</aside>
);
};
const sidebarPlugin = createSidebarPlugin({
render: props => <CustomSidebar {...props} />,
});自定义右键菜单
可以替换日历项的默认右键菜单:
createSidebarPlugin({
renderCalendarContextMenu: (calendar, onClose) => (
<div className='bg-white shadow-lg border rounded p-2'>
<button
onClick={() => {
console.log('自定义操作');
onClose();
}}
>
针对 {calendar.name} 的自定义操作
</button>
</div>
),
});自定义创建日历对话框
可以替换默认的创建日历对话框:
createSidebarPlugin({
renderCreateCalendarDialog: ({ onCreate, onClose }) => (
<MyCustomDialog onSave={onCreate} onCancel={onClose} />
),
});自定义侧边栏头部
可以替换默认的侧边栏头部(包含“日历”标题和折叠按钮的区域):
createSidebarPlugin({
renderSidebarHeader: ({ isCollapsed, onCollapseToggle }) => (
<div className='flex items-center justify-between p-4 border-b'>
{!isCollapsed && <span className='font-bold text-lg'>我的日历</span>}
<button
onClick={onCollapseToggle}
className='p-1 hover:bg-slate-100 rounded'
>
{isCollapsed ? '→' : '←'}
</button>
</div>
),
});