cal-webcomponents
v1.0.2
Published
Readme
cal-components
面向 AI Agents 的日历 Web Components 库:让 Agent 在静态 HTML 中以纯声明式的方式快速嵌入月历 / 周历 / 日历交互,零 JS 即可获得完整可用的日程视图。典型场景:旅行计划安排、行程单、课程表、会议日程等 Agent 直接输出 HTML 的场合。
基于 Lit 构建,产物为原生 Custom Elements。使用方完全不需要 node 或任何构建工具:一个 <script> 标签从 unpkg 引入(依赖已全部打包进产物),即可在任意静态 HTML 文件中使用,双击打开本地文件也能运行。
为什么适合 AI Agent
- 纯 HTML 声明式:数据就是
<cal-event>标签,Agent 生成文本即得可交互日历,无需编写任何 JavaScript。 - 业务字段自由扩展:任意
data-*属性(如data-location、data-order)自动进入模板通道,Agent 可按场景自定义字段。 - 详情卡片也是 HTML:
<template slot="detail">+{{占位符}}插值即完成点击详情弹层,样式完全由页面 CSS 控制。 - 健壮的时间解析:全天事件、跨夜酒店(≥24h)、跨午夜行程(21:00–08:00)等旅行场景的时间语义内置处理。
特性
- 三种视图:月(
month)/ 周(week,可见日数 1–14 可调)/ 日(day),切换带淡入淡出过渡 - 高度自适应容器:周/日视图默认完整显示 24 小时、月视图默认整 6 周行填满可视区(可用
hour-height/start-hour/end-hour/row-height覆盖恢复固定高度 + 滚动) - 无限滚动:月视图纵向周行流、周视图时间+日期双向无限、日视图日块堆叠(基于 lenis,滚轮/触摸惯性滑动)
- 重叠事件自动分列布局;跨天事件按天分段并裁剪圆角
- 全天泳道:纯日期或时长 ≥24h 的事件渲染为跨天横条
- 点击事件高亮 + 模板驱动的详情弹层(自动避让边界,Esc/点击空白关闭)
- 月视图
+n展开当日列表弹层 - 丰富的定制口:CSS 变量、
itemRenderer、timeFormatter、cal-event-click/cal-day-click事件
快速开始(零构建)
使用方无需安装 node、无需打包,在 HTML 的 <head> 中加一行 <script> 即可(UMD 产物,加载时自动注册 <cal-calendar> / <cal-event>):
<script src="https://unpkg.com/cal-webcomponents@1"></script>建议锁定主版本号(
@1)避免意外升级;https://unpkg.com/cal-webcomponents默认解析到 UMD 产物。 如需 ES Module:<script type="module" src="https://unpkg.com/cal-webcomponents/dist/cal-webcomponents.js"></script>。
一个完整可运行的单文件示例(保存为 .html 直接用浏览器打开):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>日本关西 5 日游</title>
<script src="https://unpkg.com/cal-webcomponents@1"></script>
<style>
body {
margin: 0;
padding: 20px;
background: #f2f3f5;
font-family: -apple-system, 'PingFang SC', sans-serif;
}
cal-calendar {
--cal-height: 80vh;
}
/* 详情卡片样式:slot 模板内容在 Shadow DOM 外渲染,CSS 完全由页面控制 */
.dcard {
padding: 12px 14px;
}
.dcard-t {
font-size: 14px;
font-weight: 600;
}
.dcard-r {
margin-top: 5px;
color: #4e5969;
}
.dcard-r.order {
color: #2f6fe0;
}
</style>
</head>
<body>
<cal-calendar type="month" initial-date="2026-08-10">
<!-- 点击事件的详情弹层模板(可选,纯 HTML + 占位符) -->
<template slot="detail">
<div class="dcard">
<div class="dcard-t">{{data.icon}} {{title}}</div>
<div class="dcard-r">🕐 {{time}}</div>
<div class="dcard-r">📍 {{data.location}}</div>
<div class="dcard-r order">订单号:{{data.order}}</div>
</div>
</template>
<cal-event
start="2026-08-10 08:55"
end="2026-08-10 12:40"
title="深圳 → 大阪 CA133"
data-icon="✈️"
color="#2f6fe0"
data-location="宝安 T3 → 关西 T1"
data-order="FL2026081001"
></cal-event>
<cal-event
start="2026-08-10 15:00"
end="2026-08-12 11:00"
title="大阪难波酒店"
data-icon="🏨"
color="#7c5ce0"
data-location="难波站步行 5 分钟"
data-order="HT2026081001"
></cal-event>
<cal-event
start="2026-08-10 18:00"
end="2026-08-10 20:00"
title="道顿堀晚餐"
data-icon="🍜"
color="#e8483f"
data-location="蟹道乐 道顿堀本店"
></cal-event>
</cal-calendar>
</body>
</html><cal-calendar> 属性
| 属性 | 类型 | 默认值 | 说明 |
| -------------- | ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type | day \| week \| month | month | 视图类型,可动态切换 |
| initial-date | string | 今天 | 初始定位日期,如 "2026-08-10" |
| days | number | 7 | 周视图可见日数(≥1) |
| scroll-x | boolean | true | 周视图是否允许横向滚动 |
| hour-height | number | - | 周/日视图每小时高度(px,固定高度模式);不设置时按 start-hour/end-hour 时间窗均分容器高度 |
| start-hour | number | 0 | 周/日视图时间窗起始小时(0–23);与 end-hour 的差值即可视区显示的小时数(差值 <24 时可上下滚动),同时作为初始滚动锚点(未设置时:今天定位到当前时间前 1 小时,否则 8 点) |
| end-hour | number | 24 | 周/日视图时间窗结束小时(1–24) |
| row-height | number | - | 月视图行高(px);不设置时按容器高度均分 6 行自适应 |
start-hour / end-hour 直接声明默认时间窗口。例如旅行规划的周日历,默认显示 8:00–22:00、早晚仍可滚动查看:
<cal-calendar
type="week"
start-hour="8"
end-hour="22"
initial-date="2026-08-10"
>
...
</cal-calendar>说明:start-hour/end-hour 不裁剪时间轴——时间格始终是完整 24 小时,只是默认窗口落在该范围。都不设置时默认 0–24(一整天填满可视区,无纵向滚动)。优先级:hour-height(固定像素)> start-hour/end-hour(时间窗均分)。
参数按视图适用范围
属性管布局/行为,CSS 变量只管外观;不适用当前视图的属性会被安全忽略(不报错):
| 参数 | month | week | day |
| ----------------------------------------- | ----- | ---- | --- |
| type / initial-date | ✓ | ✓ | ✓ |
| days | - | ✓ | - |
| scroll-x | - | ✓ | - |
| hour-height / start-hour / end-hour | - | ✓ | ✓ |
| row-height | ✓ | - | - |
<cal-event> 属性
<cal-event> 仅作数据载体,自身不渲染。
| 属性 | 说明 |
| -------- | --------------------------------------------------------------------------- |
| start | 开始时间:"2026-08-10 08:55" 或 "2026-08-10"(全天) |
| end | 结束时间;省略时 timed 按 1 小时、全天按 1 天;纯日期的 end 包含当天 |
| title | 事件标题 |
| color | 主题色(如 #e8483f),自动派生背景/前景色 |
| data-* | 任意业务扩展字段(icon/location/desc/order…),模板中以 {{data.xxx}} 引用 |
时间语义(旅行场景重点):
- 纯日期输入,或时长 ≥24h(如酒店 15:00 入住~隔日 11:00 退房)→ 渲染到全天泳道的跨天横条
- 跨午夜但 <24h(如
21:00–08:00的跨夜发版)→ 在时间格内按天分段渲染,段边缘自动去圆角 end早于start时自动兜底为 1 小时
模板与占位符
在 <cal-calendar> 内放置 <template> 即可定制 UI,全部为声明式 HTML,插值自动 HTML 转义:
| 模板 | 触发时机 |
| ---------------------------- | ---------------------------------------------------- |
| <template slot="detail"> | 点击事件弹出的详情卡片 |
| <template slot="day-item"> | 月视图 +n 弹层中当日事件的行内容 |
| <template slot="item"> | 日历格子内事件项的内容(低于 itemRenderer 优先级) |
可用占位符:
- 内核字段:
{{title}}{{color}}{{time}}(长时间格式){{timeShort}}(简短){{date}}{{weekday}}{{start}}{{end}} - 扩展数据:
{{data.xxx}},对应<cal-event>的data-xxx属性
弹层渲染在 light DOM(挂在 document.body,position: fixed),模板内容的样式完全由页面普通 CSS 控制(不受 Shadow DOM 隔离影响);弹层外壳可用 --cal-popover-width / --cal-popover-radius / --cal-popover-bg / --cal-popover-shadow 等变量定制(定义在 cal-calendar 上,打开弹层时自动继承过去)。
JS API(可选进阶)
声明式用法已覆盖大多数场景;需要编程控制时:
const cal = document.querySelector('cal-calendar')!;
cal.jumpTo('2026-08-01'); // 跳转到指定日期
cal.goToday(); // 回到今天
// 自定义事件项渲染(返回 HTML 字符串或 lit TemplateResult,返回 null 回退默认)
cal.itemRenderer = (ev, kind) =>
kind === 'block' ? `<b>${ev.data.icon} ${ev.title}</b>` : null;
// 自定义时间格式(kind: 'long' 用于 {{time}},'short' 用于 {{timeShort}})
cal.timeFormatter = (ev, kind) => '...';
// 调用方自行实现详情 UI 时:监听事件并用 anchor 定位,setActiveEvent 同步高亮
cal.addEventListener('cal-event-click', (e) => {
e.preventDefault(); // 抑制内置弹层
const { event, anchor, kind } = e.detail;
// ...打开自己的气泡/抽屉
cal.setActiveEvent(event); // 关闭时传 null
});
cal.addEventListener('cal-day-click', (e) => {
const { date, events, anchor } = e.detail;
});事件均 bubbles + composed + cancelable。itemRenderer 的 kind 取值:chip(月格子)/ bar(全天泳道)/ block(时间格)。
样式定制(CSS 变量)
cal-calendar {
--cal-height: 72vh; /* 组件高度(默认 640px) */
--cal-accent: #2f6fe0; /* 强调色:今天、高亮、当前时间线 */
--cal-bg: #ffffff;
--cal-border: #e5e6eb;
--cal-radius: 10px;
--cal-text: #1f2329;
--cal-sub: #86909c; /* 次级文字 */
--cal-grid: #f2f3f5; /* 网格线 */
--cal-event-bg: #e8f0fe; /* 无 color 事件的默认底色 */
--cal-event-text: #1d5fd4;
--cal-gutter: 56px; /* 时间轴左侧宽度 */
--cal-popover-width: 280px;
--cal-font: 14px/1.4...; /* 字体 */
}事件未指定 color 时使用默认配色;指定后按 color-mix 自动派生浅色背景与深色文字。
项目结构
src/
index.ts 入口:注册 custom elements 并导出 API
cal-calendar.ts 宿主组件:属性、事件收集、视图分发、弹层
cal-event.ts 事件数据载体元素
views/ 视图控制器(month-view / week-view / day-view)
event-utils.ts cal-event 解析、时间格式化、重叠分列布局
event-templates.ts 模板插值(占位符 + HTML 转义)
date-utils.ts 日期工具
calendar-styles.ts 组件样式(含全部 CSS 变量默认值)开发(组件库贡献者)
仅开发本组件库时需要 node 环境;使用组件无需此步骤。
pnpm install
pnpm dev # 打开 showcase(index.html,含旅行计划 / 工作安排 / 课程表 / 自定义渲染 4 个案例)
pnpm build # 产出 dist/cal-webcomponents.js (ES) 与 dist/cal-webcomponents.umd.js (UMD)产物为自包含单文件(lit、lenis 已打包入内),发布 npm 后使用方经 unpkg / jsDelivr 等 CDN 直接引用:
- UMD:
https://unpkg.com/cal-webcomponents(或.../dist/cal-webcomponents.umd.js) - ES Module:
https://unpkg.com/cal-webcomponents/dist/cal-webcomponents.js
License
MIT
