@qijenchen/design-system 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/package.json +93 -0
  2. package/src/README.md +32 -0
  3. package/src/components/Accordion/accordion.tsx +104 -0
  4. package/src/components/Alert/alert.tsx +188 -0
  5. package/src/components/AppShell/_demo-helpers.tsx +198 -0
  6. package/src/components/AppShell/app-shell.tsx +364 -0
  7. package/src/components/AspectRatio/aspect-ratio.tsx +58 -0
  8. package/src/components/Avatar/avatar.tsx +368 -0
  9. package/src/components/Badge/badge.tsx +104 -0
  10. package/src/components/Breadcrumb/breadcrumb.tsx +609 -0
  11. package/src/components/BulkActionBar/bulk-action-bar.tsx +156 -0
  12. package/src/components/Button/button-group.tsx +96 -0
  13. package/src/components/Button/button.tsx +539 -0
  14. package/src/components/Calendar/calendar.tsx +411 -0
  15. package/src/components/Carousel/carousel.tsx +371 -0
  16. package/src/components/Chart/chart.tsx +376 -0
  17. package/src/components/Checkbox/checkbox-group.tsx +94 -0
  18. package/src/components/Checkbox/checkbox.tsx +237 -0
  19. package/src/components/Chip/chip.tsx +359 -0
  20. package/src/components/CircularProgress/circular-progress.tsx +204 -0
  21. package/src/components/Coachmark/coachmark.tsx +255 -0
  22. package/src/components/Combobox/combobox.tsx +826 -0
  23. package/src/components/Command/command.tsx +187 -0
  24. package/src/components/DataTable/active-editor-controller.ts +72 -0
  25. package/src/components/DataTable/cell-registry.tsx +520 -0
  26. package/src/components/DataTable/column-types.ts +180 -0
  27. package/src/components/DataTable/data-table-column-visibility-panel.tsx +261 -0
  28. package/src/components/DataTable/data-table-filter-panel.tsx +813 -0
  29. package/src/components/DataTable/data-table-interaction-layer.tsx +483 -0
  30. package/src/components/DataTable/data-table-sort-manager.tsx +210 -0
  31. package/src/components/DataTable/data-table.css +165 -0
  32. package/src/components/DataTable/data-table.tsx +2924 -0
  33. package/src/components/DataTable/filter-operators.ts +225 -0
  34. package/src/components/DataTable/filter-tree.ts +313 -0
  35. package/src/components/DataTable/lib/column-meta.ts +79 -0
  36. package/src/components/DateGrid/date-grid.tsx +209 -0
  37. package/src/components/DatePicker/date-picker.tsx +1114 -0
  38. package/src/components/DescriptionList/description-list.tsx +141 -0
  39. package/src/components/Dialog/dialog.tsx +267 -0
  40. package/src/components/DropdownMenu/dropdown-menu.tsx +475 -0
  41. package/src/components/Empty/empty.tsx +108 -0
  42. package/src/components/Field/field-context.ts +136 -0
  43. package/src/components/Field/field-types.ts +52 -0
  44. package/src/components/Field/field-wrapper.tsx +348 -0
  45. package/src/components/Field/field.tsx +535 -0
  46. package/src/components/FieldControlGroup/field-control-group.tsx +136 -0
  47. package/src/components/FileItem/file-item.tsx +322 -0
  48. package/src/components/FileUpload/file-upload.tsx +326 -0
  49. package/src/components/FileViewer/file-viewer-types.ts +76 -0
  50. package/src/components/FileViewer/file-viewer.tsx +1065 -0
  51. package/src/components/FileViewer/image-renderer.tsx +256 -0
  52. package/src/components/HoverCard/hover-card.tsx +79 -0
  53. package/src/components/Input/input.tsx +233 -0
  54. package/src/components/LinkInput/link-input.tsx +304 -0
  55. package/src/components/Menu/menu-item.tsx +334 -0
  56. package/src/components/NameCard/name-card.tsx +319 -0
  57. package/src/components/Notice/notice.tsx +196 -0
  58. package/src/components/NumberInput/number-input.tsx +203 -0
  59. package/src/components/OverflowIndicator/overflow-indicator.tsx +156 -0
  60. package/src/components/PeoplePicker/avatar-stack-overflow.ts +100 -0
  61. package/src/components/PeoplePicker/people-picker-helpers.ts +76 -0
  62. package/src/components/PeoplePicker/people-picker.tsx +455 -0
  63. package/src/components/PeoplePicker/person-display.tsx +358 -0
  64. package/src/components/Popover/popover.tsx +183 -0
  65. package/src/components/ProgressBar/progress-bar.tsx +157 -0
  66. package/src/components/README.md +58 -0
  67. package/src/components/RadioGroup/radio-group.tsx +261 -0
  68. package/src/components/Rating/rating.tsx +295 -0
  69. package/src/components/ScrollArea/scroll-area.tsx +110 -0
  70. package/src/components/SegmentedControl/segmented-control.tsx +304 -0
  71. package/src/components/Select/select.tsx +658 -0
  72. package/src/components/SelectMenu/select-menu.tsx +430 -0
  73. package/src/components/SelectionControl/selection-item.tsx +261 -0
  74. package/src/components/Separator/separator.tsx +48 -0
  75. package/src/components/Sheet/sheet.tsx +240 -0
  76. package/src/components/Sidebar/sidebar.tsx +1280 -0
  77. package/src/components/Skeleton/skeleton.tsx +35 -0
  78. package/src/components/Slider/slider.tsx +158 -0
  79. package/src/components/Steps/steps.tsx +850 -0
  80. package/src/components/Switch/switch.tsx +285 -0
  81. package/src/components/Tabs/tabs.tsx +515 -0
  82. package/src/components/Tag/tag.tsx +246 -0
  83. package/src/components/Textarea/textarea.tsx +280 -0
  84. package/src/components/TimePicker/time-columns.tsx +260 -0
  85. package/src/components/TimePicker/time-picker.tsx +419 -0
  86. package/src/components/Toast/toast.tsx +129 -0
  87. package/src/components/Tooltip/tooltip.tsx +68 -0
  88. package/src/components/TreeView/tree-view.tsx +1031 -0
  89. package/src/hooks/use-controllable.ts +40 -0
  90. package/src/hooks/use-is-narrow-viewport.ts +19 -0
  91. package/src/hooks/use-is-touch-device.ts +21 -0
  92. package/src/hooks/use-overflow-items.ts +256 -0
  93. package/src/index.ts +85 -0
  94. package/src/lib/README.md +82 -0
  95. package/src/lib/drag-visual.ts +272 -0
  96. package/src/lib/i18n/README.md +60 -0
  97. package/src/lib/i18n/i18n-context.tsx +129 -0
  98. package/src/lib/multi-select-ordering.ts +61 -0
  99. package/src/lib/utils.ts +93 -0
  100. package/src/patterns/README.md +67 -0
  101. package/src/patterns/element-anatomy/item-anatomy.tsx +744 -0
  102. package/src/patterns/header-canonical/chrome-header.tsx +175 -0
  103. package/src/patterns/header-canonical/header-canonical.css +27 -0
  104. package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +217 -0
  105. package/src/patterns/overlay-surface/overlay-surface.tsx +191 -0
  106. package/src/patterns/resize-handle/resize-handle.tsx +188 -0
  107. package/src/stories-helpers/anatomy/anatomy-utils.tsx +64 -0
  108. package/src/tokens/README.md +53 -0
  109. package/src/tokens/color/primitives.css +429 -0
  110. package/src/tokens/color/semantic.css +539 -0
  111. package/src/tokens/elevation/overlay-geometry.ts +13 -0
  112. package/src/tokens/layoutSpace/layoutSpace.css +36 -0
  113. package/src/tokens/motion/motion.css +30 -0
  114. package/src/tokens/motion/motion.ts +17 -0
  115. package/src/tokens/opacity/opacity.css +23 -0
  116. package/src/tokens/radius/radius.css +19 -0
  117. package/src/tokens/typography/typography.css +118 -0
  118. package/src/tokens/uiSize/icon-size.ts +52 -0
  119. package/src/tokens/uiSize/uiSize.css +125 -0
