Skies.ui
元件 (Components)

Calendar 日曆

Skies.ui Calendar:純 React 日曆,單日/範圍/活動/行程表,無需日期函式庫;API 與範例。

Calendar 日曆

四種開箱即用的日曆變種,無需任何外部日期函式庫:

  • Calendar — 單日選擇
  • CalendarRange — 日期範圍選擇
  • CalendarEvents — 全頁活動日曆
  • CalendarAgenda — 行程表小工具

單日選擇

2026年4月
週日
週一
週二
週三
週四
週五
週六
import { Calendar } from "@skies-ui/ui";

export function Example() {
  return <Calendar />;
}

受控單日選擇

"use client";

import { useState } from "react";
import { Calendar } from "@skies-ui/ui";

export function ControlledCalendar() {
  const [date, setDate] = useState<Date | undefined>();
  return (
    <div>
      <Calendar value={date} onValueChange={setDate} />
      <p>{date ? date.toLocaleDateString("zh-TW") : "未選擇"}</p>
    </div>
  );
}

日期範圍選擇

第一次點擊設定起始日,第二次點擊設定結束日,hover 時有範圍預覽。

2026年4月
週日
週一
週二
週三
週四
週五
週六
import { CalendarRange } from "@skies-ui/ui";
import type { DateRange } from "@skies-ui/ui";

export function RangeExample() {
  const [range, setRange] = useState<DateRange>({ from: undefined, to: undefined });
  return <CalendarRange value={range} onValueChange={setRange} />;
}

全頁活動日曆

CalendarEvents 顯示整月格狀視圖,每天格子可顯示多個帶顏色的事件條,超過 3 個會顯示「+N 個」。

2026年4月
週日
週一
週二
週三
週四
週五
週六
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
import { CalendarEvents } from "@skies-ui/ui";
import type { CalendarEventItem } from "@skies-ui/ui";

const events: CalendarEventItem[] = [
  { id: "1", title: "設計評審", date: new Date(2026, 3, 3), color: "blue" },
  { id: "2", title: "前端週會", date: new Date(2026, 3, 3), color: "green" },
  { id: "3", title: "產品發布", date: new Date(2026, 3, 10), color: "red" },
];

export function EventCalendarExample() {
  return (
    <CalendarEvents
      events={events}
      onDateClick={(date) => console.log("clicked:", date)}
      onEventClick={(ev) => console.log("event:", ev)}
    />
  );
}

行程表小工具

CalendarAgenda 列出接下來 N 天(預設 14)有行程的日期,適合放在 Dashboard 側欄。

4月10日週五

4月12日週日

4月16日週四

import { CalendarAgenda } from "@skies-ui/ui";
import type { CalendarEventItem } from "@skies-ui/ui";

const events: CalendarEventItem[] = [
  { id: "1", title: "設計評審", date: new Date(2026, 3, 7), color: "blue" },
  { id: "2", title: "產品發布", date: new Date(2026, 3, 9), color: "red" },
];

export function AgendaExample() {
  return <CalendarAgenda events={events} days={14} />;
}

限制可選日期

<Calendar
  minDate={new Date(2026, 0, 1)}
  maxDate={new Date(2026, 11, 31)}
  disabledDates={[new Date(2026, 3, 1), new Date(2026, 3, 5)]}
/>

自訂語言與週起始日

locale 使用 BCP 47 字串(例如 zh-TWen-US),會影響星期縮寫月份標題與行程表側欄日期格式。

weekStartsOn0 = 第一欄為週日,1 = 週一)可省略;省略時在支援的環境會依 Intl.LocaleweekInfo 推斷地區慣例,否則為 0

導覽按鈕的 aria-label、行程表的「今天」、空狀態與「+N 則事件」等字串,會依 localezh* 為繁中風格預設,其餘為英文)顯示;需要微調時傳 messages

<Calendar locale="en-US" />

<Calendar locale="de-DE" />
{/* weekStartsOn 未指定時,多為週一起排 */}

<Calendar locale="zh-TW" weekStartsOn={1} />

<CalendarAgenda
  locale="zh-TW"
  messages={{
    today: "今日",
    noEventsInDays: (d) => `未來 ${d} 天暫無行程`,
  }}
/>

星期列:單字「日一二…」或跟著 locale

星期標題列的是 CalendarCalendarRangeCalendarEvents(行程表 CalendarAgenda 沒有這一列)。

