@spatika/react 1.4.0 → 1.5.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 (93) hide show
  1. package/AGENTS.md +6 -3
  2. package/README.md +1 -1
  3. package/dist/composites/EventCalendar.js +3 -3
  4. package/dist/composites/GlassCard.d.ts +1 -1
  5. package/dist/composites/RichTextEditor.d.ts +1 -4
  6. package/dist/composites/RichTextEditor.d.ts.map +1 -1
  7. package/dist/composites/RichTextEditor.js +12 -19
  8. package/dist/composites/SurfaceCard.d.ts +1 -1
  9. package/dist/composites/Tag.d.ts +2 -2
  10. package/dist/index.d.ts +8 -22
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +5 -11
  13. package/dist/lib/breakpoints.d.ts +17 -0
  14. package/dist/lib/breakpoints.d.ts.map +1 -0
  15. package/dist/lib/breakpoints.js +23 -0
  16. package/dist/lib/create-theme.d.ts +162 -0
  17. package/dist/lib/create-theme.d.ts.map +1 -0
  18. package/dist/lib/create-theme.js +138 -0
  19. package/dist/lib/scheduler.js +2 -2
  20. package/dist/lib/themes.d.ts +17 -7
  21. package/dist/lib/themes.d.ts.map +1 -1
  22. package/dist/lib/themes.js +22 -8
  23. package/dist/lib/use-media-query.d.ts +9 -0
  24. package/dist/lib/use-media-query.d.ts.map +1 -1
  25. package/dist/lib/use-media-query.js +28 -1
  26. package/dist/primitives/Alert.d.ts +1 -1
  27. package/dist/primitives/Badge.d.ts +2 -2
  28. package/dist/primitives/Button.d.ts +2 -2
  29. package/dist/primitives/Container.d.ts +2 -2
  30. package/dist/primitives/Fab.d.ts +4 -4
  31. package/dist/primitives/Grid.d.ts +2 -1
  32. package/dist/primitives/Grid.d.ts.map +1 -1
  33. package/dist/primitives/Grid.js +10 -2
  34. package/dist/primitives/IconButton.d.ts +2 -2
  35. package/dist/primitives/ToggleButton.d.ts +1 -1
  36. package/dist/theme/SpatikaThemeProvider.d.ts +4 -5
  37. package/dist/theme/SpatikaThemeProvider.d.ts.map +1 -1
  38. package/dist/theme/SpatikaThemeProvider.js +28 -13
  39. package/package.json +4 -2
  40. package/skills/spatika-ui/SKILL.md +25 -6
  41. package/src/composites/EventCalendar.test.tsx +293 -0
  42. package/src/composites/EventCalendar.tsx +3 -3
  43. package/src/composites/RichTextEditor.test.tsx +51 -0
  44. package/src/composites/RichTextEditor.tsx +20 -84
  45. package/src/composites/chart-ui.test.tsx +21 -0
  46. package/src/composites/scheduler.test.tsx +0 -258
  47. package/src/index.test.ts +6 -0
  48. package/src/index.ts +138 -72
  49. package/src/lib/breakpoints.test.ts +29 -0
  50. package/src/lib/breakpoints.ts +28 -0
  51. package/src/lib/create-theme.test.ts +79 -0
  52. package/src/lib/create-theme.ts +249 -0
  53. package/src/lib/scheduler.test.ts +10 -0
  54. package/src/lib/scheduler.ts +2 -2
  55. package/src/lib/themes.test.ts +17 -0
  56. package/src/lib/themes.ts +43 -12
  57. package/src/lib/use-media-query.test.tsx +80 -0
  58. package/src/lib/use-media-query.ts +35 -1
  59. package/src/primitives/Grid.test.tsx +55 -0
  60. package/src/primitives/Grid.tsx +13 -3
  61. package/src/primitives/mui-more.test.tsx +17 -0
  62. package/src/theme/SpatikaThemeProvider.test.tsx +38 -0
  63. package/src/theme/SpatikaThemeProvider.tsx +43 -15
  64. package/src/composites/ChartDataGrid.test.tsx +0 -57
  65. package/src/composites/ChartDataGrid.tsx +0 -196
  66. package/src/composites/MapChart.test.tsx +0 -39
  67. package/src/composites/MapChart.tsx +0 -130
  68. package/src/composites/ScatterWebGL.test.tsx +0 -25
  69. package/src/composites/ScatterWebGL.tsx +0 -79
  70. package/src/composites/cartesian-charts.test.tsx +0 -136
  71. package/src/composites/cartesian-charts.tsx +0 -1355
  72. package/src/composites/chart-composition.test.tsx +0 -68
  73. package/src/composites/chart-composition.tsx +0 -1039
  74. package/src/composites/chart-interaction.ts +0 -136
  75. package/src/composites/chart-ui.tsx +0 -336
  76. package/src/composites/charts-3d.test.tsx +0 -25
  77. package/src/composites/charts-3d.tsx +0 -318
  78. package/src/composites/charts.test.tsx +0 -254
  79. package/src/composites/flow-charts.test.tsx +0 -98
  80. package/src/composites/flow-charts.tsx +0 -641
  81. package/src/composites/radial-charts.test.tsx +0 -89
  82. package/src/composites/radial-charts.tsx +0 -643
  83. package/src/lib/chart-export.test.ts +0 -20
  84. package/src/lib/chart-export.ts +0 -99
  85. package/src/lib/charts-platform.test.ts +0 -93
  86. package/src/lib/charts.test.ts +0 -132
  87. package/src/lib/charts.ts +0 -1021
  88. package/src/lib/geo.test.ts +0 -42
  89. package/src/lib/geo.ts +0 -138
  90. package/src/lib/iso.test.ts +0 -22
  91. package/src/lib/iso.ts +0 -102
  92. package/src/lib/webgl-scatter.test.ts +0 -36
  93. package/src/lib/webgl-scatter.ts +0 -230