@@ -0,0 +1,411 @@
1
+ // @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
2
+ import * as React from 'react'
3
+ import { ChevronLeft, ChevronRight, Plus } from 'lucide-react'
4
+ import {
5
+ startOfMonth,
6
+ endOfMonth,
7
+ startOfWeek,
8
+ endOfWeek,
9
+ eachDayOfInterval,
10
+ format,
11
+ isSameMonth,
12
+ isSameDay,
13
+ addMonths,
14
+ subMonths,
15
+ } from 'date-fns'
16
+ import { cn } from '@/lib/utils'
17
+ import { Button } from '@/design-system/components/Button/button'
18
+ import { SegmentedControl, SegmentedControlItem } from '@/design-system/components/SegmentedControl/segmented-control'
19
+
20
+ /**
21
+ * Calendar — 事件檢視 canvas(月 view MVP)
22
+ *
23
+ * 定位:看事件的 page-level canvas,對齊 Notion Calendar / Google Calendar。
24
+ * 完整 spec 見 `event-calendar.spec.md`。
25
+ *
26
+ * ── Layout Family ──
27
+ * 非 4-Family,屬 page-composite(多區塊 Toolbar + Grid + EventTile)。
28
+ *
29
+ * ── MVP scope(本次 session)──
30
+ * - 月 view 完整(toolbar / grid / event tile / today highlight / outside days)
31
+ * - 週 / 日 view 是 tech debt
32
+ * - 拖拉增刪 event 是 tech debt
33
+ *
34
+ * ── 與 DatePicker 的區分 ──
35
+ * DatePicker 是「選日期」form control;Calendar 是「看事件」page canvas。
36
+ * 名字相近但職責完全不同,spec 頂段明示分界。
37
+ */
38
+
39
+ // ── Types ──────────────────────────────────────────────────────────────────
40
+
41
+ export interface CalendarEvent {
42
+ id: string
43
+ title: string
44
+ /** ISO 字串 "YYYY-MM-DD"(all-day)或 "YYYY-MM-DDTHH:mm"(timed) */
45
+ start: string | Date
46
+ end: string | Date
47
+ allDay?: boolean
48
+ /**
49
+ * 事件類別色。值為 DS primitive 色名(blue / green / orange / purple / red / yellow)。
50
+ * 對照 Badge / Tag 的 primitive color variants。
51
+ */
52
+ color?: 'blue' | 'green' | 'orange' | 'purple' | 'red' | 'yellow'
53
+ metadata?: Record<string, unknown>
54
+ }
55
+
56
+ export type CalendarView = 'month' | 'week' | 'day'
57
+
58
+ export interface CalendarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onSelect'> {
59
+ /** 當前 view(MVP 只 'month',其餘 view tech debt) */
60
+ view?: CalendarView
61
+ defaultView?: CalendarView
62
+ onViewChange?: (view: CalendarView) => void
63
+
64
+ /** 聚焦日期(月 view 的那個月) */
65
+ referenceDate?: Date
66
+ defaultReferenceDate?: Date
67
+ onReferenceDateChange?: (date: Date) => void
68
+
69
+ /** 事件資料 */
70
+ events?: CalendarEvent[]
71
+
72
+ /** 點 event tile 回調 */
73
+ onEventClick?: (event: CalendarEvent) => void
74
+ /** 點月 cell 回調(用於新增) */
75
+ onDateClick?: (date: Date) => void
76
+ /** 點新事件 CTA 回調 */
77
+ onCreateEvent?: () => void
78
+
79
+ /** 0 = Sunday, 1 = Monday。預設 0(對齊 Google Calendar 美系預設) */
80
+ weekStartsOn?: 0 | 1
81
+
82
+ /** 自訂 event tile 渲染 */
83
+ renderEventTile?: (event: CalendarEvent) => React.ReactNode
84
+
85
+ /** size(MVP 只 md;lg 為 tech debt) */
86
+ size?: 'md' | 'lg'
87
+ className?: string
88
+
89
+ /** locale(預設 'en-US') */
90
+ locale?: string
91
+
92
+ /** ARIA labels for chrome controls. Override for i18n. */
93
+ prevAriaLabel?: string
94
+ nextAriaLabel?: string
95
+ viewToggleAriaLabel?: string
96
+ todayLabel?: string
97
+ }
98
+
99
+ // ── Event tile color tokens ─────────────────────────────────────────────────
100
+ // 對齊 Tag / Badge 的 primitive color system(見 `color.spec.md`)
101
+
102
+ const EVENT_COLOR_CLASSES: Record<NonNullable<CalendarEvent['color']>, string> = {
103
+ blue: 'bg-[var(--color-blue-1)] text-[var(--color-blue-7)] hover:bg-[var(--color-blue-2)]',
104
+ green: 'bg-[var(--color-green-1)] text-[var(--color-green-7)] hover:bg-[var(--color-green-2)]',
105
+ orange: 'bg-[var(--color-deep-orange-1)] text-[var(--color-deep-orange-7)] hover:bg-[var(--color-deep-orange-2)]',
106
+ purple: 'bg-[var(--color-purple-1)] text-[var(--color-purple-7)] hover:bg-[var(--color-purple-2)]',
107
+ red: 'bg-[var(--color-deep-orange-1)] text-[var(--color-deep-orange-7)] hover:bg-[var(--color-deep-orange-2)]',
108
+ yellow: 'bg-[var(--color-yellow-1)] text-[var(--color-yellow-7)] hover:bg-[var(--color-yellow-2)]',
109
+ }
110
+
111
+ // ── Helpers ────────────────────────────────────────────────────────────────
112
+
113
+ function coerceDate(value: string | Date): Date {
114
+ return value instanceof Date ? value : new Date(value)
115
+ }
116
+
117
+ function eventsOnDate(events: CalendarEvent[], date: Date): CalendarEvent[] {
118
+ return events.filter((e) => {
119
+ const start = coerceDate(e.start)
120
+ const end = coerceDate(e.end)
121
+ // 日期落在 [start, end] 範圍內(日精度)
122
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime()
123
+ const s = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime()
124
+ const eEnd = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime()
125
+ return d >= s && d <= eEnd
126
+ })
127
+ }
128
+
129
+ // ── Component ──────────────────────────────────────────────────────────────
130
+
131
+ const MAX_TILES_PER_CELL = 3
132
+
133
+ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(function Calendar({
134
+ view: viewProp,
135
+ defaultView = 'month',
136
+ onViewChange,
137
+ referenceDate: referenceDateProp,
138
+ defaultReferenceDate,
139
+ onReferenceDateChange,
140
+ events = [],
141
+ onEventClick,
142
+ onDateClick,
143
+ onCreateEvent,
144
+ weekStartsOn = 0,
145
+ renderEventTile,
146
+ size = 'md',
147
+ className,
148
+ locale = 'en-US',
149
+ prevAriaLabel = '上個月', // i18n-allow: DS default; consumer override via prevAriaLabel prop
150
+ nextAriaLabel = '下個月', // i18n-allow: DS default; consumer override via nextAriaLabel prop
151
+ viewToggleAriaLabel = '檢視切換', // i18n-allow: DS default; consumer override via viewToggleAriaLabel prop
152
+ todayLabel = '今天', // i18n-allow: DS default; consumer override via todayLabel prop
153
+ ...props
154
+ }, ref) {
155
+ // Controlled / uncontrolled refDate
156
+ const [internalRef, setInternalRef] = React.useState<Date>(
157
+ defaultReferenceDate ?? new Date(),
158
+ )
159
+ const refDate = referenceDateProp ?? internalRef
160
+ const setRefDate = React.useCallback(
161
+ (next: Date) => {
162
+ if (referenceDateProp === undefined) setInternalRef(next)
163
+ onReferenceDateChange?.(next)
164
+ },
165
+ [referenceDateProp, onReferenceDateChange],
166
+ )
167
+
168
+ // View state(MVP 只用 month,其他 tech debt)
169
+ const [internalView, setInternalView] = React.useState<CalendarView>(defaultView)
170
+ const currentView = viewProp ?? internalView
171
+ const setView = React.useCallback(
172
+ (next: CalendarView) => {
173
+ if (viewProp === undefined) setInternalView(next)
174
+ onViewChange?.(next)
175
+ },
176
+ [viewProp, onViewChange],
177
+ )
178
+
179
+ // Build month grid
180
+ const days = React.useMemo(() => {
181
+ const monthStart = startOfMonth(refDate)
182
+ const monthEnd = endOfMonth(refDate)
183
+ const gridStart = startOfWeek(monthStart, { weekStartsOn })
184
+ const gridEnd = endOfWeek(monthEnd, { weekStartsOn })
185
+ return eachDayOfInterval({ start: gridStart, end: gridEnd })
186
+ }, [refDate, weekStartsOn])
187
+
188
+ const monthTitle = new Intl.DateTimeFormat(locale, {
189
+ year: 'numeric',
190
+ month: 'long',
191
+ }).format(refDate)
192
+
193
+ const today = new Date()
194
+
195
+ const weekdayNames = React.useMemo(() => {
196
+ // 取 `days[0..6]` 的名字(gridStart 開始 7 天,正好一週)
197
+ return days.slice(0, 7).map((d) =>
198
+ new Intl.DateTimeFormat(locale, { weekday: 'short' }).format(d),
199
+ )
200
+ }, [days, locale])
201
+
202
+ const handleToday = () => setRefDate(new Date())
203
+ const handlePrev = () => setRefDate(subMonths(refDate, 1))
204
+ const handleNext = () => setRefDate(addMonths(refDate, 1))
205
+
206
+ return (
207
+ <div
208
+ ref={ref}
209
+ className={cn(
210
+ 'flex flex-col w-full h-full bg-surface rounded-md border border-divider overflow-hidden',
211
+ className,
212
+ )}
213
+ data-view={currentView}
214
+ data-size={size}
215
+ {...props}
216
+ >
217
+ {/* Toolbar:[◀] [今天] [▶] title [view tabs] [+ new] */}
218
+ <div
219
+ className={cn(
220
+ 'flex items-center gap-2 shrink-0 border-b border-divider',
221
+ 'px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]',
222
+ )}
223
+ >
224
+ <div className="flex items-center gap-2">
225
+ <Button
226
+ variant="text"
227
+ size="sm"
228
+ iconOnly
229
+ startIcon={ChevronLeft}
230
+ aria-label={prevAriaLabel}
231
+ onClick={handlePrev}
232
+ />
233
+ <Button variant="tertiary" size="sm" onClick={handleToday}>
234
+ {todayLabel}
235
+ </Button>
236
+ <Button
237
+ variant="text"
238
+ size="sm"
239
+ iconOnly
240
+ startIcon={ChevronRight}
241
+ aria-label={nextAriaLabel}
242
+ onClick={handleNext}
243
+ />
244
+ </div>
245
+
246
+ <h2 className="text-body-lg font-medium text-foreground flex-1 min-w-0 truncate ml-2">
247
+ {monthTitle}
248
+ </h2>
249
+
250
+ {/* View switcher:用 SegmentedControl(互斥多選一 canonical)——
251
+ 對齊 CLAUDE.md「互斥分類選擇走 SegmentedControl,非 checked Button group」原則。
252
+ Button 的 pressed 是「toggle 持續狀態」語意,不適合「單選 view 切換」 */}
253
+ <SegmentedControl
254
+ size="sm"
255
+ value={currentView}
256
+ onValueChange={(v) => setView(v as CalendarView)}
257
+ aria-label={viewToggleAriaLabel}
258
+ >
259
+ <SegmentedControlItem value="day" disabled>日</SegmentedControlItem>
260
+ <SegmentedControlItem value="week" disabled>週</SegmentedControlItem>
261
+ <SegmentedControlItem value="month">月</SegmentedControlItem>
262
+ </SegmentedControl>
263
+
264
+ {onCreateEvent && (
265
+ <Button variant="primary" size="sm" startIcon={Plus} onClick={onCreateEvent}>
266
+ 新事件
267
+ </Button>
268
+ )}
269
+ </div>
270
+
271
+ {/* Weekday header */}
272
+ <div className="grid grid-cols-7 border-b border-divider bg-muted">
273
+ {weekdayNames.map((name, i) => (
274
+ <div
275
+ key={i}
276
+ className="px-2 py-1.5 text-caption text-fg-muted font-normal text-center"
277
+ >
278
+ {name}
279
+ </div>
280
+ ))}
281
+ </div>
282
+
283
+ {/* Month grid:7 cols, ~5-6 rows。a11y(2026-04-25):WAI-ARIA grid 要求 row > gridcell
284
+ 階層,chunk days 7 一組,wrap 成 role='row'(display:contents 保 CSS grid 佈局)。 */}
285
+ <div
286
+ className="grid grid-cols-7 flex-1 min-h-0"
287
+ role="grid"
288
+ aria-label={`月行事曆,${monthTitle}`}
289
+ >
290
+ {Array.from({ length: Math.ceil(days.length / 7) }, (_, rowIdx) => (
291
+ <div key={rowIdx} role="row" style={{ display: 'contents' }}>
292
+ {days.slice(rowIdx * 7, rowIdx * 7 + 7).map((date) => {
293
+ const inMonth = isSameMonth(date, refDate)
294
+ const isToday = isSameDay(date, today)
295
+ const dayEvents = eventsOnDate(events, date)
296
+ const visibleEvents = dayEvents.slice(0, MAX_TILES_PER_CELL)
297
+ const overflowCount = dayEvents.length - visibleEvents.length
298
+
299
+ return (
300
+ <button
301
+ key={date.toISOString()}
302
+ type="button"
303
+ role="gridcell"
304
+ aria-label={`${format(date, 'yyyy-MM-dd')},${dayEvents.length} 個事件`}
305
+ onClick={() => onDateClick?.(date)}
306
+ className={cn(
307
+ 'flex flex-col gap-1 min-h-28 p-1.5 text-left',
308
+ 'border-r border-b border-divider last:border-r-0',
309
+ '[&:nth-child(7n)]:border-r-0',
310
+ 'hover:bg-neutral-hover transition-colors',
311
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
312
+ !inMonth && 'bg-muted',
313
+ )}
314
+ >
315
+ {/* Date number header */}
316
+ <div className="flex items-start justify-end">
317
+ {isToday ? (
318
+ <span className="inline-flex items-center justify-center min-w-6 h-6 px-2 rounded-full bg-primary text-on-emphasis text-body font-medium">
319
+ {format(date, 'd')}
320
+ </span>
321
+ ) : (
322
+ <span
323
+ className={cn(
324
+ 'text-body font-medium',
325
+ !inMonth && 'text-fg-disabled',
326
+ )}
327
+ >
328
+ {format(date, 'd')}
329
+ </span>
330
+ )}
331
+ </div>
332
+
333
+ {/* Event tiles */}
334
+ <div className="flex flex-col gap-0.5 min-h-0">
335
+ {visibleEvents.map((event) => {
336
+ const colorClass = EVENT_COLOR_CLASSES[event.color ?? 'blue']
337
+ if (renderEventTile) {
338
+ return (
339
+ <div
340
+ key={event.id}
341
+ onClick={(e) => {
342
+ e.stopPropagation()
343
+ onEventClick?.(event)
344
+ }}
345
+ >
346
+ {renderEventTile(event)}
347
+ </div>
348
+ )
349
+ }
350
+ return (
351
+ <div
352
+ key={event.id}
353
+ role="button"
354
+ tabIndex={0}
355
+ onClick={(e) => {
356
+ e.stopPropagation()
357
+ onEventClick?.(event)
358
+ }}
359
+ onKeyDown={(e) => {
360
+ if (e.key === 'Enter' || e.key === ' ') {
361
+ e.preventDefault()
362
+ onEventClick?.(event)
363
+ }
364
+ }}
365
+ aria-label={`事件:${event.title}`}
366
+ className={cn(
367
+ 'rounded-md px-1.5 py-0.5 text-caption truncate cursor-pointer transition-colors',
368
+ colorClass,
369
+ )}
370
+ >
371
+ {event.title}
372
+ </div>
373
+ )
374
+ })}
375
+ {overflowCount > 0 && (
376
+ <div className="text-caption text-fg-muted px-1.5">
377
+ +{overflowCount} more
378
+ </div>
379
+ )}
380
+ </div>
381
+ </button>
382
+ )
383
+ })}
384
+ </div>
385
+ ))}
386
+ </div>
387
+ </div>
388
+ )
389
+ })
390
+ Calendar.displayName = "Calendar"
391
+
392
+ // Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
393
+ // Phase 2 fill needed: purpose descriptions + when rationale + world-class refs
394
+ export const calendarMeta = {
395
+ component: 'Calendar',
396
+ family: null, // non-family composite / overlay / layout
397
+ variants: {
398
+
399
+ },
400
+ sizes: {
401
+
402
+ },
403
+ states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
404
+ tokens: {
405
+ bg: ['bg-muted', 'bg-neutral-hover', 'bg-primary', 'bg-surface'],
406
+ fg: ['text-fg-disabled', 'text-fg-muted', 'text-foreground'],
407
+ ring: ['ring-ring'],
408
+ },
409
+ } as const
410
+
411
+ export { Calendar }