@spatika/react 1.1.0 → 1.2.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 +35 -0
  16. package/dist/composites/EventCalendar.d.ts.map +1 -0
  17. package/dist/composites/EventCalendar.js +339 -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 +24 -0
  22. package/dist/composites/EventTimeline.d.ts.map +1 -0
  23. package/dist/composites/EventTimeline.js +184 -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 +43 -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 +67 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +57 -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 +152 -0
  84. package/dist/lib/scheduler.d.ts.map +1 -0
  85. package/dist/lib/scheduler.js +501 -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 +928 -0
  219. package/src/composites/EventEditor.tsx +268 -0
  220. package/src/composites/EventTimeline.tsx +357 -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 +304 -0
  250. package/src/composites/scheduler.test.tsx +194 -0
  251. package/src/index.test.ts +22 -0
  252. package/src/index.ts +277 -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 +186 -0
  265. package/src/lib/scheduler.ts +688 -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,501 @@
1
+ /**
2
+ * Date math and layout helpers for EventCalendar / EventTimeline.
3
+ * Local timezone throughout — date-only ISO strings parse as local midnight.
4
+ */
5
+ export const SCHEDULER_COLOR_TONES = [
6
+ "blue",
7
+ "violet",
8
+ "teal",
9
+ "orange",
10
+ "rose",
11
+ "slate",
12
+ ];
13
+ export const DEFAULT_SCHEDULER_PREFERENCES = {
14
+ showWeekends: true,
15
+ showWeekNumber: false,
16
+ ampm: true,
17
+ weekStartsOn: 0,
18
+ };
19
+ export const SCHEDULER_COLOR_VARS = {
20
+ blue: "var(--chart-1)",
21
+ violet: "var(--chart-2)",
22
+ teal: "var(--chart-3)",
23
+ orange: "var(--chart-4)",
24
+ rose: "var(--destructive)",
25
+ slate: "var(--chart-5)",
26
+ };
27
+ const TONE_CYCLE = [
28
+ "blue",
29
+ "violet",
30
+ "teal",
31
+ "orange",
32
+ "rose",
33
+ "slate",
34
+ ];
35
+ export function toDate(value) {
36
+ if (value instanceof Date)
37
+ return new Date(value.getTime());
38
+ const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
39
+ if (dateOnly) {
40
+ return new Date(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]));
41
+ }
42
+ const localDateTime = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?$/.exec(value);
43
+ if (localDateTime) {
44
+ return new Date(Number(localDateTime[1]), Number(localDateTime[2]) - 1, Number(localDateTime[3]), Number(localDateTime[4]), Number(localDateTime[5]), Number(localDateTime[6] ?? 0), Number((localDateTime[7] ?? "0").slice(0, 3).padEnd(3, "0")));
45
+ }
46
+ return new Date(value);
47
+ }
48
+ export function startOfDay(date) {
49
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
50
+ }
51
+ export function addDays(date, amount) {
52
+ const next = new Date(date.getTime());
53
+ next.setDate(next.getDate() + amount);
54
+ return next;
55
+ }
56
+ export function addMonths(date, amount) {
57
+ const next = new Date(date.getTime());
58
+ next.setMonth(next.getMonth() + amount);
59
+ return next;
60
+ }
61
+ export function startOfMonth(date) {
62
+ return new Date(date.getFullYear(), date.getMonth(), 1);
63
+ }
64
+ export function startOfWeek(date, weekStartsOn = 0) {
65
+ const start = startOfDay(date);
66
+ const shift = (start.getDay() - weekStartsOn + 7) % 7;
67
+ return addDays(start, -shift);
68
+ }
69
+ export function isSameDay(a, b) {
70
+ return (a.getFullYear() === b.getFullYear() &&
71
+ a.getMonth() === b.getMonth() &&
72
+ a.getDate() === b.getDate());
73
+ }
74
+ export function isSameMonth(a, b) {
75
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
76
+ }
77
+ export function differenceInDays(later, earlier) {
78
+ return Math.round((startOfDay(later).getTime() - startOfDay(earlier).getTime()) / 86_400_000);
79
+ }
80
+ export function differenceInMs(later, earlier) {
81
+ return later.getTime() - earlier.getTime();
82
+ }
83
+ export function addMs(date, ms) {
84
+ return new Date(date.getTime() + ms);
85
+ }
86
+ export function clampDate(date, min, max) {
87
+ const t = date.getTime();
88
+ if (t < min.getTime())
89
+ return new Date(min.getTime());
90
+ if (t > max.getTime())
91
+ return new Date(max.getTime());
92
+ return date;
93
+ }
94
+ export function getMonthGrid(date, weekStartsOn = 0) {
95
+ const start = startOfWeek(startOfMonth(date), weekStartsOn);
96
+ return Array.from({ length: 42 }, (_, i) => addDays(start, i));
97
+ }
98
+ export function getWeekDays(date, weekStartsOn = 0) {
99
+ const start = startOfWeek(date, weekStartsOn);
100
+ return Array.from({ length: 7 }, (_, i) => addDays(start, i));
101
+ }
102
+ export function weekdayLabels(weekStartsOn = 0, locale) {
103
+ const sunday = new Date(2026, 7, 2);
104
+ return Array.from({ length: 7 }, (_, i) => {
105
+ const day = addDays(sunday, (weekStartsOn + i) % 7);
106
+ return new Intl.DateTimeFormat(locale, { weekday: "short" }).format(day);
107
+ });
108
+ }
109
+ export function formatMonthYear(date, locale) {
110
+ return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(date);
111
+ }
112
+ export function formatWeekRange(date, weekStartsOn = 0, locale) {
113
+ const days = getWeekDays(date, weekStartsOn);
114
+ const start = days[0];
115
+ const end = days[6];
116
+ const startFmt = new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" }).format(start);
117
+ const endFmt = new Intl.DateTimeFormat(locale, {
118
+ month: start.getMonth() === end.getMonth() ? undefined : "short",
119
+ day: "numeric",
120
+ year: "numeric",
121
+ }).format(end);
122
+ return `${startFmt} – ${endFmt}`;
123
+ }
124
+ export function formatDayHeading(date, locale) {
125
+ return new Intl.DateTimeFormat(locale, {
126
+ weekday: "long",
127
+ month: "long",
128
+ day: "numeric",
129
+ year: "numeric",
130
+ }).format(date);
131
+ }
132
+ export function formatTime(date, locale, hour12 = true) {
133
+ return new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", hour12 }).format(date);
134
+ }
135
+ export function formatHour(hour, locale, hour12 = true) {
136
+ const date = new Date(2026, 0, 1, hour, 0);
137
+ return new Intl.DateTimeFormat(locale, { hour: "numeric", hour12 }).format(date);
138
+ }
139
+ export function formatDateRange(start, end, allDay, locale, hour12 = true) {
140
+ if (allDay) {
141
+ const last = addMs(end, -1);
142
+ if (isSameDay(start, last)) {
143
+ return new Intl.DateTimeFormat(locale, {
144
+ weekday: "short",
145
+ month: "short",
146
+ day: "numeric",
147
+ }).format(start);
148
+ }
149
+ const fmt = new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" });
150
+ return `${fmt.format(start)} – ${fmt.format(last)}`;
151
+ }
152
+ if (isSameDay(start, end) || isSameDay(start, addMs(end, -1))) {
153
+ return `${formatTime(start, locale, hour12)} – ${formatTime(end, locale, hour12)}`;
154
+ }
155
+ const dayFmt = new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" });
156
+ return `${dayFmt.format(start)} ${formatTime(start, locale, hour12)} – ${dayFmt.format(end)} ${formatTime(end, locale, hour12)}`;
157
+ }
158
+ export function formatDateSpan(start, end, locale) {
159
+ const sameYear = start.getFullYear() === end.getFullYear();
160
+ const startFmt = new Intl.DateTimeFormat(locale, {
161
+ month: "short",
162
+ day: "numeric",
163
+ year: sameYear ? undefined : "numeric",
164
+ }).format(start);
165
+ const endFmt = new Intl.DateTimeFormat(locale, {
166
+ month: "short",
167
+ day: "numeric",
168
+ year: "numeric",
169
+ }).format(end);
170
+ return `${startFmt} – ${endFmt}`;
171
+ }
172
+ export function occurrenceKey(event) {
173
+ return event.occurrenceStart ? `${event.id}@${event.occurrenceStart.getTime()}` : event.id;
174
+ }
175
+ export function eventCanDrag(event, options) {
176
+ if (options.readOnly || event.readOnly || options.enabled === false || event.draggable === false) {
177
+ return false;
178
+ }
179
+ return true;
180
+ }
181
+ export function eventCanResize(event, options) {
182
+ if (options.readOnly || event.readOnly || options.enabled === false || event.resizable === false) {
183
+ return false;
184
+ }
185
+ return true;
186
+ }
187
+ export function resolveEventColor(event, resources = []) {
188
+ if (event.color)
189
+ return event.color;
190
+ const resource = resources.find((item) => item.id === event.resourceId);
191
+ if (resource?.color)
192
+ return resource.color;
193
+ if (event.resourceId) {
194
+ const index = resources.findIndex((item) => item.id === event.resourceId);
195
+ return TONE_CYCLE[(index >= 0 ? index : hashString(event.resourceId)) % TONE_CYCLE.length];
196
+ }
197
+ return "blue";
198
+ }
199
+ function hashString(value) {
200
+ let hash = 0;
201
+ for (let i = 0; i < value.length; i += 1)
202
+ hash = (hash * 31 + value.charCodeAt(i)) | 0;
203
+ return Math.abs(hash);
204
+ }
205
+ export function normalizeEvents(events, resources = []) {
206
+ return events
207
+ .map((event) => {
208
+ const start = toDate(event.start);
209
+ const end = toDate(event.end);
210
+ const ordered = end.getTime() >= start.getTime();
211
+ return {
212
+ id: event.id,
213
+ title: event.title,
214
+ start: ordered ? start : end,
215
+ end: ordered ? end : start,
216
+ resourceId: event.resourceId,
217
+ allDay: Boolean(event.allDay),
218
+ color: resolveEventColor(event, resources),
219
+ description: event.description,
220
+ rrule: event.rrule,
221
+ exceptionDates: event.exceptionDates,
222
+ timezone: event.timezone,
223
+ readOnly: event.readOnly,
224
+ draggable: event.draggable,
225
+ resizable: event.resizable,
226
+ };
227
+ })
228
+ .sort((a, b) => a.start.getTime() - b.start.getTime() || a.title.localeCompare(b.title));
229
+ }
230
+ /** Last occupied instant is exclusive-end minus 1ms. */
231
+ export function lastInstant(event) {
232
+ return addMs(event.end, event.end.getTime() === startOfDay(event.end).getTime() ? -1 : 0);
233
+ }
234
+ export function occupiesDay(event, day) {
235
+ const dayStart = startOfDay(day);
236
+ const dayEnd = addDays(dayStart, 1);
237
+ return event.start.getTime() < dayEnd.getTime() && event.end.getTime() > dayStart.getTime();
238
+ }
239
+ export function isMonthBar(event) {
240
+ if (event.allDay)
241
+ return true;
242
+ return !isSameDay(event.start, lastInstant(event));
243
+ }
244
+ export function weekSpanLanes(weekStart, events) {
245
+ return weekSpanLanesForDays(Array.from({ length: 7 }, (_, i) => addDays(weekStart, i)), events);
246
+ }
247
+ export function weekSpanLanesForDays(days, events) {
248
+ if (days.length === 0)
249
+ return [];
250
+ const weekStart = startOfDay(days[0]);
251
+ const weekEnd = addDays(startOfDay(days[days.length - 1]), 1);
252
+ const indexByKey = new Map(days.map((day, index) => [dayKey(day), index]));
253
+ const segments = [];
254
+ for (const event of events) {
255
+ if (!isMonthBar(event))
256
+ continue;
257
+ if (event.end.getTime() <= weekStart.getTime() || event.start.getTime() >= weekEnd.getTime()) {
258
+ continue;
259
+ }
260
+ let first = -1;
261
+ let last = -1;
262
+ for (const day of days) {
263
+ if (!occupiesDay(event, day))
264
+ continue;
265
+ const index = indexByKey.get(dayKey(day)) ?? -1;
266
+ if (index < 0)
267
+ continue;
268
+ if (first === -1)
269
+ first = index;
270
+ last = index;
271
+ }
272
+ if (first === -1)
273
+ continue;
274
+ segments.push({ event, startCol: first, span: last - first + 1 });
275
+ }
276
+ segments.sort((a, b) => a.startCol - b.startCol || b.span - a.span);
277
+ const lanes = [];
278
+ for (const segment of segments) {
279
+ const lane = lanes.find((items) => items.every((item) => item.startCol + item.span <= segment.startCol));
280
+ if (lane)
281
+ lane.push(segment);
282
+ else
283
+ lanes.push([segment]);
284
+ }
285
+ return lanes;
286
+ }
287
+ function dayKey(date) {
288
+ return `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
289
+ }
290
+ export function isoDay(date) {
291
+ const month = String(date.getMonth() + 1).padStart(2, "0");
292
+ const day = String(date.getDate()).padStart(2, "0");
293
+ return `${date.getFullYear()}-${month}-${day}`;
294
+ }
295
+ export function filterWeekendDays(days, showWeekends) {
296
+ if (showWeekends)
297
+ return days;
298
+ return days.filter((day) => day.getDay() !== 0 && day.getDay() !== 6);
299
+ }
300
+ export function weekNumber(date) {
301
+ const utc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
302
+ const day = new Date(utc);
303
+ day.setUTCDate(day.getUTCDate() + 4 - (day.getUTCDay() || 7));
304
+ const yearStart = Date.UTC(day.getUTCFullYear(), 0, 1);
305
+ return Math.ceil(((utc - yearStart) / 86_400_000 + 1) / 7);
306
+ }
307
+ export function agendaDays(date, count = 14) {
308
+ const start = startOfDay(date);
309
+ return Array.from({ length: count }, (_, i) => addDays(start, i));
310
+ }
311
+ export function snapMs(ms, unit = 15 * 60_000) {
312
+ return Math.round(ms / unit) * unit;
313
+ }
314
+ export function resizeEvent(event, edge, nextInstant) {
315
+ const min = 15 * 60_000;
316
+ if (edge === "start") {
317
+ const start = new Date(Math.min(nextInstant.getTime(), event.end.getTime() - min));
318
+ return { start, end: event.end };
319
+ }
320
+ const end = new Date(Math.max(nextInstant.getTime(), event.start.getTime() + min));
321
+ return { start: event.start, end };
322
+ }
323
+ export function toDatetimeLocalValue(date) {
324
+ const pad = (value) => String(value).padStart(2, "0");
325
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
326
+ }
327
+ export function fromDatetimeLocalValue(value) {
328
+ return toDate(value);
329
+ }
330
+ export function createEventId() {
331
+ return `evt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
332
+ }
333
+ export function timedEventsOnDay(events, day) {
334
+ return events.filter((event) => !isMonthBar(event) && occupiesDay(event, day));
335
+ }
336
+ export function layoutTimedEvents(events, day, hourStart, hourEnd) {
337
+ const dayStart = startOfDay(day);
338
+ const windowStart = new Date(dayStart.getTime() + hourStart * 3_600_000);
339
+ const windowEnd = new Date(dayStart.getTime() + hourEnd * 3_600_000);
340
+ const windowMs = Math.max(1, windowEnd.getTime() - windowStart.getTime());
341
+ const items = events
342
+ .filter((event) => !event.allDay && occupiesDay(event, day))
343
+ .map((event) => {
344
+ const start = clampDate(event.start, windowStart, windowEnd);
345
+ const end = clampDate(event.end, windowStart, windowEnd);
346
+ const top = ((start.getTime() - windowStart.getTime()) / windowMs) * 100;
347
+ const height = Math.max(3.2, ((end.getTime() - start.getTime()) / windowMs) * 100);
348
+ return { event, start, end, top, height, col: 0, cols: 1 };
349
+ })
350
+ .sort((a, b) => a.start.getTime() - b.start.getTime() || b.end.getTime() - a.end.getTime());
351
+ const columns = [];
352
+ let active = [];
353
+ for (let i = 0; i < items.length; i += 1) {
354
+ const item = items[i];
355
+ active = active.filter((idx) => items[idx].end.getTime() > item.start.getTime());
356
+ let col = columns.findIndex((column) => column.end <= item.start.getTime());
357
+ if (col === -1) {
358
+ col = columns.length;
359
+ columns.push({ end: item.end.getTime() });
360
+ }
361
+ else {
362
+ columns[col].end = item.end.getTime();
363
+ }
364
+ item.col = col;
365
+ active.push(i);
366
+ const clusterCols = Math.max(columns.length, active.length);
367
+ for (const idx of active)
368
+ items[idx].cols = Math.max(items[idx].cols, clusterCols);
369
+ }
370
+ return items.map(({ event, col, cols, top, height }) => ({ event, col, cols, top, height }));
371
+ }
372
+ export function shiftByDays(event, days) {
373
+ return { start: addDays(event.start, days), end: addDays(event.end, days) };
374
+ }
375
+ export function shiftByMs(event, ms) {
376
+ return { start: addMs(event.start, ms), end: addMs(event.end, ms) };
377
+ }
378
+ export function hoursInView(hourStart, hourEnd) {
379
+ const hours = [];
380
+ for (let hour = hourStart; hour < hourEnd; hour += 1)
381
+ hours.push(hour);
382
+ return hours;
383
+ }
384
+ export function getTimelineRange(date, scale, locale) {
385
+ if (scale === "hours") {
386
+ const start = startOfDay(date);
387
+ const end = addDays(start, 2);
388
+ const ticks = Array.from({ length: 8 }, (_, i) => {
389
+ const tick = addMs(start, i * 6 * 3_600_000);
390
+ return {
391
+ date: tick,
392
+ label: new Intl.DateTimeFormat(locale, { weekday: "short", hour: "numeric" }).format(tick),
393
+ };
394
+ });
395
+ return { start, end, ticks };
396
+ }
397
+ if (scale === "days") {
398
+ const start = startOfDay(addDays(date, -3));
399
+ const end = addDays(start, 14);
400
+ const ticks = Array.from({ length: 14 }, (_, i) => {
401
+ const tick = addDays(start, i);
402
+ return {
403
+ date: tick,
404
+ label: new Intl.DateTimeFormat(locale, { weekday: "short", day: "numeric" }).format(tick),
405
+ };
406
+ });
407
+ return { start, end, ticks };
408
+ }
409
+ if (scale === "weeks") {
410
+ const start = startOfWeek(addDays(date, -14), 1);
411
+ const end = addDays(start, 7 * 8);
412
+ const ticks = Array.from({ length: 8 }, (_, i) => {
413
+ const tick = addDays(start, i * 7);
414
+ return {
415
+ date: tick,
416
+ label: new Intl.DateTimeFormat(locale, { month: "short", day: "numeric" }).format(tick),
417
+ };
418
+ });
419
+ return { start, end, ticks };
420
+ }
421
+ const start = new Date(date.getFullYear(), date.getMonth() - 1, 1);
422
+ if (scale === "years") {
423
+ const yearStart = new Date(date.getFullYear() - 1, 0, 1);
424
+ const yearEnd = new Date(date.getFullYear() + 4, 0, 1);
425
+ const ticks = Array.from({ length: 5 }, (_, i) => {
426
+ const tick = new Date(date.getFullYear() - 1 + i, 0, 1);
427
+ return { date: tick, label: String(tick.getFullYear()) };
428
+ });
429
+ return { start: yearStart, end: yearEnd, ticks };
430
+ }
431
+ const end = addMonths(start, 5);
432
+ const ticks = Array.from({ length: 5 }, (_, i) => {
433
+ const tick = addMonths(start, i);
434
+ return {
435
+ date: tick,
436
+ label: new Intl.DateTimeFormat(locale, { month: "short" }).format(tick),
437
+ };
438
+ });
439
+ return { start, end, ticks };
440
+ }
441
+ export function shiftTimelineDate(date, scale, direction) {
442
+ if (scale === "hours")
443
+ return addDays(date, direction * 2);
444
+ if (scale === "days")
445
+ return addDays(date, direction * 14);
446
+ if (scale === "weeks")
447
+ return addDays(date, direction * 28);
448
+ if (scale === "years")
449
+ return addMonths(date, direction * 12);
450
+ return addMonths(date, direction * 3);
451
+ }
452
+ export function layoutTimelineBars(events, rangeStart, rangeEnd) {
453
+ const rangeMs = Math.max(1, rangeEnd.getTime() - rangeStart.getTime());
454
+ const items = events
455
+ .filter((event) => event.end.getTime() > rangeStart.getTime() && event.start.getTime() < rangeEnd.getTime())
456
+ .map((event) => {
457
+ const start = clampDate(event.start, rangeStart, rangeEnd);
458
+ const end = clampDate(event.end, rangeStart, rangeEnd);
459
+ const left = ((start.getTime() - rangeStart.getTime()) / rangeMs) * 100;
460
+ const width = Math.max(1.2, ((end.getTime() - start.getTime()) / rangeMs) * 100);
461
+ return { event, start, end, left, width, lane: 0 };
462
+ })
463
+ .sort((a, b) => a.start.getTime() - b.start.getTime());
464
+ const laneEnds = [];
465
+ for (const item of items) {
466
+ let lane = laneEnds.findIndex((end) => end <= item.start.getTime());
467
+ if (lane === -1) {
468
+ lane = laneEnds.length;
469
+ laneEnds.push(item.end.getTime());
470
+ }
471
+ else {
472
+ laneEnds[lane] = item.end.getTime();
473
+ }
474
+ item.lane = lane;
475
+ }
476
+ return items.map(({ event, lane, left, width }) => ({ event, lane, left, width }));
477
+ }
478
+ export function eventColorStyle(tone, variant) {
479
+ const color = SCHEDULER_COLOR_VARS[tone];
480
+ if (variant === "dot")
481
+ return { backgroundColor: color };
482
+ if (variant === "bar") {
483
+ return {
484
+ backgroundColor: `color-mix(in srgb, ${color} 88%, transparent)`,
485
+ color: "var(--primary-foreground)",
486
+ };
487
+ }
488
+ if (variant === "wash") {
489
+ return { backgroundColor: `color-mix(in srgb, ${color} 16%, transparent)`, color };
490
+ }
491
+ return {
492
+ backgroundColor: `color-mix(in srgb, ${color} 18%, transparent)`,
493
+ borderLeft: `3px solid ${color}`,
494
+ color,
495
+ };
496
+ }
497
+ export function visibleEvents(events, hiddenResourceIds) {
498
+ if (hiddenResourceIds.size === 0)
499
+ return events;
500
+ return events.filter((event) => !event.resourceId || !hiddenResourceIds.has(event.resourceId));
501
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Raw WebGL scatter renderer — no three.js.
3
+ * Canvas sits behind SVG axes; hit-testing stays on the CPU.
4
+ */
5
+ export type ScatterRenderer = "svg" | "webgl" | "auto";
6
+ export declare const WEBGL_AUTO_THRESHOLD = 2000;
7
+ export declare function shouldUseWebGL(renderer: ScatterRenderer | undefined, pointCount: number): boolean;
8
+ export type Rgba = [number, number, number, number];
9
+ export declare function parseCssColor(css: string): Rgba | null;
10
+ export declare function resolveCssColor(color: string, el?: Element | null): Rgba;
11
+ export type ScatterHitCandidate = {
12
+ x: number;
13
+ y: number;
14
+ r: number;
15
+ seriesIndex: number;
16
+ pointIndex: number;
17
+ };
18
+ export declare function nearestScatterHit(candidates: ScatterHitCandidate[], px: number, py: number, pad?: number): ScatterHitCandidate | null;
19
+ export declare function svgPointerPosition(svg: SVGSVGElement, clientX: number, clientY: number, width: number, height: number): {
20
+ x: number;
21
+ y: number;
22
+ };
23
+ export type ScatterGL = {
24
+ gl: WebGLRenderingContext;
25
+ program: WebGLProgram;
26
+ buffer: WebGLBuffer;
27
+ aPosition: number;
28
+ aSize: number;
29
+ uResolution: WebGLUniformLocation | null;
30
+ uDpr: WebGLUniformLocation | null;
31
+ uColor: WebGLUniformLocation | null;
32
+ };
33
+ export declare function createScatterGL(canvas: HTMLCanvasElement): ScatterGL | null;
34
+ export type ScatterGLSeries = {
35
+ positions: Array<{
36
+ x: number;
37
+ y: number;
38
+ size: number;
39
+ }>;
40
+ color: string;
41
+ };
42
+ export declare function drawScatterGL(gpu: ScatterGL, canvas: HTMLCanvasElement, width: number, height: number, series: ScatterGLSeries[]): void;
43
+ export declare function destroyScatterGL(gpu: ScatterGL): void;
44
+ //# sourceMappingURL=webgl-scatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webgl-scatter.d.ts","sourceRoot":"","sources":["../../src/lib/webgl-scatter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAEvD,eAAO,MAAM,oBAAoB,OAAO,CAAC;AAEzC,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAIjG;AAED,MAAM,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEpD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAoBtD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAaxE;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,mBAAmB,EAAE,EACjC,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,GAAG,SAAI,GACN,mBAAmB,GAAG,IAAI,CAa5B;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAQ1B;AAyBD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,qBAAqB,CAAC;IAC1B,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACzC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACrC,CAAC;AAcF,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CA8B3E;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,aAAa,CAC3B,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,eAAe,EAAE,GACxB,IAAI,CAoCN;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAIrD"}