@@ -1,267 +1,9 @@
1
1
  import { render, screen } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
  import { describe, expect, it, vi } from "vitest";
4
- import { EventCalendar } from "./EventCalendar";
5
4
  import { EventTimeline } from "./EventTimeline";
6
5
  import type { SchedulerEvent, SchedulerResource } from "../lib/scheduler";
7
6
 
8
- const resources: SchedulerResource[] = [
9
- { id: "work", title: "Work", color: "violet" },
10
- { id: "health", title: "Health", color: "teal" },
11
- ];
12
-
13
- const events: SchedulerEvent[] = [
14
- {
15
- id: "run",
16
- title: "Morning Run",
17
- start: "2026-08-03T07:00:00",
18
- end: "2026-08-03T07:45:00",
19
- resourceId: "health",
20
- },
21
- {
22
- id: "bday",
23
- title: "Alice's Birthday",
24
- start: "2026-08-06",
25
- end: "2026-08-08",
26
- allDay: true,
27
- resourceId: "work",
28
- color: "rose",
29
- },
30
- {
31
- id: "review",
32
- title: "Design Review",
33
- start: "2026-08-09T14:00:00",
34
- end: "2026-08-09T16:00:00",
35
- resourceId: "work",
36
- },
37
- ];
38
-
39
- describe("EventCalendar", () => {
40
- it("renders the month heading, weekday labels, and events", () => {
41
- render(
42
- <EventCalendar
43
- defaultDate={new Date(2026, 7, 15)}
44
- events={events}
45
- resources={resources}
46
- locale="en-US"
47
- />,
48
- );
49
- expect(screen.getByRole("heading", { name: /August 2026/i })).toBeInTheDocument();
50
- expect(screen.getByText(/Sun/)).toBeInTheDocument();
51
- expect(screen.getByText("Morning Run")).toBeInTheDocument();
52
- expect(screen.getByText("Alice's Birthday")).toBeInTheDocument();
53
- expect(screen.getByText("Work")).toBeInTheDocument();
54
- });
55
-
56
- it("jumps to today from the toolbar", async () => {
57
- const user = userEvent.setup();
58
- const onDateChange = vi.fn();
59
- render(
60
- <EventCalendar
61
- defaultDate={new Date(2025, 0, 1)}
62
- onDateChange={onDateChange}
63
- events={events}
64
- />,
65
- );
66
- await user.click(screen.getByRole("button", { name: "Today" }));
67
- expect(onDateChange).toHaveBeenCalled();
68
- const next = onDateChange.mock.calls[0][0] as Date;
69
- const now = new Date();
70
- expect(next.getFullYear()).toBe(now.getFullYear());
71
- expect(next.getMonth()).toBe(now.getMonth());
72
- });
73
-
74
- it("fires onEventClick for a timed event", async () => {
75
- const user = userEvent.setup();
76
- const onEventClick = vi.fn();
77
- render(
78
- <EventCalendar
79
- defaultDate={new Date(2026, 7, 15)}
80
- events={events}
81
- resources={resources}
82
- locale="en-US"
83
- onEventClick={onEventClick}
84
- />,
85
- );
86
- await user.click(screen.getByText("Morning Run"));
87
- expect(onEventClick).toHaveBeenCalledWith(expect.objectContaining({ id: "run" }));
88
- });
89
-
90
- it("switches to week view from the menu", async () => {
91
- const user = userEvent.setup();
92
- render(
93
- <EventCalendar defaultDate={new Date(2026, 7, 15)} events={events} locale="en-US" />,
94
- );
95
- await user.click(screen.getByRole("button", { name: /month/i }));
96
- await user.click(screen.getByRole("menuitem", { name: "Week" }));
97
- expect(screen.getByRole("heading", { name: /2026/ })).toBeInTheDocument();
98
- });
99
-
100
- it("lists events in agenda view", () => {
101
- render(
102
- <EventCalendar
103
- defaultView="agenda"
104
- defaultDate={new Date(2026, 7, 3)}
105
- events={events}
106
- locale="en-US"
107
- />,
108
- );
109
- expect(screen.getByRole("heading", { name: /Aug 3/ })).toBeInTheDocument();
110
- expect(screen.getAllByText("No events").length).toBeGreaterThan(0);
111
- expect(screen.getByText("Morning Run")).toBeInTheDocument();
112
- expect(screen.getAllByText("Alice's Birthday").length).toBeGreaterThan(0);
113
- });
114
-
115
- it("opens the editor and saves title changes", async () => {
116
- const user = userEvent.setup();
117
- const onEventChange = vi.fn();
118
- render(
119
- <EventCalendar
120
- defaultDate={new Date(2026, 7, 15)}
121
- events={events}
122
- resources={resources}
123
- locale="en-US"
124
- onEventChange={onEventChange}
125
- />,
126
- );
127
- await user.click(screen.getByText("Morning Run"));
128
- const title = screen.getByRole("textbox", { name: /title/i });
129
- await user.clear(title);
130
- await user.type(title, "Jog");
131
- await user.click(screen.getByRole("button", { name: "Save" }));
132
- expect(onEventChange).toHaveBeenCalledWith(
133
- expect.objectContaining({ id: "run" }),
134
- expect.objectContaining({ title: "Jog" }),
135
- );
136
- });
137
-
138
- it("creates an event from a day slot", async () => {
139
- const user = userEvent.setup();
140
- const onEventCreate = vi.fn();
141
- render(
142
- <EventCalendar
143
- defaultDate={new Date(2026, 7, 15)}
144
- events={events}
145
- locale="en-US"
146
- onEventCreate={onEventCreate}
147
- />,
148
- );
149
- await user.click(screen.getByRole("button", { name: "15" }));
150
- expect(screen.getByRole("heading", { name: "New event" })).toBeInTheDocument();
151
- await user.type(screen.getByRole("textbox", { name: /title/i }), "Focus time");
152
- await user.click(screen.getByRole("button", { name: "Save" }));
153
- expect(onEventCreate).toHaveBeenCalledWith(expect.objectContaining({ title: "Focus time" }));
154
- });
155
-
156
- it("skips the built-in editor when showEventEditor is false", async () => {
157
- const user = userEvent.setup();
158
- const onEventClick = vi.fn();
159
- const onSlotClick = vi.fn();
160
- render(
161
- <EventCalendar
162
- defaultDate={new Date(2026, 7, 15)}
163
- events={events}
164
- resources={resources}
165
- locale="en-US"
166
- showEventEditor={false}
167
- onEventClick={onEventClick}
168
- onSlotClick={onSlotClick}
169
- />,
170
- );
171
- await user.click(screen.getByText("Morning Run"));
172
- expect(onEventClick).toHaveBeenCalledWith(expect.objectContaining({ id: "run" }));
173
- expect(screen.queryByRole("heading", { name: /event/i })).not.toBeInTheDocument();
174
-
175
- await user.click(screen.getByRole("button", { name: "15" }));
176
- expect(onSlotClick).toHaveBeenCalled();
177
- expect(screen.queryByRole("heading", { name: "New event" })).not.toBeInTheDocument();
178
- });
179
-
180
- it("renders custom event chips and toolbar", async () => {
181
- const user = userEvent.setup();
182
- const onDateChange = vi.fn();
183
- render(
184
- <EventCalendar
185
- defaultDate={new Date(2026, 7, 15)}
186
- events={events}
187
- resources={resources}
188
- locale="en-US"
189
- showEventEditor={false}
190
- showPreferences={false}
191
- toolbarTrailing={<button type="button">Jump year</button>}
192
- renderEvent={(event) => <span>★ {event.title}</span>}
193
- renderToolbar={({ title, onToday }) => (
194
- <div>
195
- <p>{title}</p>
196
- <button type="button" onClick={onToday}>
197
- Jump today
198
- </button>
199
- </div>
200
- )}
201
- onDateChange={onDateChange}
202
- />,
203
- );
204
- expect(screen.getByText("★ Morning Run")).toBeInTheDocument();
205
- expect(screen.queryByRole("button", { name: "Preferences" })).not.toBeInTheDocument();
206
- expect(screen.queryByRole("button", { name: "Jump year" })).not.toBeInTheDocument();
207
- await user.click(screen.getByRole("button", { name: "Jump today" }));
208
- expect(onDateChange).toHaveBeenCalled();
209
- });
210
-
211
- it("keeps toolbarTrailing on the default toolbar", () => {
212
- render(
213
- <EventCalendar
214
- defaultDate={new Date(2026, 7, 15)}
215
- events={events}
216
- locale="en-US"
217
- showPreferences={false}
218
- toolbarTrailing={<button type="button">Jump year</button>}
219
- />,
220
- );
221
- expect(screen.getByRole("button", { name: "Jump year" })).toBeInTheDocument();
222
- expect(screen.getByRole("heading", { name: /August 2026/i })).toBeInTheDocument();
223
- });
224
-
225
- it("renders a compact toolbar with equal month and year jumps", () => {
226
- render(
227
- <EventCalendar
228
- defaultDate={new Date(2026, 7, 15)}
229
- events={events}
230
- locale="en-US"
231
- showPreferences={false}
232
- toolbarDensity="compact"
233
- showDateJump
234
- />,
235
- );
236
- expect(document.querySelector('[data-density="compact"]')).toBeTruthy();
237
- expect(screen.getByLabelText("Month")).toHaveAttribute("data-size", "sm");
238
- expect(screen.getByLabelText("Year")).toHaveAttribute("data-size", "sm");
239
- expect(screen.getByLabelText("Month")).toHaveValue("7");
240
- expect(screen.getByLabelText("Year")).toHaveValue("2026");
241
- });
242
-
243
- it("reports the visible range for week view", () => {
244
- const onVisibleRangeChange = vi.fn();
245
- render(
246
- <EventCalendar
247
- defaultDate={new Date(2026, 7, 15)}
248
- defaultView="week"
249
- locale="en-US"
250
- onVisibleRangeChange={onVisibleRangeChange}
251
- />,
252
- );
253
- expect(onVisibleRangeChange).toHaveBeenCalled();
254
- const range = onVisibleRangeChange.mock.calls[0][0] as {
255
- view: string;
256
- start: Date;
257
- end: Date;
258
- };
259
- expect(range.view).toBe("week");
260
- expect(range.start.getDay()).toBe(0);
261
- expect(range.end.getTime() - range.start.getTime()).toBe(7 * 86_400_000);
262
- });
263
- });
264
-
265
7
  describe("EventTimeline", () => {
266
8
  const timelineEvents: SchedulerEvent[] = [
267
9
  {
package/src/index.test.ts CHANGED
@@ -9,7 +9,9 @@ import {
9
9
  MapChart,
10
10
  SchedulerDateJump,
11
11
  SchedulerToolbar,
12
+ BREAKPOINTS,
12
13
  calendarVisibleRange,
14
+ createTheme,
13
15
  mercator,
14
16
  } from "./index";
15
17
 
@@ -26,5 +28,9 @@ describe("@spatika/react public exports", () => {
26
28
  expect(SchedulerDateJump).toBeTypeOf("function");
27
29
  expect(calendarVisibleRange).toBeTypeOf("function");
28
30
  expect(mercator([0, 0])[0]).toBeCloseTo(0.5);
31
+ expect(BREAKPOINTS.md).toBe(768);
32
+ expect(createTheme({ id: "brand", palette: { primary: "#7c3aed" } }).vars["--primary"]).toBe(
33
+ "#7c3aed",
34
+ );
29
35
  });
30
36
  });
package/src/index.ts CHANGED
@@ -57,8 +57,33 @@ export {
57
57
  applyTheme,
58
58
  isDarkTheme,
59
59
  isThemeId,
60
+ type ApplyThemeCustom,
61
+ type BuiltinThemeId,
60
62
  type ThemeId,
61
63
  } from "./lib/themes";
64
+ export {
65
+ createTheme,
66
+ themeToCss,
67
+ injectCustomThemeStyles,
68
+ findCustomTheme,
69
+ PALETTE_CSS_VARS,
70
+ GLASS_CSS_VARS,
71
+ SHAPE_CSS_VARS,
72
+ type CreateThemeOptions,
73
+ type SpatikaTheme,
74
+ type ThemePalette,
75
+ type ThemeGlass,
76
+ type ThemeShape,
77
+ type ThemeTypography,
78
+ } from "./lib/create-theme";
79
+ export {
80
+ BREAKPOINTS,
81
+ BREAKPOINT_KEYS,
82
+ breakpointUp,
83
+ breakpointDown,
84
+ breakpointBetween,
85
+ type Breakpoint,
86
+ } from "./lib/breakpoints";
62
87
  export { getAvatarColor, getAvatarFallbackHue, getInitials } from "./lib/avatar-utils";
63
88
  export {
64
89
  COVER_PATTERN_KINDS,
@@ -79,7 +104,13 @@ export {
79
104
  coverTitleClass,
80
105
  } from "./lib/cover-chrome";
81
106
  export { useCoverChromeBleed, type CoverChromeInk } from "./lib/use-cover-chrome-bleed";
82
- export { useIsMobile, useMediaQuery } from "./lib/use-media-query";
107
+ export {
108
+ useIsMobile,
109
+ useMediaQuery,
110
+ useBreakpoint,
111
+ useBreakpointUp,
112
+ useBreakpointDown,
113
+ } from "./lib/use-media-query";
83
114
  export {
84
115
  SpatikaThemeProvider,
85
116
  useSpatikaTheme,
@@ -404,6 +435,36 @@ export {
404
435
  type PhotoViewerItem,
405
436
  } from "./composites/PhotoViewer";
406
437
  export { RichTextEditor, type RichTextEditorProps } from "./composites/RichTextEditor";
438
+ export {
439
+ SpatikaEditor,
440
+ useSpatikaEditor,
441
+ buildSpatikaExtensions,
442
+ EditorToolbar,
443
+ EditorBubbleMenu,
444
+ ImageBubbleMenu,
445
+ SlashCommandMenu,
446
+ MentionMenu,
447
+ AiOverlay,
448
+ ToolbarButton,
449
+ createSlashCommandExtension,
450
+ defaultSlashCommands,
451
+ createMentionExtension,
452
+ ResizableImage,
453
+ createAiActionExtension,
454
+ } from "@spatika/editor";
455
+ export type {
456
+ SpatikaEditorProps,
457
+ SpatikaEditorHandle,
458
+ SpatikaEditorToolbarConfig,
459
+ SlashCommandItem,
460
+ MentionContact,
461
+ AiActionDefinition,
462
+ PlaceholderConfig,
463
+ UseSpatikaEditorOptions,
464
+ SlashCommandStorage,
465
+ MentionStorage,
466
+ ImageAlign,
467
+ } from "@spatika/editor";
407
468
  export { HeaderIconButton, type HeaderIconButtonProps } from "./composites/HeaderIconButton";
408
469
  export {
409
470
  QuickFilterRail,
@@ -529,26 +590,25 @@ export {
529
590
  CHART_COLOR_VARS,
530
591
  chartColor,
531
592
  formatChartNumber,
593
+ formatAxisNumber,
594
+ resolveChartRadius,
595
+ categoryLabelGutter,
596
+ fitCategoryLabel,
532
597
  seriesDataFromDataset,
533
598
  axisDataFromDataset,
534
599
  clampZoom,
535
600
  DEFAULT_ZOOM,
536
- } from "./lib/charts";
537
- export type {
538
- ChartAxisConfig,
539
- ChartCurve,
540
- ChartDataset,
541
- ChartInteraction,
542
- ChartMargin,
543
- ChartZoom,
544
- HighlightScope,
545
- SankeyLink,
546
- SankeyNode,
547
- TreeNode,
548
- } from "./lib/charts";
549
- export { serializeChartSvg, exportChart, downloadChartSvg, downloadChartPng } from "./lib/chart-export";
550
- export {
601
+ stackSeries,
602
+ errorBarRange,
603
+ pieLabelLine,
604
+ seriesHasPlottableData,
605
+ serializeChartSvg,
606
+ exportChart,
607
+ downloadChartSvg,
608
+ downloadChartPng,
551
609
  ChartContainer,
610
+ ComposedChart,
611
+ ResponsiveContainer,
552
612
  ChartSurface,
553
613
  ChartsGrid,
554
614
  ChartsXAxis,
@@ -556,28 +616,16 @@ export {
556
616
  ChartsLegend,
557
617
  ChartsToolbar,
558
618
  ChartsBrush,
619
+ ChartsCursor,
559
620
  ChartsReferenceLine,
621
+ ChartsReferenceArea,
622
+ ChartsReferenceDot,
623
+ ChartsErrorBar,
560
624
  BarPlot,
561
625
  LinePlot,
562
626
  AreaPlot,
563
627
  ScatterPlot,
564
628
  useChartContext,
565
- } from "./composites/chart-composition";
566
- export type {
567
- ChartContainerProps,
568
- ComposedSeries,
569
- ComposedSeriesType,
570
- HighlightedItem,
571
- } from "./composites/chart-composition";
572
- export type {
573
- ChartHover,
574
- ChartItemEvent,
575
- ChartMarkRenderContext,
576
- ChartReferenceLine,
577
- ChartTooltipItem,
578
- ChartTooltipRenderer,
579
- } from "./composites/chart-interaction";
580
- export {
581
629
  AreaChart,
582
630
  BarChart,
583
631
  BoxPlotChart,
@@ -590,8 +638,60 @@ export {
590
638
  ScatterChart,
591
639
  SparkLineChart,
592
640
  WaterfallChart,
593
- } from "./composites/cartesian-charts";
641
+ Gauge,
642
+ LinearGauge,
643
+ PieChart,
644
+ PolarLineChart,
645
+ RadarChart,
646
+ RadialBarChart,
647
+ RadialLineChart,
648
+ ChordChart,
649
+ FunnelChart,
650
+ Heatmap,
651
+ PyramidChart,
652
+ SankeyChart,
653
+ SunburstChart,
654
+ Treemap,
655
+ MapChart,
656
+ BarChart3D,
657
+ PieChart3D,
658
+ ChartDataGrid,
659
+ renderChartCell,
660
+ mercator,
661
+ equirectangular,
662
+ geoPath,
663
+ fitFeatures,
664
+ featureId,
665
+ } from "@spatika/charts";
594
666
  export type {
667
+ ChartAxisConfig,
668
+ ChartCurve,
669
+ ChartDataset,
670
+ ChartErrorValue,
671
+ ChartInteraction,
672
+ ChartLegendPosition,
673
+ ChartMargin,
674
+ ChartRadius,
675
+ ChartZoom,
676
+ HighlightScope,
677
+ SankeyLink,
678
+ SankeyNode,
679
+ StackOffset,
680
+ TreeNode,
681
+ ChartContainerProps,
682
+ ComposedSeries,
683
+ ComposedSeriesType,
684
+ HighlightedItem,
685
+ ResponsiveContainerProps,
686
+ ChartHover,
687
+ ChartItemEvent,
688
+ ChartMarkRenderContext,
689
+ ChartReferenceArea,
690
+ ChartReferenceDot,
691
+ ChartReferenceLine,
692
+ ChartTooltipItem,
693
+ ChartTooltipRenderer,
694
+ ChartTooltipTrigger,
595
695
  BarChartProps,
596
696
  BoxPlotChartProps,
597
697
  CandleDatum,
@@ -608,17 +708,6 @@ export type {
608
708
  ScatterSeries,
609
709
  SparkLineChartProps,
610
710
  WaterfallChartProps,
611
- } from "./composites/cartesian-charts";
612
- export {
613
- Gauge,
614
- LinearGauge,
615
- PieChart,
616
- PolarLineChart,
617
- RadarChart,
618
- RadialBarChart,
619
- RadialLineChart,
620
- } from "./composites/radial-charts";
621
- export type {
622
711
  GaugeProps,
623
712
  GaugeSection,
624
713
  LinearGaugeProps,
@@ -629,17 +718,6 @@ export type {
629
718
  RadarSeries,
630
719
  RadialBarChartProps,
631
720
  RadialLineChartProps,
632
- } from "./composites/radial-charts";
633
- export {
634
- ChordChart,
635
- FunnelChart,
636
- Heatmap,
637
- PyramidChart,
638
- SankeyChart,
639
- SunburstChart,
640
- Treemap,
641
- } from "./composites/flow-charts";
642
- export type {
643
721
  ChordChartProps,
644
722
  FunnelChartProps,
645
723
  FunnelDatum,
@@ -650,28 +728,16 @@ export type {
650
728
  SankeyChartProps,
651
729
  SunburstChartProps,
652
730
  TreemapProps,
653
- } from "./composites/flow-charts";
654
- export { MapChart } from "./composites/MapChart";
655
- export type { MapChartProps, MapShapeDatum } from "./composites/MapChart";
656
- export { BarChart3D, PieChart3D } from "./composites/charts-3d";
657
- export type { BarChart3DProps, PieChart3DProps } from "./composites/charts-3d";
658
- export { ChartDataGrid, renderChartCell } from "./composites/ChartDataGrid";
659
- export type {
731
+ MapChartProps,
732
+ MapShapeDatum,
733
+ BarChart3DProps,
734
+ PieChart3DProps,
660
735
  ChartDataGridProps,
661
736
  ChartDataGridRow,
662
737
  ChartGridColumn,
663
738
  ChartGridColumnType,
664
- } from "./composites/ChartDataGrid";
665
- export {
666
- mercator,
667
- equirectangular,
668
- geoPath,
669
- fitFeatures,
670
- featureId,
671
- } from "./lib/geo";
672
- export type {
673
739
  GeoJsonFeature,
674
740
  GeoJsonFeatureCollection,
675
741
  GeoJsonGeometry,
676
742
  GeoProjectionKind,
677
- } from "./lib/geo";
743
+ } from "@spatika/charts";
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ BREAKPOINTS,
4
+ BREAKPOINT_KEYS,
5
+ breakpointBetween,
6
+ breakpointDown,
7
+ breakpointUp,
8
+ } from "./breakpoints";
9
+
10
+ describe("breakpoints", () => {
11
+ it("matches Tailwind viewport widths", () => {
12
+ expect(BREAKPOINTS).toEqual({ xs: 0, sm: 640, md: 768, lg: 1024, xl: 1280 });
13
+ expect(BREAKPOINT_KEYS).toEqual(["xs", "sm", "md", "lg", "xl"]);
14
+ });
15
+
16
+ it("builds min-width and max-width queries", () => {
17
+ expect(breakpointUp("xs")).toBe("(min-width: 0px)");
18
+ expect(breakpointUp("md")).toBe("(min-width: 768px)");
19
+ expect(breakpointDown("md")).toBe("(max-width: 767px)");
20
+ expect(breakpointDown("xl")).toBe("(max-width: 1279px)");
21
+ });
22
+
23
+ it("builds an inclusive range", () => {
24
+ expect(breakpointBetween("sm", "lg")).toBe(
25
+ "(min-width: 640px) and (max-width: 1023px)",
26
+ );
27
+ expect(breakpointBetween("md")).toBe("(min-width: 768px)");
28
+ });
29
+ });
@@ -0,0 +1,28 @@
1
+ /** Viewport widths (px) that match Tailwind and Spatika `Grid` / `Container`. */
2
+ export const BREAKPOINTS = {
3
+ xs: 0,
4
+ sm: 640,
5
+ md: 768,
6
+ lg: 1024,
7
+ xl: 1280,
8
+ } as const;
9
+
10
+ export type Breakpoint = keyof typeof BREAKPOINTS;
11
+
12
+ export const BREAKPOINT_KEYS = ["xs", "sm", "md", "lg", "xl"] as const satisfies readonly Breakpoint[];
13
+
14
+ /** `(min-width: Npx)` — inclusive start of a breakpoint. `xs` is always true. */
15
+ export function breakpointUp(key: Breakpoint): string {
16
+ return `(min-width: ${BREAKPOINTS[key]}px)`;
17
+ }
18
+
19
+ /** `(max-width: N-1px)` — viewports strictly below a breakpoint. */
20
+ export function breakpointDown(key: Exclude<Breakpoint, "xs">): string {
21
+ return `(max-width: ${BREAKPOINTS[key] - 1}px)`;
22
+ }
23
+
24
+ /** Inclusive range between two breakpoints. `end` omitted means no upper bound. */
25
+ export function breakpointBetween(start: Breakpoint, end?: Exclude<Breakpoint, "xs">): string {
26
+ if (!end) return breakpointUp(start);
27
+ return `${breakpointUp(start)} and ${breakpointDown(end)}`;
28
+ }