@spatika/react 1.1.0 → 1.3.0

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 (311) hide show
  1. package/AGENTS.md +71 -0
  2. package/README.md +4 -3
  3. package/dist/composites/Card.d.ts +7 -1
  4. package/dist/composites/Card.d.ts.map +1 -1
  5. package/dist/composites/Card.js +10 -0
  6. package/dist/composites/ChartDataGrid.d.ts +33 -0
  7. package/dist/composites/ChartDataGrid.d.ts.map +1 -0
  8. package/dist/composites/ChartDataGrid.js +77 -0
  9. package/dist/composites/CoverHero.d.ts +1 -1
  10. package/dist/composites/CoverHero.d.ts.map +1 -1
  11. package/dist/composites/CoverHero.js +2 -2
  12. package/dist/composites/EntityMediaCard.d.ts +1 -1
  13. package/dist/composites/EntityMediaCard.d.ts.map +1 -1
  14. package/dist/composites/EntityMediaCard.js +12 -4
  15. package/dist/composites/EventCalendar.d.ts +72 -0
  16. package/dist/composites/EventCalendar.d.ts.map +1 -0
  17. package/dist/composites/EventCalendar.js +371 -0
  18. package/dist/composites/EventEditor.d.ts +22 -0
  19. package/dist/composites/EventEditor.d.ts.map +1 -0
  20. package/dist/composites/EventEditor.js +86 -0
  21. package/dist/composites/EventTimeline.d.ts +29 -0
  22. package/dist/composites/EventTimeline.d.ts.map +1 -0
  23. package/dist/composites/EventTimeline.js +186 -0
  24. package/dist/composites/MapChart.d.ts +24 -0
  25. package/dist/composites/MapChart.d.ts.map +1 -0
  26. package/dist/composites/MapChart.js +58 -0
  27. package/dist/composites/ProfileHero.d.ts +6 -4
  28. package/dist/composites/ProfileHero.d.ts.map +1 -1
  29. package/dist/composites/ProfileHero.js +8 -8
  30. package/dist/composites/ScatterWebGL.d.ts +22 -0
  31. package/dist/composites/ScatterWebGL.d.ts.map +1 -0
  32. package/dist/composites/ScatterWebGL.js +45 -0
  33. package/dist/composites/SiteFooter.d.ts +1 -1
  34. package/dist/composites/SiteFooter.js +1 -1
  35. package/dist/composites/SiteNav.d.ts +7 -2
  36. package/dist/composites/SiteNav.d.ts.map +1 -1
  37. package/dist/composites/SiteNav.js +9 -5
  38. package/dist/composites/StatCard.d.ts +2 -1
  39. package/dist/composites/StatCard.d.ts.map +1 -1
  40. package/dist/composites/StatCard.js +3 -3
  41. package/dist/composites/StatusDot.d.ts +1 -1
  42. package/dist/composites/Wordmark.d.ts +2 -2
  43. package/dist/composites/Wordmark.d.ts.map +1 -1
  44. package/dist/composites/cartesian-charts.d.ts +138 -0
  45. package/dist/composites/cartesian-charts.d.ts.map +1 -0
  46. package/dist/composites/cartesian-charts.js +401 -0
  47. package/dist/composites/chart-composition.d.ts +139 -0
  48. package/dist/composites/chart-composition.d.ts.map +1 -0
  49. package/dist/composites/chart-composition.js +486 -0
  50. package/dist/composites/chart-ui.d.ts +83 -0
  51. package/dist/composites/chart-ui.d.ts.map +1 -0
  52. package/dist/composites/chart-ui.js +108 -0
  53. package/dist/composites/charts-3d.d.ts +37 -0
  54. package/dist/composites/charts-3d.d.ts.map +1 -0
  55. package/dist/composites/charts-3d.js +121 -0
  56. package/dist/composites/flow-charts.d.ts +89 -0
  57. package/dist/composites/flow-charts.d.ts.map +1 -0
  58. package/dist/composites/flow-charts.js +186 -0
  59. package/dist/composites/radial-charts.d.ts +109 -0
  60. package/dist/composites/radial-charts.d.ts.map +1 -0
  61. package/dist/composites/radial-charts.js +176 -0
  62. package/dist/composites/scheduler-ui.d.ts +44 -0
  63. package/dist/composites/scheduler-ui.d.ts.map +1 -0
  64. package/dist/composites/scheduler-ui.js +71 -0
  65. package/dist/index.d.ts +68 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +58 -7
  68. package/dist/lib/chart-export.d.ts +10 -0
  69. package/dist/lib/chart-export.d.ts.map +1 -0
  70. package/dist/lib/chart-export.js +102 -0
  71. package/dist/lib/charts.d.ts +235 -0
  72. package/dist/lib/charts.d.ts.map +1 -0
  73. package/dist/lib/charts.js +744 -0
  74. package/dist/lib/geo.d.ts +39 -0
  75. package/dist/lib/geo.d.ts.map +1 -0
  76. package/dist/lib/geo.js +101 -0
  77. package/dist/lib/iso.d.ts +21 -0
  78. package/dist/lib/iso.d.ts.map +1 -0
  79. package/dist/lib/iso.js +56 -0
  80. package/dist/lib/rrule.d.ts +20 -0
  81. package/dist/lib/rrule.d.ts.map +1 -0
  82. package/dist/lib/rrule.js +236 -0
  83. package/dist/lib/scheduler.d.ts +156 -0
  84. package/dist/lib/scheduler.d.ts.map +1 -0
  85. package/dist/lib/scheduler.js +502 -0
  86. package/dist/lib/webgl-scatter.d.ts +44 -0
  87. package/dist/lib/webgl-scatter.d.ts.map +1 -0
  88. package/dist/lib/webgl-scatter.js +185 -0
  89. package/dist/primitives/Accordion.d.ts +44 -20
  90. package/dist/primitives/Accordion.d.ts.map +1 -1
  91. package/dist/primitives/Accordion.js +82 -22
  92. package/dist/primitives/AppBar.d.ts +13 -0
  93. package/dist/primitives/AppBar.d.ts.map +1 -0
  94. package/dist/primitives/AppBar.js +27 -0
  95. package/dist/primitives/Autocomplete.d.ts +28 -0
  96. package/dist/primitives/Autocomplete.d.ts.map +1 -0
  97. package/dist/primitives/Autocomplete.js +118 -0
  98. package/dist/primitives/Avatar.d.ts +13 -1
  99. package/dist/primitives/Avatar.d.ts.map +1 -1
  100. package/dist/primitives/Avatar.js +17 -2
  101. package/dist/primitives/Backdrop.d.ts +17 -0
  102. package/dist/primitives/Backdrop.d.ts.map +1 -0
  103. package/dist/primitives/Backdrop.js +17 -0
  104. package/dist/primitives/BottomNavigation.d.ts +23 -0
  105. package/dist/primitives/BottomNavigation.d.ts.map +1 -0
  106. package/dist/primitives/BottomNavigation.js +30 -0
  107. package/dist/primitives/ButtonBase.d.ts +14 -0
  108. package/dist/primitives/ButtonBase.d.ts.map +1 -0
  109. package/dist/primitives/ButtonBase.js +13 -0
  110. package/dist/primitives/ButtonGroup.d.ts +25 -0
  111. package/dist/primitives/ButtonGroup.d.ts.map +1 -0
  112. package/dist/primitives/ButtonGroup.js +44 -0
  113. package/dist/primitives/CircularProgress.d.ts +16 -0
  114. package/dist/primitives/CircularProgress.d.ts.map +1 -0
  115. package/dist/primitives/CircularProgress.js +17 -0
  116. package/dist/primitives/ClickAwayListener.d.ts +16 -0
  117. package/dist/primitives/ClickAwayListener.d.ts.map +1 -0
  118. package/dist/primitives/ClickAwayListener.js +33 -0
  119. package/dist/primitives/Collapse.d.ts +19 -0
  120. package/dist/primitives/Collapse.d.ts.map +1 -0
  121. package/dist/primitives/Collapse.js +14 -0
  122. package/dist/primitives/Container.d.ts +13 -0
  123. package/dist/primitives/Container.d.ts.map +1 -0
  124. package/dist/primitives/Container.js +26 -0
  125. package/dist/primitives/Fab.d.ts +22 -0
  126. package/dist/primitives/Fab.d.ts.map +1 -0
  127. package/dist/primitives/Fab.js +42 -0
  128. package/dist/primitives/FormControl.d.ts +27 -0
  129. package/dist/primitives/FormControl.d.ts.map +1 -0
  130. package/dist/primitives/FormControl.js +24 -0
  131. package/dist/primitives/FormControlLabel.d.ts +18 -0
  132. package/dist/primitives/FormControlLabel.d.ts.map +1 -0
  133. package/dist/primitives/FormControlLabel.js +19 -0
  134. package/dist/primitives/Grid.d.ts +32 -0
  135. package/dist/primitives/Grid.d.ts.map +1 -0
  136. package/dist/primitives/Grid.js +50 -0
  137. package/dist/primitives/IconButton.d.ts +22 -0
  138. package/dist/primitives/IconButton.d.ts.map +1 -0
  139. package/dist/primitives/IconButton.js +39 -0
  140. package/dist/primitives/ImageList.d.ts +31 -0
  141. package/dist/primitives/ImageList.d.ts.map +1 -0
  142. package/dist/primitives/ImageList.js +33 -0
  143. package/dist/primitives/InputAdornment.d.ts +28 -0
  144. package/dist/primitives/InputAdornment.d.ts.map +1 -0
  145. package/dist/primitives/InputAdornment.js +13 -0
  146. package/dist/primitives/Link.d.ts +17 -0
  147. package/dist/primitives/Link.d.ts.map +1 -0
  148. package/dist/primitives/Link.js +29 -0
  149. package/dist/primitives/List.d.ts +41 -0
  150. package/dist/primitives/List.d.ts.map +1 -0
  151. package/dist/primitives/List.js +22 -0
  152. package/dist/primitives/Masonry.d.ts +14 -0
  153. package/dist/primitives/Masonry.d.ts.map +1 -0
  154. package/dist/primitives/Masonry.js +15 -0
  155. package/dist/primitives/Menu.d.ts +28 -0
  156. package/dist/primitives/Menu.d.ts.map +1 -0
  157. package/dist/primitives/Menu.js +54 -0
  158. package/dist/primitives/MobileStepper.d.ts +15 -0
  159. package/dist/primitives/MobileStepper.d.ts.map +1 -0
  160. package/dist/primitives/MobileStepper.js +8 -0
  161. package/dist/primitives/Modal.d.ts +18 -0
  162. package/dist/primitives/Modal.d.ts.map +1 -0
  163. package/dist/primitives/Modal.js +29 -0
  164. package/dist/primitives/NoSsr.d.ts +15 -0
  165. package/dist/primitives/NoSsr.d.ts.map +1 -0
  166. package/dist/primitives/NoSsr.js +19 -0
  167. package/dist/primitives/Paper.d.ts +22 -0
  168. package/dist/primitives/Paper.d.ts.map +1 -0
  169. package/dist/primitives/Paper.js +33 -0
  170. package/dist/primitives/Progress.d.ts +6 -2
  171. package/dist/primitives/Progress.d.ts.map +1 -1
  172. package/dist/primitives/Progress.js +8 -3
  173. package/dist/primitives/Snackbar.d.ts +23 -0
  174. package/dist/primitives/Snackbar.d.ts.map +1 -0
  175. package/dist/primitives/Snackbar.js +33 -0
  176. package/dist/primitives/SpeedDial.d.ts +26 -0
  177. package/dist/primitives/SpeedDial.d.ts.map +1 -0
  178. package/dist/primitives/SpeedDial.js +26 -0
  179. package/dist/primitives/Stack.d.ts +15 -0
  180. package/dist/primitives/Stack.d.ts.map +1 -0
  181. package/dist/primitives/Stack.js +45 -0
  182. package/dist/primitives/StarRating.d.ts +19 -1
  183. package/dist/primitives/StarRating.d.ts.map +1 -1
  184. package/dist/primitives/StarRating.js +36 -6
  185. package/dist/primitives/Stepper.d.ts +36 -0
  186. package/dist/primitives/Stepper.d.ts.map +1 -0
  187. package/dist/primitives/Stepper.js +53 -0
  188. package/dist/primitives/TablePagination.d.ts +16 -0
  189. package/dist/primitives/TablePagination.d.ts.map +1 -0
  190. package/dist/primitives/TablePagination.js +12 -0
  191. package/dist/primitives/TextField.d.ts +24 -0
  192. package/dist/primitives/TextField.d.ts.map +1 -0
  193. package/dist/primitives/TextField.js +19 -0
  194. package/dist/primitives/TextareaAutosize.d.ts +11 -0
  195. package/dist/primitives/TextareaAutosize.d.ts.map +1 -0
  196. package/dist/primitives/TextareaAutosize.js +26 -0
  197. package/dist/primitives/ToggleButton.d.ts +28 -0
  198. package/dist/primitives/ToggleButton.d.ts.map +1 -0
  199. package/dist/primitives/ToggleButton.js +56 -0
  200. package/dist/primitives/TransferList.d.ts +21 -0
  201. package/dist/primitives/TransferList.d.ts.map +1 -0
  202. package/dist/primitives/TransferList.js +40 -0
  203. package/dist/primitives/Typography.d.ts +23 -0
  204. package/dist/primitives/Typography.d.ts.map +1 -0
  205. package/dist/primitives/Typography.js +69 -0
  206. package/dist/primitives/transitions.d.ts +29 -0
  207. package/dist/primitives/transitions.d.ts.map +1 -0
  208. package/dist/primitives/transitions.js +52 -0
  209. package/package.json +28 -4
  210. package/skills/spatika-ui/SKILL.md +96 -0
  211. package/src/composites/Card.tsx +51 -1
  212. package/src/composites/ChartDataGrid.test.tsx +57 -0
  213. package/src/composites/ChartDataGrid.tsx +196 -0
  214. package/src/composites/CoverHero.test.tsx +38 -0
  215. package/src/composites/CoverHero.tsx +29 -22
  216. package/src/composites/EntityMediaCard.test.tsx +58 -0
  217. package/src/composites/EntityMediaCard.tsx +31 -13
  218. package/src/composites/EventCalendar.tsx +1030 -0
  219. package/src/composites/EventEditor.tsx +268 -0
  220. package/src/composites/EventTimeline.tsx +366 -0
  221. package/src/composites/MapChart.test.tsx +39 -0
  222. package/src/composites/MapChart.tsx +130 -0
  223. package/src/composites/ProfileHero.test.tsx +33 -0
  224. package/src/composites/ProfileHero.tsx +96 -79
  225. package/src/composites/ScatterWebGL.test.tsx +25 -0
  226. package/src/composites/ScatterWebGL.tsx +79 -0
  227. package/src/composites/SiteFooter.test.tsx +37 -0
  228. package/src/composites/SiteFooter.tsx +1 -1
  229. package/src/composites/SiteNav.test.tsx +46 -0
  230. package/src/composites/SiteNav.tsx +14 -4
  231. package/src/composites/StatCard.test.tsx +30 -0
  232. package/src/composites/StatCard.tsx +4 -0
  233. package/src/composites/Wordmark.test.tsx +23 -0
  234. package/src/composites/Wordmark.tsx +2 -2
  235. package/src/composites/cartesian-charts.test.tsx +52 -0
  236. package/src/composites/cartesian-charts.tsx +1287 -0
  237. package/src/composites/chart-composition.test.tsx +53 -0
  238. package/src/composites/chart-composition.tsx +898 -0
  239. package/src/composites/chart-ui.tsx +328 -0
  240. package/src/composites/charts-3d.test.tsx +25 -0
  241. package/src/composites/charts-3d.tsx +318 -0
  242. package/src/composites/charts.test.tsx +254 -0
  243. package/src/composites/chrome.test.tsx +29 -0
  244. package/src/composites/flow-charts.test.tsx +79 -0
  245. package/src/composites/flow-charts.tsx +607 -0
  246. package/src/composites/marketing.test.tsx +38 -34
  247. package/src/composites/radial-charts.test.tsx +74 -0
  248. package/src/composites/radial-charts.tsx +617 -0
  249. package/src/composites/scheduler-ui.tsx +306 -0
  250. package/src/composites/scheduler.test.tsx +280 -0
  251. package/src/index.test.ts +26 -0
  252. package/src/index.ts +287 -5
  253. package/src/lib/chart-export.test.ts +20 -0
  254. package/src/lib/chart-export.ts +99 -0
  255. package/src/lib/charts-platform.test.ts +93 -0
  256. package/src/lib/charts.test.ts +132 -0
  257. package/src/lib/charts.ts +1021 -0
  258. package/src/lib/geo.test.ts +42 -0
  259. package/src/lib/geo.ts +138 -0
  260. package/src/lib/iso.test.ts +22 -0
  261. package/src/lib/iso.ts +102 -0
  262. package/src/lib/rrule.test.ts +53 -0
  263. package/src/lib/rrule.ts +281 -0
  264. package/src/lib/scheduler.test.ts +199 -0
  265. package/src/lib/scheduler.ts +693 -0
  266. package/src/lib/tokens.test.ts +2 -2
  267. package/src/lib/webgl-scatter.test.ts +36 -0
  268. package/src/lib/webgl-scatter.ts +230 -0
  269. package/src/primitives/Accordion.tsx +218 -63
  270. package/src/primitives/AppBar.tsx +39 -0
  271. package/src/primitives/Autocomplete.tsx +236 -0
  272. package/src/primitives/Avatar.tsx +52 -1
  273. package/src/primitives/Backdrop.tsx +48 -0
  274. package/src/primitives/BottomNavigation.tsx +102 -0
  275. package/src/primitives/ButtonBase.tsx +34 -0
  276. package/src/primitives/ButtonGroup.tsx +90 -0
  277. package/src/primitives/CircularProgress.tsx +104 -0
  278. package/src/primitives/ClickAwayListener.tsx +46 -0
  279. package/src/primitives/Collapse.tsx +39 -0
  280. package/src/primitives/Container.tsx +40 -0
  281. package/src/primitives/Fab.tsx +62 -0
  282. package/src/primitives/FormControl.tsx +90 -0
  283. package/src/primitives/FormControlLabel.tsx +51 -0
  284. package/src/primitives/Grid.tsx +101 -0
  285. package/src/primitives/IconButton.tsx +59 -0
  286. package/src/primitives/ImageList.test.tsx +48 -0
  287. package/src/primitives/ImageList.tsx +112 -0
  288. package/src/primitives/InputAdornment.tsx +86 -0
  289. package/src/primitives/Link.tsx +48 -0
  290. package/src/primitives/List.tsx +121 -0
  291. package/src/primitives/Masonry.tsx +32 -0
  292. package/src/primitives/Menu.tsx +112 -0
  293. package/src/primitives/MobileStepper.tsx +64 -0
  294. package/src/primitives/Modal.tsx +70 -0
  295. package/src/primitives/NoSsr.tsx +28 -0
  296. package/src/primitives/Paper.tsx +55 -0
  297. package/src/primitives/Progress.tsx +25 -7
  298. package/src/primitives/Snackbar.tsx +83 -0
  299. package/src/primitives/SpeedDial.tsx +110 -0
  300. package/src/primitives/Stack.tsx +88 -0
  301. package/src/primitives/StarRating.tsx +87 -28
  302. package/src/primitives/Stepper.tsx +184 -0
  303. package/src/primitives/TablePagination.tsx +58 -0
  304. package/src/primitives/TextField.tsx +105 -0
  305. package/src/primitives/TextareaAutosize.tsx +49 -0
  306. package/src/primitives/ToggleButton.tsx +148 -0
  307. package/src/primitives/TransferList.tsx +110 -0
  308. package/src/primitives/Typography.tsx +90 -0
  309. package/src/primitives/mui-more.test.tsx +76 -0
  310. package/src/primitives/mui.test.tsx +134 -0
  311. package/src/primitives/transitions.tsx +120 -0