weekdayStyle行為
locale(預設)locale 相同,使用 Intl 的星期縮寫(如 zh-TW 可能是「週一…」這類格式,依引擎而定)。
zh-abbr固定顯示單字 日、一、二、三、四、五、六欄順與 weekStartsOn 一致(週日開頭 → 日一二三四五六;週一開頭 → 一二三四五六日)。

即使 localeen-US,仍可設 weekdayStyle="zh-abbr" 讓表頭用中文單字。

<Calendar weekdayStyle="zh-abbr" />
<CalendarRange weekdayStyle="zh-abbr" weekStartsOn={1} />

Next.js 與 SSR

為什麼要注意: App Router 會先在伺服器產出 HTML,再在瀏覽器做 hydration。若伺服器算出的 locale 和瀏覽器不一致(例如伺服器用預設 zh-TW、客戶端才讀 navigator.language 變成 en-US),星期縮寫等文字可能和首屏 HTML 對不起來,React 會警告 hydration mismatch

建議做法:

  1. 讓「語系」在伺服器與客戶端都能推出同一個值——常見來源:/[locale]/... 動態路由的 params.locale、cookie(伺服器在 cookies()、客戶端讀同一個名稱)、或 layout 從設定注入的常數。
  2. navigator 僅在瀏覽器存在,在 Server Component 裡沒有 navigator.language;不要只在 useEffect 裡才第一次設定 locale 並改畫面,否則首屏與 hydration 仍可能不一致。若語系只存在客戶端,可整塊日曆放在 Client Component,但 locale 仍應來自與首屏一致的來源(例如已由 middleware 寫入 cookie、再在同一份 props 傳入)。

App Router 範例(語系來自路由): 假設頁面路徑為 app/[locale]/page.tsx,把 locale 往下傳給標示了 "use client" 的元件即可;伺服器與客戶端第一次 render 都會拿到同一個 params.locale

// app/[locale]/calendar-section.tsx
"use client";

import { Calendar } from "@skies-ui/ui";

export function CalendarSection({ locale }: { locale: string }) {
  return <Calendar locale={locale} />;
}
// app/[locale]/page.tsx(Server Component 範例)
import { CalendarSection } from "./calendar-section";

export default async function Page({
  params,
}: {
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  return <CalendarSection locale={locale} />;
}

關於 Intl 星期與月份字樣由執行環境的 ICU 決定,本機 Node 與使用者瀏覽器版本不同時,同一 locale 也可能有極小差異,一般可接受。

API 摘要

Calendar / CalendarRange

Prop型別說明
valueDate | DateRange受控選取值
onValueChangefn變更回呼
defaultValueDate | DateRange非受控預設值
minDateDate最早可選日期
maxDateDate最晚可選日期
disabledDatesDate[]指定停用的日期
localestring語言(預設 "zh-TW"
weekStartsOn0 | 1選填;週起始。省略時依 locale 自動推斷(見上文)
weekdayStyleCalendarWeekdayStyle選填;星期列用 Intllocale)或單字日一二…(zh-abbr
messagesPartial<CalendarMessages>選填;覆寫導覽 aria-label 等文案

CalendarEvents

Prop型別說明
eventsCalendarEventItem[]事件清單
onEventClick(ev: CalendarEventItem) => void點擊事件回呼
onDateClick(date: Date) => void點擊日期格回呼
localestring語言
weekStartsOn0 | 1選填;週起始(省略時自動推斷)
weekdayStyleCalendarWeekdayStyle選填;同上
messagesPartial<CalendarMessages>選填

CalendarAgenda

Prop型別說明
eventsCalendarEventItem[]事件清單
daysnumber顯示未來幾天(預設 14
onEventClick(ev: CalendarEventItem) => void點擊事件回呼
localestring語言
messagesPartial<CalendarMessages>選填

CalendarMessages(摘要)

欄位說明
previousMonth / nextMonth上/下月按鈕 aria-label
today行程表「今天」
noEventsInDays(days)無行程時的空狀態
moreEvents(count)活動格內「+N …」

亦匯出 defaultCalendarMessages(locale)getDefaultWeekStartsOn(locale) 供進階用法或測試。

CalendarWeekdayStyle

說明
locale預設;星期列依 localeIntl
zh-abbr星期列為 日一二三四五六(順序依 weekStartsOn)。

CalendarEventItem

欄位型別說明
idstring唯一識別碼
titlestring事件標題
dateDate事件日期(或起始日)
endDateDate選用,多日事件的結束日
color"default" | "blue" | "green" | "red" | "orange" | "purple"事件顏色

On this page