@@ -0,0 +1,371 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo, useRef, useState, } from "react";
3
+ import { cn } from "../lib/cn";
4
+ import { expandVisibleEvents } from "../lib/rrule";
5
+ import { useControllableState } from "../lib/use-controllable-state";
6
+ import { addDays, addMonths, agendaDays, DEFAULT_SCHEDULER_PREFERENCES, eventCanDrag, eventCanResize, eventColorStyle, filterWeekendDays, formatDateSpan, formatDayHeading, formatHour, formatMonthYear, formatTime, formatWeekRange, getMonthGrid, getWeekDays, hoursInView, isMonthBar, isSameDay, isSameMonth, isoDay, layoutTimedEvents, normalizeEvents, occupiesDay, occurrenceKey, resizeEvent, shiftByDays, startOfDay, timedEventsOnDay, toDate, visibleEvents, weekNumber, weekSpanLanesForDays, } from "../lib/scheduler";
7
+ import { EventEditor } from "./EventEditor";
8
+ import { CALENDAR_VIEW_LABEL, EventResizeHandle, ResourceLegend, SchedulerPreferencesMenu, SchedulerShell, SchedulerToolbar, } from "./scheduler-ui";
9
+ import { DropdownMenuItem } from "../primitives/DropdownMenu";
10
+ const MAX_TIMED_IN_CELL = 3;
11
+ const AGENDA_LENGTH = 14;
12
+ function chunkWeeks(items) {
13
+ const weeks = [];
14
+ for (let i = 0; i < items.length; i += 7)
15
+ weeks.push(items.slice(i, i + 7));
16
+ return weeks;
17
+ }
18
+ function sourceEvent(events, id) {
19
+ return events.find((event) => event.id === id);
20
+ }
21
+ function startOfToday() {
22
+ const now = new Date();
23
+ return new Date(now.getFullYear(), now.getMonth(), now.getDate());
24
+ }
25
+ function visibleRange(view, date, weekStartsOn) {
26
+ if (view === "month") {
27
+ const grid = getMonthGrid(date, weekStartsOn);
28
+ return { start: grid[0], end: addDays(grid[grid.length - 1], 1) };
29
+ }
30
+ if (view === "week") {
31
+ const days = getWeekDays(date, weekStartsOn);
32
+ return { start: days[0], end: addDays(days[6], 1) };
33
+ }
34
+ if (view === "agenda") {
35
+ const days = agendaDays(date, AGENDA_LENGTH);
36
+ return { start: days[0], end: addDays(days[days.length - 1], 1) };
37
+ }
38
+ const start = startOfDay(date);
39
+ return { start, end: addDays(start, 1) };
40
+ }
41
+ function draftFromSource(source) {
42
+ return {
43
+ id: source.id,
44
+ title: source.title,
45
+ start: toDate(source.start),
46
+ end: toDate(source.end),
47
+ allDay: source.allDay,
48
+ resourceId: source.resourceId,
49
+ color: source.color,
50
+ description: source.description,
51
+ rrule: source.rrule,
52
+ };
53
+ }
54
+ /** Month / week / day / agenda calendar with editor, resize, recurrence, and preferences. */
55
+ export function EventCalendar({ events = [], resources = [], view: viewProp, defaultView = "month", onViewChange, date: dateProp, defaultDate, onDateChange, views = ["month", "week", "day", "agenda"], weekStartsOn = 0, hourStart = 7, hourEnd = 21, locale, readOnly = false, areEventsDraggable, areEventsResizable, preferences, defaultPreferences, onPreferencesChange, onEventClick, onEventChange, onEventCreate, onEventDelete, onSlotClick, showEventEditor = true, showPreferences = true, toolbarTrailing, renderEvent, renderToolbar, className, }) {
56
+ const [view, setView] = useControllableState({
57
+ prop: viewProp,
58
+ defaultProp: defaultView,
59
+ onChange: onViewChange,
60
+ });
61
+ const [date, setDate] = useControllableState({
62
+ prop: dateProp,
63
+ defaultProp: defaultDate ?? new Date(),
64
+ onChange: onDateChange,
65
+ });
66
+ const [prefs, setPrefs] = useControllableState({
67
+ prop: preferences
68
+ ? { ...DEFAULT_SCHEDULER_PREFERENCES, weekStartsOn, ...defaultPreferences, ...preferences }
69
+ : undefined,
70
+ defaultProp: { ...DEFAULT_SCHEDULER_PREFERENCES, weekStartsOn, ...defaultPreferences },
71
+ onChange: onPreferencesChange,
72
+ });
73
+ const [hiddenIds, setHiddenIds] = useState(new Set());
74
+ const [draft, setDraft] = useState(null);
75
+ const dragRef = useRef(null);
76
+ const resolvedView = view ?? "month";
77
+ const resolvedDate = date ?? new Date();
78
+ const resolvedPrefs = prefs ?? { ...DEFAULT_SCHEDULER_PREFERENCES, weekStartsOn };
79
+ const startOn = resolvedPrefs.weekStartsOn;
80
+ const today = startOfToday();
81
+ const hour12 = resolvedPrefs.ampm;
82
+ const canDrag = !readOnly && (areEventsDraggable ?? Boolean(onEventChange));
83
+ const canResize = !readOnly && (areEventsResizable ?? Boolean(onEventChange));
84
+ const editorLocked = readOnly || Boolean(draft?.id && sourceEvent(events, draft.id)?.readOnly);
85
+ const range = useMemo(() => visibleRange(resolvedView, resolvedDate, startOn), [resolvedView, resolvedDate, startOn]);
86
+ const normalized = useMemo(() => {
87
+ const visible = visibleEvents(normalizeEvents(events, resources), hiddenIds);
88
+ return expandVisibleEvents(visible, range.start, range.end, events);
89
+ }, [events, resources, hiddenIds, range.start, range.end]);
90
+ const title = resolvedView === "month"
91
+ ? formatMonthYear(resolvedDate, locale)
92
+ : resolvedView === "week"
93
+ ? formatWeekRange(resolvedDate, startOn, locale)
94
+ : resolvedView === "agenda"
95
+ ? formatDateSpan(range.start, addDays(range.end, -1), locale)
96
+ : formatDayHeading(resolvedDate, locale);
97
+ function go(direction) {
98
+ if (resolvedView === "month")
99
+ setDate(addMonths(resolvedDate, direction));
100
+ else if (resolvedView === "week")
101
+ setDate(addDays(resolvedDate, direction * 7));
102
+ else if (resolvedView === "agenda")
103
+ setDate(addDays(resolvedDate, direction * 7));
104
+ else
105
+ setDate(addDays(resolvedDate, direction));
106
+ }
107
+ function toggleResource(id) {
108
+ setHiddenIds((prev) => {
109
+ const next = new Set(prev);
110
+ if (next.has(id))
111
+ next.delete(id);
112
+ else
113
+ next.add(id);
114
+ return next;
115
+ });
116
+ }
117
+ function openEvent(event) {
118
+ const source = sourceEvent(events, event.id);
119
+ if (source)
120
+ onEventClick?.(source);
121
+ if (!showEventEditor)
122
+ return;
123
+ setDraft(source ? draftFromSource(source) : {
124
+ id: event.id,
125
+ title: event.title,
126
+ start: event.start,
127
+ end: event.end,
128
+ allDay: event.allDay,
129
+ resourceId: event.resourceId,
130
+ color: event.color,
131
+ description: event.description,
132
+ rrule: event.rrule,
133
+ });
134
+ }
135
+ function openSlot(slot) {
136
+ onSlotClick?.(slot);
137
+ if (readOnly || !showEventEditor)
138
+ return;
139
+ setDraft({ start: slot.start, end: slot.end, allDay: slot.allDay });
140
+ }
141
+ function handleEventPointerDown(event, pointer) {
142
+ if (!onEventChange || !eventCanDrag(event, { readOnly, enabled: canDrag }))
143
+ return;
144
+ pointer.currentTarget.setPointerCapture?.(pointer.pointerId);
145
+ dragRef.current = { id: event.id, x: pointer.clientX, y: pointer.clientY, active: false };
146
+ }
147
+ function handleEventPointerMove(pointer) {
148
+ const drag = dragRef.current;
149
+ if (!drag)
150
+ return;
151
+ if (Math.hypot(pointer.clientX - drag.x, pointer.clientY - drag.y) > 6) {
152
+ drag.active = true;
153
+ }
154
+ }
155
+ function handleEventPointerUp(event, pointer) {
156
+ const drag = dragRef.current;
157
+ dragRef.current = null;
158
+ if (!drag?.active || !onEventChange)
159
+ return;
160
+ const target = document
161
+ .elementFromPoint(pointer.clientX, pointer.clientY)
162
+ ?.closest("[data-scheduler-day]");
163
+ const iso = target?.dataset.schedulerDay;
164
+ if (!iso)
165
+ return;
166
+ const [year, month, day] = iso.split("-").map(Number);
167
+ const targetDay = new Date(year, month - 1, day);
168
+ const currentDay = startOfDay(event.start);
169
+ const delta = Math.round((targetDay.getTime() - currentDay.getTime()) / 86_400_000);
170
+ if (delta === 0)
171
+ return;
172
+ const source = sourceEvent(events, event.id);
173
+ if (!source)
174
+ return;
175
+ const master = normalizeEvents([source], resources)[0];
176
+ onEventChange(source, shiftByDays(master, delta));
177
+ }
178
+ function commitResize(event, edge, instant) {
179
+ if (!onEventChange)
180
+ return;
181
+ const next = resizeEvent(event, edge, instant);
182
+ const source = sourceEvent(events, event.id);
183
+ if (!source)
184
+ return;
185
+ const master = normalizeEvents([source], resources)[0];
186
+ onEventChange(source, {
187
+ start: new Date(master.start.getTime() + (next.start.getTime() - event.start.getTime())),
188
+ end: new Date(master.end.getTime() + (next.end.getTime() - event.end.getTime())),
189
+ });
190
+ }
191
+ function handleKeyDown(keyboard) {
192
+ if (draft)
193
+ return;
194
+ const target = keyboard.target;
195
+ if (target.closest("input, textarea, select, [contenteditable='true']"))
196
+ return;
197
+ if (keyboard.key === "ArrowLeft") {
198
+ keyboard.preventDefault();
199
+ go(-1);
200
+ }
201
+ else if (keyboard.key === "ArrowRight") {
202
+ keyboard.preventDefault();
203
+ go(1);
204
+ }
205
+ else if (keyboard.key === "t" || keyboard.key === "T") {
206
+ keyboard.preventDefault();
207
+ setDate(new Date());
208
+ }
209
+ }
210
+ function renderEventTrigger(event, className, defaultChildren, options) {
211
+ const source = sourceEvent(events, event.id);
212
+ const variant = options?.variant ?? "month-timed";
213
+ const children = renderEvent
214
+ ? renderEvent(event, {
215
+ view: resolvedView,
216
+ variant,
217
+ locale,
218
+ hour12,
219
+ source,
220
+ defaultChildren,
221
+ })
222
+ : defaultChildren;
223
+ if (renderEvent && children == null)
224
+ return null;
225
+ const resizable = options?.resize === "ns" &&
226
+ eventCanResize(event, { readOnly, enabled: canResize }) &&
227
+ Boolean(onEventChange);
228
+ const painted = variant === "month-bar" || variant === "all-day" || variant === "time-grid";
229
+ const button = (_jsx("button", { type: "button", draggable: false, className: className, style: variant === "agenda"
230
+ ? eventColorStyle(event.color, "wash")
231
+ : painted
232
+ ? eventColorStyle(event.color, resolvedView === "month" ? "bar" : "block")
233
+ : undefined, onClick: () => {
234
+ if (dragRef.current?.active)
235
+ return;
236
+ openEvent(event);
237
+ }, onPointerDown: (pointer) => handleEventPointerDown(event, pointer), onPointerMove: handleEventPointerMove, onPointerUp: (pointer) => handleEventPointerUp(event, pointer), children: children }));
238
+ if (!resizable)
239
+ return button;
240
+ return (_jsxs("div", { className: "relative h-full min-h-0", children: [button, _jsx(EventResizeHandle, { edge: "start", axis: "ns", event: event, hourStart: hourStart, hourEnd: hourEnd, onCommit: commitResize }), _jsx(EventResizeHandle, { edge: "end", axis: "ns", event: event, hourStart: hourStart, hourEnd: hourEnd, onCommit: commitResize })] }));
241
+ }
242
+ return (_jsxs(SchedulerShell, { slot: "event-calendar", className: className, tabIndex: 0, "aria-label": "Event calendar", onKeyDown: handleKeyDown, children: [renderToolbar ? (renderToolbar({
243
+ view: resolvedView,
244
+ date: resolvedDate,
245
+ title,
246
+ views,
247
+ locale,
248
+ preferences: resolvedPrefs,
249
+ onViewChange: (next) => setView(next),
250
+ onPrev: () => go(-1),
251
+ onNext: () => go(1),
252
+ onToday: () => setDate(new Date()),
253
+ onDateChange: (next) => setDate(next),
254
+ onPreferencesChange: (next) => setPrefs(next),
255
+ })) : (_jsx(SchedulerToolbar, { title: title, onPrev: () => go(-1), onNext: () => go(1), onToday: () => setDate(new Date()), menuLabel: CALENDAR_VIEW_LABEL[resolvedView], menu: views.map((item) => (_jsx(DropdownMenuItem, { onClick: () => setView(item), children: CALENDAR_VIEW_LABEL[item] }, item))), trailing: _jsxs(_Fragment, { children: [toolbarTrailing, showPreferences ? (_jsx(SchedulerPreferencesMenu, { value: resolvedPrefs, onChange: (next) => setPrefs(next) })) : null] }) })), resolvedView === "month" ? (_jsx(MonthGrid, { date: resolvedDate, today: today, events: normalized, weekStartsOn: startOn, showWeekends: resolvedPrefs.showWeekends, showWeekNumber: resolvedPrefs.showWeekNumber, locale: locale, hour12: hour12, onSlotClick: openSlot, renderEventTrigger: renderEventTrigger })) : resolvedView === "agenda" ? (_jsx(AgendaList, { date: resolvedDate, events: normalized, showWeekends: resolvedPrefs.showWeekends, locale: locale, hour12: hour12, onSlotClick: openSlot, renderEventTrigger: renderEventTrigger })) : (_jsx(TimeGrid, { date: resolvedDate, today: today, events: normalized, view: resolvedView === "day" ? "day" : "week", weekStartsOn: startOn, showWeekends: resolvedPrefs.showWeekends, hourStart: hourStart, hourEnd: hourEnd, locale: locale, hour12: hour12, onSlotClick: openSlot, renderEventTrigger: renderEventTrigger })), _jsx(ResourceLegend, { resources: resources, hiddenIds: hiddenIds, onToggle: toggleResource }), showEventEditor ? (_jsx(EventEditor, { open: draft != null, onOpenChange: (open) => {
256
+ if (!open)
257
+ setDraft(null);
258
+ }, draft: draft, resources: resources, readOnly: editorLocked, onSave: (event) => {
259
+ if (event.id && sourceEvent(events, event.id)) {
260
+ const source = sourceEvent(events, event.id);
261
+ onEventChange?.(source, event);
262
+ }
263
+ else {
264
+ onEventCreate?.(event);
265
+ }
266
+ }, onDelete: onEventDelete
267
+ ? (id) => {
268
+ const source = sourceEvent(events, id);
269
+ if (source)
270
+ onEventDelete(source);
271
+ }
272
+ : undefined })) : null] }));
273
+ }
274
+ function MonthGrid({ date, today, events, weekStartsOn, showWeekends, showWeekNumber, locale, hour12, onSlotClick, renderEventTrigger, }) {
275
+ const weeks = chunkWeeks(getMonthGrid(date, weekStartsOn));
276
+ const headerDays = filterWeekendDays(getWeekDays(date, weekStartsOn), showWeekends);
277
+ const columns = headerDays.length;
278
+ const gridStyle = { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` };
279
+ return (_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex border-b border-border/50", children: [showWeekNumber ? _jsx("div", { className: "w-8 shrink-0" }) : null, _jsx("div", { className: "grid min-w-0 flex-1", style: gridStyle, children: headerDays.map((day) => (_jsx("div", { className: "px-2 py-2 text-center text-[11px] font-bold uppercase tracking-wider text-muted-foreground", children: new Intl.DateTimeFormat(locale, { weekday: "short" }).format(day) }, day.getDay()))) })] }), _jsx("div", { children: weeks.map((week) => {
280
+ const days = filterWeekendDays(week, showWeekends);
281
+ const lanes = weekSpanLanesForDays(days, events);
282
+ return (_jsxs("div", { className: "flex border-b border-border/40 last:border-b-0", children: [showWeekNumber ? (_jsx("div", { className: "flex w-8 shrink-0 items-start justify-center pt-2 text-[10px] font-bold text-muted-foreground", children: weekNumber(week[0]) })) : null, _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "grid", style: gridStyle, children: days.map((day) => {
283
+ const inMonth = isSameMonth(day, date);
284
+ const isToday = isSameDay(day, today);
285
+ return (_jsx("div", { "data-scheduler-day": isoDay(day), className: cn("flex items-start justify-end px-1.5 pt-1.5", !inMonth && "bg-muted/15"), children: _jsx("button", { type: "button", onClick: () => onSlotClick?.({
286
+ start: day,
287
+ end: addDays(day, 1),
288
+ allDay: true,
289
+ }), className: cn("flex size-7 items-center justify-center rounded-full text-xs font-bold", isToday && "bg-primary text-primary-foreground", !isToday && inMonth && "text-foreground hover:bg-muted/50", !isToday && !inMonth && "text-muted-foreground/70"), children: day.getDate() }) }, day.toISOString()));
290
+ }) }), lanes.map((lane, laneIndex) => (_jsx("div", { className: "grid gap-px px-0.5 pb-0.5", style: gridStyle, children: laneSlots(lane, columns).map((slot, slotIndex) => slot.kind === "empty" ? (_jsx("div", {}, slotIndex)) : (_jsx("div", { className: "min-w-0", style: { gridColumn: `span ${slot.span}` }, children: renderEventTrigger(slot.event, "flex h-5 w-full items-center truncate rounded-md px-1.5 text-[11px] font-semibold leading-none", slot.event.title, { variant: "month-bar" }) }, occurrenceKey(slot.event)))) }, laneIndex))), _jsx("div", { className: "grid min-h-[4.25rem]", style: gridStyle, children: days.map((day) => {
291
+ const timed = timedEventsOnDay(events, day);
292
+ const visibleTimed = timed.slice(0, MAX_TIMED_IN_CELL);
293
+ const extra = timed.length - visibleTimed.length;
294
+ return (_jsxs("div", { "data-scheduler-day": isoDay(day), className: cn("min-w-0 space-y-0.5 px-1 pb-2", !isSameMonth(day, date) && "bg-muted/15"), onClick: (click) => {
295
+ if (click.target.closest("button"))
296
+ return;
297
+ onSlotClick?.({
298
+ start: new Date(day.getFullYear(), day.getMonth(), day.getDate(), 9),
299
+ end: new Date(day.getFullYear(), day.getMonth(), day.getDate(), 10),
300
+ allDay: false,
301
+ });
302
+ }, children: [visibleTimed.map((event) => (_jsx("div", { className: "min-w-0", children: renderEventTrigger(event, "flex w-full min-w-0 items-center gap-1 rounded-sm px-0.5 py-0.5 text-left text-[11px] leading-tight text-foreground hover:bg-muted/40", _jsxs(_Fragment, { children: [_jsx("span", { className: "size-1.5 shrink-0 rounded-full", style: eventColorStyle(event.color, "dot") }), _jsx("span", { className: "shrink-0 text-muted-foreground", children: formatTime(event.start, locale, hour12) }), _jsx("span", { className: "min-w-0 truncate font-medium", children: event.title })] }), { variant: "month-timed" }) }, occurrenceKey(event)))), extra > 0 ? (_jsxs("p", { className: "px-0.5 text-[10px] font-bold text-muted-foreground", children: ["+", extra, " more"] })) : null] }, day.toISOString()));
303
+ }) })] })] }, week[0].toISOString()));
304
+ }) })] }));
305
+ }
306
+ function laneSlots(lane, columns) {
307
+ const slots = [];
308
+ let cursor = 0;
309
+ const ordered = [...lane].sort((a, b) => a.startCol - b.startCol);
310
+ for (const segment of ordered) {
311
+ while (cursor < segment.startCol) {
312
+ slots.push({ kind: "empty", span: 1 });
313
+ cursor += 1;
314
+ }
315
+ slots.push({ kind: "event", event: segment.event, span: segment.span });
316
+ cursor += segment.span;
317
+ }
318
+ while (cursor < columns) {
319
+ slots.push({ kind: "empty", span: 1 });
320
+ cursor += 1;
321
+ }
322
+ return slots;
323
+ }
324
+ function TimeGrid({ date, today, events, view, weekStartsOn, showWeekends, hourStart, hourEnd, locale, hour12, onSlotClick, renderEventTrigger, }) {
325
+ const days = view === "day"
326
+ ? [date]
327
+ : filterWeekendDays(getWeekDays(date, weekStartsOn), showWeekends);
328
+ const hours = hoursInView(hourStart, hourEnd);
329
+ const hourHeight = 48;
330
+ const now = new Date();
331
+ const showNow = days.some((day) => isSameDay(day, now));
332
+ const nowTop = ((now.getHours() + now.getMinutes() / 60 - hourStart) / (hourEnd - hourStart)) * (hours.length * hourHeight);
333
+ return (_jsxs("div", { className: "overflow-x-auto", children: [_jsxs("div", { className: "grid min-w-[640px]", style: { gridTemplateColumns: `3.5rem repeat(${days.length}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "border-b border-border/50" }), days.map((day) => (_jsxs("div", { className: cn("border-b border-l border-border/50 px-2 py-2 text-center", isSameDay(day, today) && "bg-primary/5"), children: [_jsx("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: new Intl.DateTimeFormat(locale, { weekday: "short" }).format(day) }), _jsx("p", { className: cn("mx-auto mt-0.5 flex size-7 items-center justify-center rounded-full text-sm font-black", isSameDay(day, today) && "bg-primary text-primary-foreground"), children: day.getDate() })] }, day.toISOString()))), _jsx("div", { className: "border-b border-border/50" }), days.map((day) => {
334
+ const allDay = events.filter((event) => isMonthBar(event) && occupiesDay(event, day));
335
+ return (_jsx("div", { "data-scheduler-day": isoDay(day), className: "min-h-10 space-y-0.5 border-b border-l border-border/50 p-1", children: allDay.map((event) => (_jsx("div", { children: renderEventTrigger(event, "flex h-5 w-full items-center truncate rounded-md px-1.5 text-[11px] font-semibold", event.title, { variant: "all-day" }) }, occurrenceKey(event)))) }, `allday-${day.toISOString()}`));
336
+ })] }), _jsxs("div", { className: "relative min-w-[640px]", style: { height: hours.length * hourHeight }, children: [_jsxs("div", { className: "grid h-full", style: { gridTemplateColumns: `3.5rem repeat(${days.length}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "relative", children: hours.map((hour) => (_jsx("div", { className: "relative border-b border-border/30 pr-2 text-right text-[10px] font-bold text-muted-foreground", style: { height: hourHeight }, children: _jsx("span", { className: "-translate-y-2 absolute right-2", children: formatHour(hour, locale, hour12) }) }, hour))) }), days.map((day) => {
337
+ const layout = layoutTimedEvents(events, day, hourStart, hourEnd);
338
+ return (_jsxs("div", { "data-scheduler-day": isoDay(day), className: "relative border-l border-border/40", onClick: (click) => {
339
+ if (click.target.closest("button"))
340
+ return;
341
+ const rect = click.currentTarget.getBoundingClientRect();
342
+ const ratio = (click.clientY - rect.top) / rect.height;
343
+ const hour = hourStart + ratio * (hourEnd - hourStart);
344
+ const startHour = Math.floor(hour);
345
+ const start = new Date(day.getFullYear(), day.getMonth(), day.getDate(), startHour);
346
+ onSlotClick?.({
347
+ start,
348
+ end: new Date(start.getTime() + 3_600_000),
349
+ allDay: false,
350
+ });
351
+ }, children: [hours.map((hour) => (_jsx("div", { className: "border-b border-border/30", style: { height: hourHeight } }, hour))), layout.map((item) => (_jsx("div", { className: "absolute px-0.5", style: {
352
+ top: `${item.top}%`,
353
+ height: `${item.height}%`,
354
+ left: `${(item.col / item.cols) * 100}%`,
355
+ width: `${100 / item.cols}%`,
356
+ }, children: renderEventTrigger(item.event, "flex h-full w-full flex-col overflow-hidden rounded-md px-1.5 py-1 text-left text-[11px] font-semibold leading-tight", _jsxs(_Fragment, { children: [_jsx("span", { className: "truncate", children: item.event.title }), _jsx("span", { className: "truncate text-[10px] font-medium opacity-80", children: formatTime(item.event.start, locale, hour12) })] }), { resize: "ns", variant: "time-grid" }) }, occurrenceKey(item.event))))] }, day.toISOString()));
357
+ })] }), showNow && nowTop > 0 && nowTop < hours.length * hourHeight ? (_jsx("div", { className: "pointer-events-none absolute right-0 left-14 z-10 h-px bg-destructive", style: { top: nowTop }, children: _jsx("span", { className: "absolute -top-1 -left-1 size-2 rounded-full bg-destructive" }) })) : null] })] }));
358
+ }
359
+ function AgendaList({ date, events, showWeekends, locale, hour12, onSlotClick, renderEventTrigger, }) {
360
+ const days = filterWeekendDays(agendaDays(date, AGENDA_LENGTH), showWeekends);
361
+ return (_jsx("div", { className: "divide-y divide-border/50", children: days.map((day) => {
362
+ const items = events.filter((event) => occupiesDay(event, day));
363
+ return (_jsxs("div", { className: "grid grid-cols-[7.5rem_minmax(0,1fr)] gap-3 px-3 py-3 sm:px-4", children: [_jsxs("button", { type: "button", "data-scheduler-day": isoDay(day), className: "text-left", onClick: () => onSlotClick?.({
364
+ start: new Date(day.getFullYear(), day.getMonth(), day.getDate(), 9),
365
+ end: new Date(day.getFullYear(), day.getMonth(), day.getDate(), 10),
366
+ allDay: false,
367
+ }), children: [_jsx("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: new Intl.DateTimeFormat(locale, { weekday: "short" }).format(day) }), _jsx("p", { className: cn("text-lg font-black", isSameDay(day, startOfToday()) && "text-primary"), children: new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" }).format(day) })] }), _jsx("div", { className: "min-w-0 space-y-1.5", children: items.length === 0 ? (_jsx("p", { className: "pt-2 text-xs text-muted-foreground", children: "No events" })) : (items.map((event) => (_jsx("div", { "data-scheduler-day": isoDay(day), children: renderEventTrigger(event, "flex w-full min-w-0 items-start gap-2 rounded-md px-2 py-1.5 text-left hover:bg-muted/40", _jsxs(_Fragment, { children: [_jsx("span", { className: "mt-1.5 size-2 shrink-0 rounded-full", style: eventColorStyle(event.color, "dot") }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate text-sm font-semibold text-foreground", children: event.title }), _jsx("span", { className: "text-[11px] text-muted-foreground", children: event.allDay || isMonthBar(event)
368
+ ? "All day"
369
+ : `${formatTime(event.start, locale, hour12)} – ${formatTime(event.end, locale, hour12)}` })] })] }), { variant: "agenda" }) }, occurrenceKey(event))))) })] }, day.toISOString()));
370
+ }) }));
371
+ }
@@ -0,0 +1,22 @@
1
+ import { type SchedulerColorTone, type SchedulerEvent, type SchedulerResource } from "../lib/scheduler";
2
+ export type EventEditorDraft = {
3
+ id?: string;
4
+ title?: string;
5
+ start: Date;
6
+ end: Date;
7
+ allDay?: boolean;
8
+ resourceId?: string;
9
+ color?: SchedulerColorTone;
10
+ description?: string;
11
+ rrule?: string;
12
+ };
13
+ export declare function EventEditor({ open, onOpenChange, draft, resources, readOnly, onSave, onDelete, }: {
14
+ open: boolean;
15
+ onOpenChange: (open: boolean) => void;
16
+ draft: EventEditorDraft | null;
17
+ resources?: SchedulerResource[];
18
+ readOnly?: boolean;
19
+ onSave: (event: SchedulerEvent) => void;
20
+ onDelete?: (id: string) => void;
21
+ }): import("react").JSX.Element;
22
+ //# sourceMappingURL=EventEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventEditor.d.ts","sourceRoot":"","sources":["../../src/composites/EventEditor.tsx"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;AAgB1B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,IAAI,CAAC;IACZ,GAAG,EAAE,IAAI,CAAC;IACV,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAWF,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,SAAc,EACd,QAAgB,EAChB,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,+BA4MA"}
@@ -0,0 +1,86 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { createEventId, fromDatetimeLocalValue, SCHEDULER_COLOR_TONES, toDatetimeLocalValue, toDate, } from "../lib/scheduler";
4
+ import { Button } from "../primitives/Button";
5
+ import { Checkbox } from "../primitives/Checkbox";
6
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../primitives/Dialog";
7
+ import { Input } from "../primitives/Input";
8
+ import { NativeSelect } from "../primitives/NativeSelect";
9
+ import { Textarea } from "../primitives/Textarea";
10
+ import { FormField } from "./FormField";
11
+ const RRULE_PRESETS = [
12
+ { value: "", label: "Does not repeat" },
13
+ { value: "FREQ=DAILY", label: "Daily" },
14
+ { value: "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR", label: "Every weekday" },
15
+ { value: "FREQ=WEEKLY", label: "Weekly" },
16
+ { value: "FREQ=MONTHLY", label: "Monthly" },
17
+ { value: "FREQ=YEARLY", label: "Yearly" },
18
+ ];
19
+ export function EventEditor({ open, onOpenChange, draft, resources = [], readOnly = false, onSave, onDelete, }) {
20
+ const [title, setTitle] = useState("");
21
+ const [start, setStart] = useState("");
22
+ const [end, setEnd] = useState("");
23
+ const [allDay, setAllDay] = useState(false);
24
+ const [resourceId, setResourceId] = useState("");
25
+ const [color, setColor] = useState("");
26
+ const [description, setDescription] = useState("");
27
+ const [rrule, setRrule] = useState("");
28
+ useEffect(() => {
29
+ if (!draft || !open)
30
+ return;
31
+ setTitle(draft.title ?? "");
32
+ setStart(draft.allDay
33
+ ? toDatetimeLocalValue(toDate(draft.start)).slice(0, 10)
34
+ : toDatetimeLocalValue(toDate(draft.start)));
35
+ setEnd(draft.allDay
36
+ ? toDatetimeLocalValue(toDate(draft.end)).slice(0, 10)
37
+ : toDatetimeLocalValue(toDate(draft.end)));
38
+ setAllDay(Boolean(draft.allDay));
39
+ setResourceId(draft.resourceId ?? "");
40
+ setColor(draft.color ?? "");
41
+ setDescription(draft.description ?? "");
42
+ setRrule(draft.rrule ?? "");
43
+ }, [draft, open]);
44
+ const locked = readOnly;
45
+ const isEdit = Boolean(draft?.id);
46
+ function submit() {
47
+ if (!draft || locked)
48
+ return;
49
+ const nextStart = allDay ? fromDatetimeLocalValue(`${start}T00:00`) : fromDatetimeLocalValue(start);
50
+ const nextEnd = allDay
51
+ ? fromDatetimeLocalValue(`${end || start}T00:00`)
52
+ : fromDatetimeLocalValue(end || start);
53
+ onSave({
54
+ id: draft.id ?? createEventId(),
55
+ title: title.trim() || "Untitled",
56
+ start: nextStart,
57
+ end: nextEnd.getTime() <= nextStart.getTime()
58
+ ? new Date(nextStart.getTime() + (allDay ? 86_400_000 : 3_600_000))
59
+ : nextEnd,
60
+ allDay,
61
+ resourceId: resourceId || undefined,
62
+ color: color || undefined,
63
+ description: description.trim() || undefined,
64
+ rrule: rrule || undefined,
65
+ });
66
+ onOpenChange(false);
67
+ }
68
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: locked ? "Event" : isEdit ? "Edit event" : "New event" }), _jsx(DialogDescription, { children: locked ? "This calendar is read-only." : "Title, time, resource, and recurrence." })] }), draft ? (_jsxs("form", { className: "grid gap-3", onSubmit: (event) => {
69
+ event.preventDefault();
70
+ submit();
71
+ }, children: [_jsx(FormField, { id: "spk-event-title", label: "Title", required: true, children: _jsx(Input, { id: "spk-event-title", value: title, onChange: (event) => setTitle(event.target.value), disabled: locked, autoFocus: !locked }) }), _jsxs("label", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(Checkbox, { checked: allDay, onCheckedChange: (value) => {
72
+ const next = value === true;
73
+ setAllDay(next);
74
+ if (next) {
75
+ setStart(start.slice(0, 10));
76
+ setEnd((end || start).slice(0, 10));
77
+ }
78
+ else {
79
+ setStart(start.length <= 10 ? `${start}T09:00` : start);
80
+ setEnd((end || start).length <= 10 ? `${end || start}T10:00` : end);
81
+ }
82
+ }, disabled: locked }), "All day"] }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsx(FormField, { id: "spk-event-start", label: "Start", children: _jsx(Input, { id: "spk-event-start", type: allDay ? "date" : "datetime-local", value: start, onChange: (event) => setStart(event.target.value), disabled: locked }) }), _jsx(FormField, { id: "spk-event-end", label: "End", children: _jsx(Input, { id: "spk-event-end", type: allDay ? "date" : "datetime-local", value: end, onChange: (event) => setEnd(event.target.value), disabled: locked }) })] }), resources.length > 0 ? (_jsx(FormField, { id: "spk-event-resource", label: "Resource", children: _jsxs(NativeSelect, { id: "spk-event-resource", value: resourceId, onChange: (event) => setResourceId(event.target.value), disabled: locked, className: "h-9 min-h-9", children: [_jsx("option", { value: "", children: "None" }), resources.map((resource) => (_jsx("option", { value: resource.id, children: resource.title }, resource.id)))] }) })) : null, _jsx(FormField, { id: "spk-event-color", label: "Color", children: _jsxs(NativeSelect, { id: "spk-event-color", value: color, onChange: (event) => setColor(event.target.value), disabled: locked, className: "h-9 min-h-9", children: [_jsx("option", { value: "", children: "From resource" }), SCHEDULER_COLOR_TONES.map((tone) => (_jsx("option", { value: tone, children: tone }, tone)))] }) }), _jsx(FormField, { id: "spk-event-repeat", label: "Repeat", children: _jsxs(NativeSelect, { id: "spk-event-repeat", value: rrule, onChange: (event) => setRrule(event.target.value), disabled: locked, className: "h-9 min-h-9", children: [RRULE_PRESETS.map((preset) => (_jsx("option", { value: preset.value, children: preset.label }, preset.value || "none"))), rrule && !RRULE_PRESETS.some((preset) => preset.value === rrule) ? (_jsx("option", { value: rrule, children: "Custom" })) : null] }) }), _jsx(FormField, { id: "spk-event-notes", label: "Description", children: _jsx(Textarea, { id: "spk-event-notes", value: description, onChange: (event) => setDescription(event.target.value), disabled: locked, rows: 3 }) }), _jsxs(DialogFooter, { children: [isEdit && onDelete && !locked ? (_jsx(Button, { type: "button", variant: "destructive", className: "sm:mr-auto", onClick: () => {
83
+ onDelete(draft.id);
84
+ onOpenChange(false);
85
+ }, children: "Delete" })) : null, _jsx(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), children: locked ? "Close" : "Cancel" }), !locked ? _jsx(Button, { type: "submit", children: "Save" }) : null] })] })) : null] }) }));
86
+ }
@@ -0,0 +1,29 @@
1
+ import { type SchedulerEvent, type SchedulerResource, type TimelineScale } from "../lib/scheduler";
2
+ export type EventTimelineProps = {
3
+ events?: SchedulerEvent[];
4
+ resources?: SchedulerResource[];
5
+ scale?: TimelineScale;
6
+ defaultScale?: TimelineScale;
7
+ onScaleChange?: (scale: TimelineScale) => void;
8
+ date?: Date;
9
+ defaultDate?: Date;
10
+ onDateChange?: (date: Date) => void;
11
+ scales?: TimelineScale[];
12
+ locale?: string;
13
+ readOnly?: boolean;
14
+ areEventsDraggable?: boolean;
15
+ areEventsResizable?: boolean;
16
+ onEventClick?: (event: SchedulerEvent) => void;
17
+ onEventChange?: (event: SchedulerEvent, next: Partial<SchedulerEvent>) => void;
18
+ onEventCreate?: (event: SchedulerEvent) => void;
19
+ onEventDelete?: (event: SchedulerEvent) => void;
20
+ /**
21
+ * Built-in create/edit dialog. Defaults to true.
22
+ * Set false when the host owns create/edit.
23
+ */
24
+ showEventEditor?: boolean;
25
+ className?: string;
26
+ };
27
+ /** Horizontal resource timeline — rows are resources, bars are allocations across time. */
28
+ export declare function EventTimeline({ events, resources, scale: scaleProp, defaultScale, onScaleChange, date: dateProp, defaultDate, onDateChange, scales, locale, readOnly, areEventsDraggable, areEventsResizable, onEventClick, onEventChange, onEventCreate, onEventDelete, showEventEditor, className, }: EventTimelineProps): import("react").JSX.Element;
29
+ //# sourceMappingURL=EventTimeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventTimeline.d.ts","sourceRoot":"","sources":["../../src/composites/EventTimeline.tsx"],"names":[],"mappings":"AAGA,OAAO,EAeL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAW1B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAChC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC/C,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACpC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC/E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAmCF,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,EAC5B,MAAW,EACX,SAAc,EACd,KAAK,EAAE,SAAS,EAChB,YAAuB,EACvB,aAAa,EACb,IAAI,EAAE,QAAQ,EACd,WAAW,EACX,YAAY,EACZ,MAAsD,EACtD,MAAM,EACN,QAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAsB,EACtB,SAAS,GACV,EAAE,kBAAkB,+BA6PpB"}