@vllnt/ui 0.2.0 → 0.2.1-canary.06f0e84

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 (202) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/README.md +27 -12
  3. package/dist/components/activity-log/activity-log.js +1 -0
  4. package/dist/components/agent-activity/agent-activity.js +311 -0
  5. package/dist/components/agent-activity/index.js +18 -0
  6. package/dist/components/ai-artifact/ai-artifact.js +422 -0
  7. package/dist/components/ai-artifact/index.js +24 -0
  8. package/dist/components/ai-sidebar/ai-sidebar.js +254 -0
  9. package/dist/components/ai-sidebar/index.js +22 -0
  10. package/dist/components/alert-pulse/alert-pulse.js +93 -0
  11. package/dist/components/alert-pulse/index.js +6 -0
  12. package/dist/components/anchor-port/anchor-port.js +51 -0
  13. package/dist/components/anchor-port/index.js +4 -0
  14. package/dist/components/animated-text/animated-text.js +1 -0
  15. package/dist/components/auto-reload/auto-reload.js +367 -0
  16. package/dist/components/auto-reload/index.js +6 -0
  17. package/dist/components/banner/banner.js +155 -0
  18. package/dist/components/banner/index.js +10 -0
  19. package/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
  20. package/dist/components/bottom-activity-strip/index.js +6 -0
  21. package/dist/components/bottom-bar/bottom-bar.js +25 -0
  22. package/dist/components/bottom-bar/index.js +4 -0
  23. package/dist/components/canvas-shell/canvas-foundation-demo.js +183 -0
  24. package/dist/components/canvas-shell/canvas-shell-route-config.js +0 -0
  25. package/dist/components/canvas-shell/canvas-shell.js +261 -0
  26. package/dist/components/canvas-shell/index.js +4 -0
  27. package/dist/components/canvas-view/canvas-view.js +461 -0
  28. package/dist/components/canvas-view/index.js +6 -0
  29. package/dist/components/chart/area-chart.js +1 -0
  30. package/dist/components/chart/line-chart.js +1 -0
  31. package/dist/components/chat-dock-section/chat-dock-section.js +56 -0
  32. package/dist/components/chat-dock-section/index.js +6 -0
  33. package/dist/components/checklist/checklist.js +7 -0
  34. package/dist/components/checklist/index.js +3 -1
  35. package/dist/components/choropleth-map/choropleth-map.js +373 -0
  36. package/dist/components/choropleth-map/index.js +10 -0
  37. package/dist/components/chronological-timeline/chronological-timeline.js +337 -0
  38. package/dist/components/chronological-timeline/index.js +8 -0
  39. package/dist/components/civilization-card/civilization-card.js +258 -0
  40. package/dist/components/civilization-card/index.js +8 -0
  41. package/dist/components/combobox/combobox.js +44 -20
  42. package/dist/components/comment-pin/comment-pin.js +104 -0
  43. package/dist/components/comment-pin/index.js +6 -0
  44. package/dist/components/connector-edge/connector-edge.js +66 -0
  45. package/dist/components/connector-edge/index.js +6 -0
  46. package/dist/components/context-lens/context-lens.js +98 -0
  47. package/dist/components/context-lens/index.js +6 -0
  48. package/dist/components/conversation-thread/conversation-thread.js +348 -0
  49. package/dist/components/conversation-thread/index.js +20 -0
  50. package/dist/components/copy-button/copy-button.js +189 -0
  51. package/dist/components/copy-button/index.js +8 -0
  52. package/dist/components/curriculum/curriculum.js +349 -0
  53. package/dist/components/curriculum/index.js +10 -0
  54. package/dist/components/data-list/data-list.js +1 -0
  55. package/dist/components/document-sibling-nav/document-sibling-nav.js +111 -0
  56. package/dist/components/document-sibling-nav/index.js +8 -0
  57. package/dist/components/edge-label/edge-label.js +26 -0
  58. package/dist/components/edge-label/index.js +4 -0
  59. package/dist/components/empty-state/empty-state.js +93 -0
  60. package/dist/components/empty-state/index.js +8 -0
  61. package/dist/components/era-comparison/era-comparison.js +198 -0
  62. package/dist/components/era-comparison/index.js +16 -0
  63. package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
  64. package/dist/components/floating-toolbar/index.js +6 -0
  65. package/dist/components/follow-mode/follow-mode.js +89 -0
  66. package/dist/components/follow-mode/index.js +6 -0
  67. package/dist/components/form/form.js +432 -0
  68. package/dist/components/form/index.js +20 -0
  69. package/dist/components/gantt-chart/gantt-chart.js +331 -0
  70. package/dist/components/gantt-chart/index.js +6 -0
  71. package/dist/components/geography-quiz-map/geography-quiz-map.js +343 -0
  72. package/dist/components/geography-quiz-map/index.js +12 -0
  73. package/dist/components/glass-panel/glass-panel.js +21 -0
  74. package/dist/components/glass-panel/index.js +4 -0
  75. package/dist/components/globe-3d/globe-3d.js +417 -0
  76. package/dist/components/globe-3d/index.js +10 -0
  77. package/dist/components/group-hull/group-hull.js +29 -0
  78. package/dist/components/group-hull/index.js +4 -0
  79. package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
  80. package/dist/components/handoff-beacon/index.js +6 -0
  81. package/dist/components/heat-map-overlay/heat-map-overlay.js +215 -0
  82. package/dist/components/heat-map-overlay/index.js +6 -0
  83. package/dist/components/heat-overlay/heat-overlay.js +92 -0
  84. package/dist/components/heat-overlay/index.js +6 -0
  85. package/dist/components/historic-timeline/historic-timeline.js +342 -0
  86. package/dist/components/historic-timeline/index.js +6 -0
  87. package/dist/components/historical-figure-card/historical-figure-card.js +273 -0
  88. package/dist/components/historical-figure-card/index.js +6 -0
  89. package/dist/components/index.js +568 -1
  90. package/dist/components/infinite-plane/index.js +6 -0
  91. package/dist/components/infinite-plane/infinite-plane.js +75 -0
  92. package/dist/components/interactive-timeline/index.js +16 -0
  93. package/dist/components/interactive-timeline/interactive-timeline.js +708 -0
  94. package/dist/components/jarvis-dock/index.js +6 -0
  95. package/dist/components/jarvis-dock/jarvis-dock.js +98 -0
  96. package/dist/components/kbd/index.js +5 -0
  97. package/dist/components/kbd/kbd.js +117 -0
  98. package/dist/components/knowledge-check/index.js +6 -0
  99. package/dist/components/knowledge-check/knowledge-check.js +448 -0
  100. package/dist/components/left-rail/index.js +4 -0
  101. package/dist/components/left-rail/left-rail.js +25 -0
  102. package/dist/components/live-cursor/index.js +6 -0
  103. package/dist/components/live-cursor/live-cursor.js +62 -0
  104. package/dist/components/map-2d/index.js +20 -0
  105. package/dist/components/map-2d/map-2d.js +455 -0
  106. package/dist/components/map-timeline/index.js +16 -0
  107. package/dist/components/map-timeline/map-timeline.js +506 -0
  108. package/dist/components/metric-cluster/index.js +6 -0
  109. package/dist/components/metric-cluster/metric-cluster.js +96 -0
  110. package/dist/components/mini-map-panel/index.js +6 -0
  111. package/dist/components/mini-map-panel/mini-map-panel.js +74 -0
  112. package/dist/components/model-comparison/index.js +12 -0
  113. package/dist/components/model-comparison/model-comparison.js +211 -0
  114. package/dist/components/multi-select/index.js +6 -0
  115. package/dist/components/multi-select/multi-select.js +258 -0
  116. package/dist/components/multi-select-lasso/index.js +6 -0
  117. package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
  118. package/dist/components/newsletter-signup/index.js +8 -0
  119. package/dist/components/newsletter-signup/newsletter-signup.js +269 -0
  120. package/dist/components/object-card/index.js +6 -0
  121. package/dist/components/object-card/object-card.js +126 -0
  122. package/dist/components/object-handle/index.js +4 -0
  123. package/dist/components/object-handle/object-handle.js +38 -0
  124. package/dist/components/object-inspector/index.js +6 -0
  125. package/dist/components/object-inspector/object-inspector.js +136 -0
  126. package/dist/components/overview-board/index.js +8 -0
  127. package/dist/components/overview-board/overview-board.js +127 -0
  128. package/dist/components/parallel-timeline/index.js +6 -0
  129. package/dist/components/parallel-timeline/parallel-timeline.js +251 -0
  130. package/dist/components/playback-ghost/index.js +6 -0
  131. package/dist/components/playback-ghost/playback-ghost.js +83 -0
  132. package/dist/components/policy-delivery-panel/index.js +6 -0
  133. package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -0
  134. package/dist/components/presence-stack/index.js +6 -0
  135. package/dist/components/presence-stack/presence-stack.js +108 -0
  136. package/dist/components/presence-sync-indicator/index.js +6 -0
  137. package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
  138. package/dist/components/pricing-table/index.js +8 -0
  139. package/dist/components/pricing-table/pricing-table.js +247 -0
  140. package/dist/components/primary-source-viewer/index.js +26 -0
  141. package/dist/components/primary-source-viewer/primary-source-viewer.js +439 -0
  142. package/dist/components/progress-tracker/index.js +20 -0
  143. package/dist/components/progress-tracker/progress-tracker.js +527 -0
  144. package/dist/components/prompt-templates/index.js +6 -0
  145. package/dist/components/prompt-templates/prompt-templates.js +403 -0
  146. package/dist/components/property-section/index.js +6 -0
  147. package/dist/components/property-section/property-section.js +101 -0
  148. package/dist/components/relationship-inspector/index.js +6 -0
  149. package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
  150. package/dist/components/right-dock/index.js +4 -0
  151. package/dist/components/right-dock/right-dock.js +28 -0
  152. package/dist/components/route-map/index.js +6 -0
  153. package/dist/components/route-map/route-map.js +339 -0
  154. package/dist/components/routing-assignment-panel/index.js +6 -0
  155. package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
  156. package/dist/components/run-timeline/index.js +6 -0
  157. package/dist/components/run-timeline/run-timeline.js +221 -0
  158. package/dist/components/runtime-overview-panel/index.js +6 -0
  159. package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
  160. package/dist/components/segmented-control/index.js +12 -0
  161. package/dist/components/segmented-control/segmented-control.js +61 -0
  162. package/dist/components/selection-halo/index.js +6 -0
  163. package/dist/components/selection-halo/selection-halo.js +72 -0
  164. package/dist/components/selection-presence/index.js +6 -0
  165. package/dist/components/selection-presence/selection-presence.js +50 -0
  166. package/dist/components/snap-guides/index.js +6 -0
  167. package/dist/components/snap-guides/snap-guides.js +45 -0
  168. package/dist/components/spinner/unicode-spinner.js +1 -0
  169. package/dist/components/state-badge-overlay/index.js +6 -0
  170. package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
  171. package/dist/components/sticky-metric/index.js +6 -0
  172. package/dist/components/sticky-metric/sticky-metric.js +83 -0
  173. package/dist/components/story-map/index.js +8 -0
  174. package/dist/components/story-map/story-map.js +414 -0
  175. package/dist/components/tags-input/index.js +4 -0
  176. package/dist/components/tags-input/tags-input.js +178 -0
  177. package/dist/components/thread-bubble/index.js +6 -0
  178. package/dist/components/thread-bubble/thread-bubble.js +85 -0
  179. package/dist/components/threshold-ring/index.js +6 -0
  180. package/dist/components/threshold-ring/threshold-ring.js +160 -0
  181. package/dist/components/timeline/index.js +12 -0
  182. package/dist/components/timeline/timeline.js +239 -0
  183. package/dist/components/timeline-scrubber/index.js +6 -0
  184. package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
  185. package/dist/components/top-bar/index.js +4 -0
  186. package/dist/components/top-bar/top-bar.js +31 -0
  187. package/dist/components/transaction-list/index.js +14 -0
  188. package/dist/components/transaction-list/transaction-list.js +226 -0
  189. package/dist/components/tree-view/index.js +6 -0
  190. package/dist/components/tree-view/tree-view.js +298 -0
  191. package/dist/components/usage-breakdown/usage-breakdown.js +1 -0
  192. package/dist/components/viewport-bookmarks/index.js +6 -0
  193. package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
  194. package/dist/components/workspace-switcher/index.js +6 -0
  195. package/dist/components/workspace-switcher/workspace-switcher.js +61 -0
  196. package/dist/components/world-breadcrumbs/index.js +6 -0
  197. package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
  198. package/dist/components/zoom-hud/index.js +4 -0
  199. package/dist/components/zoom-hud/zoom-hud.js +61 -0
  200. package/dist/index.d.ts +7906 -225
  201. package/dist/index.js +3 -1
  202. package/package.json +9 -5
@@ -0,0 +1,331 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ forwardRef,
5
+ useMemo
6
+ } from "react";
7
+ import { cn } from "../../lib/utils";
8
+ const MS_PER_DAY = 24 * 60 * 60 * 1e3;
9
+ const DEFAULT_LOCALE = "en-US";
10
+ const COLOR_CLASSES = {
11
+ amber: {
12
+ bar: "bg-amber-500/30",
13
+ progress: "bg-amber-600 dark:bg-amber-500"
14
+ },
15
+ blue: {
16
+ bar: "bg-blue-500/30",
17
+ progress: "bg-blue-600 dark:bg-blue-500"
18
+ },
19
+ emerald: {
20
+ bar: "bg-emerald-500/30",
21
+ progress: "bg-emerald-600 dark:bg-emerald-500"
22
+ },
23
+ neutral: {
24
+ bar: "bg-muted",
25
+ progress: "bg-muted-foreground"
26
+ },
27
+ purple: {
28
+ bar: "bg-purple-500/30",
29
+ progress: "bg-purple-600 dark:bg-purple-500"
30
+ },
31
+ red: {
32
+ bar: "bg-red-500/30",
33
+ progress: "bg-red-600 dark:bg-red-500"
34
+ },
35
+ rose: {
36
+ bar: "bg-rose-500/30",
37
+ progress: "bg-rose-600 dark:bg-rose-500"
38
+ }
39
+ };
40
+ const DEFAULT_LABELS = {
41
+ milestone: "Milestone",
42
+ today: "Today"
43
+ };
44
+ function toDate(value) {
45
+ return value instanceof Date ? new Date(value.getTime()) : new Date(value);
46
+ }
47
+ function clamp(value, min, max) {
48
+ return Math.min(Math.max(value, min), max);
49
+ }
50
+ function diffInDays(later, earlier) {
51
+ return (later.getTime() - earlier.getTime()) / MS_PER_DAY;
52
+ }
53
+ function buildTickFormatter(scale, locale) {
54
+ switch (scale) {
55
+ case "day":
56
+ return new Intl.DateTimeFormat(locale, {
57
+ day: "2-digit",
58
+ month: "short"
59
+ }).format;
60
+ case "week":
61
+ return new Intl.DateTimeFormat(locale, {
62
+ day: "2-digit",
63
+ month: "short"
64
+ }).format;
65
+ case "month":
66
+ return new Intl.DateTimeFormat(locale, {
67
+ month: "short",
68
+ year: "numeric"
69
+ }).format;
70
+ case "quarter":
71
+ return (date) => {
72
+ const quarter = Math.floor(date.getMonth() / 3) + 1;
73
+ return `Q${quarter.toString()} ${date.getFullYear().toString()}`;
74
+ };
75
+ }
76
+ }
77
+ function getTickStep(scale) {
78
+ switch (scale) {
79
+ case "day":
80
+ return 1;
81
+ case "week":
82
+ return 7;
83
+ case "month":
84
+ return 30;
85
+ case "quarter":
86
+ return 91;
87
+ }
88
+ }
89
+ function buildTicks(input) {
90
+ const { end, locale, scale, start, totalDays } = input;
91
+ const formatter = buildTickFormatter(scale, locale);
92
+ const stepDays = getTickStep(scale);
93
+ const tickCount = Math.floor(totalDays / stepDays);
94
+ return Array.from({ length: tickCount + 1 }).map((_, index) => {
95
+ const day = index * stepDays;
96
+ return {
97
+ date: new Date(start.getTime() + day * MS_PER_DAY),
98
+ offset: day
99
+ };
100
+ }).filter((tick) => tick.date.getTime() <= end.getTime()).map((tick) => ({ label: formatter(tick.date), offset: tick.offset }));
101
+ }
102
+ function useChartGeometry(options) {
103
+ const { endDate, locale, scale, startDate } = options;
104
+ return useMemo(() => {
105
+ const start = toDate(startDate);
106
+ const end = toDate(endDate);
107
+ const totalDays = Math.max(1, diffInDays(end, start));
108
+ const ticks = buildTicks({ end, locale, scale, start, totalDays });
109
+ return {
110
+ end,
111
+ pxPerDay: 1 / totalDays,
112
+ start,
113
+ ticks,
114
+ totalDays
115
+ };
116
+ }, [endDate, locale, scale, startDate]);
117
+ }
118
+ function TaskBar({ geometry, task }) {
119
+ const start = toDate(task.start);
120
+ const end = toDate(task.end);
121
+ const offsetDays = diffInDays(start, geometry.start);
122
+ const durationDays = Math.max(0.5, diffInDays(end, start));
123
+ const leftRatio = clamp(offsetDays / geometry.totalDays, 0, 1);
124
+ const widthRatio = clamp(durationDays / geometry.totalDays, 0, 1 - leftRatio);
125
+ const palette = COLOR_CLASSES[task.color ?? "blue"];
126
+ const progress = clamp(task.progress ?? 0, 0, 100);
127
+ const ariaLabel = typeof task.title === "string" ? `${task.title} from ${start.toISOString().slice(0, 10)} to ${end.toISOString().slice(0, 10)}, ${progress.toString()} percent complete` : void 0;
128
+ return /* @__PURE__ */ jsx(
129
+ "div",
130
+ {
131
+ "aria-label": ariaLabel,
132
+ "aria-valuemax": 100,
133
+ "aria-valuemin": 0,
134
+ "aria-valuenow": progress,
135
+ className: cn(
136
+ "absolute top-1.5 flex h-5 items-center overflow-hidden rounded-md ring-1 ring-border",
137
+ palette.bar
138
+ ),
139
+ "data-task-id": task.id,
140
+ role: "progressbar",
141
+ style: {
142
+ left: `${(leftRatio * 100).toString()}%`,
143
+ width: `${(widthRatio * 100).toString()}%`
144
+ },
145
+ children: /* @__PURE__ */ jsx(
146
+ "span",
147
+ {
148
+ "aria-hidden": "true",
149
+ className: cn("h-full rounded-md", palette.progress),
150
+ style: { width: `${progress.toString()}%` }
151
+ }
152
+ )
153
+ }
154
+ );
155
+ }
156
+ function MilestoneMarker({
157
+ geometry,
158
+ label,
159
+ milestone
160
+ }) {
161
+ const date = toDate(milestone.date);
162
+ const offsetDays = diffInDays(date, geometry.start);
163
+ if (offsetDays < 0 || offsetDays > geometry.totalDays) return null;
164
+ const leftRatio = offsetDays / geometry.totalDays;
165
+ const titleText = typeof milestone.title === "string" ? milestone.title : "";
166
+ return /* @__PURE__ */ jsxs(
167
+ "div",
168
+ {
169
+ "aria-label": `${label}: ${titleText}`,
170
+ className: "absolute top-0 z-10 -ml-1.5 flex flex-col items-center",
171
+ "data-milestone-id": milestone.id,
172
+ style: { left: `${(leftRatio * 100).toString()}%` },
173
+ children: [
174
+ /* @__PURE__ */ jsx(
175
+ "div",
176
+ {
177
+ "aria-hidden": "true",
178
+ className: "h-3 w-3 rotate-45 bg-amber-500 ring-2 ring-background"
179
+ }
180
+ ),
181
+ titleText ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 whitespace-nowrap rounded bg-amber-500/20 px-1 text-[10px] font-medium text-amber-900 dark:text-amber-200", children: titleText }) : null
182
+ ]
183
+ }
184
+ );
185
+ }
186
+ function TodayLine({ geometry, label, now }) {
187
+ const offsetDays = diffInDays(now, geometry.start);
188
+ if (offsetDays < 0 || offsetDays > geometry.totalDays) return null;
189
+ const leftRatio = offsetDays / geometry.totalDays;
190
+ return /* @__PURE__ */ jsxs(
191
+ "div",
192
+ {
193
+ "aria-label": label,
194
+ className: "pointer-events-none absolute inset-y-0 z-10",
195
+ style: { left: `${(leftRatio * 100).toString()}%` },
196
+ children: [
197
+ /* @__PURE__ */ jsx(
198
+ "div",
199
+ {
200
+ "aria-hidden": "true",
201
+ className: "absolute inset-y-0 w-0.5 -translate-x-1/2 bg-destructive"
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsx("span", { className: "absolute -top-5 -translate-x-1/2 whitespace-nowrap rounded bg-destructive/15 px-1 text-[10px] font-semibold uppercase tracking-wide text-destructive", children: label })
205
+ ]
206
+ }
207
+ );
208
+ }
209
+ function Axis({ geometry }) {
210
+ return /* @__PURE__ */ jsx("div", { className: "relative flex h-7 items-end border-b border-border text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: geometry.ticks.map((tick) => /* @__PURE__ */ jsx(
211
+ "span",
212
+ {
213
+ className: "absolute -translate-x-1/2 px-1",
214
+ style: {
215
+ left: `${(tick.offset / geometry.totalDays * 100).toString()}%`
216
+ },
217
+ children: tick.label
218
+ },
219
+ tick.offset
220
+ )) });
221
+ }
222
+ function TaskRow({ geometry, task }) {
223
+ return /* @__PURE__ */ jsx("div", { className: "relative flex h-8 items-center border-b border-border/40", children: /* @__PURE__ */ jsx(TaskBar, { geometry, task }) });
224
+ }
225
+ function LeftColumn({ groups, taskColumnWidth }) {
226
+ return /* @__PURE__ */ jsxs(
227
+ "div",
228
+ {
229
+ className: "flex flex-col border-r border-border bg-background",
230
+ style: {
231
+ minWidth: `${taskColumnWidth.toString()}px`,
232
+ width: `${taskColumnWidth.toString()}px`
233
+ },
234
+ children: [
235
+ /* @__PURE__ */ jsx("div", { className: "h-7 border-b border-border" }),
236
+ groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
237
+ /* @__PURE__ */ jsx("div", { className: "flex h-7 items-center border-b border-border/60 px-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: group.name }),
238
+ group.tasks.map((task) => /* @__PURE__ */ jsxs(
239
+ "div",
240
+ {
241
+ className: "flex h-8 items-center justify-between gap-2 border-b border-border/40 px-3 text-sm text-foreground",
242
+ children: [
243
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: task.title }),
244
+ task.assignee ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: task.assignee }) : null
245
+ ]
246
+ },
247
+ task.id
248
+ ))
249
+ ] }, group.id))
250
+ ]
251
+ }
252
+ );
253
+ }
254
+ function TimelineColumn({
255
+ geometry,
256
+ groups,
257
+ labels,
258
+ milestones,
259
+ now
260
+ }) {
261
+ return /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
262
+ /* @__PURE__ */ jsx(Axis, { geometry }),
263
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
264
+ groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
265
+ /* @__PURE__ */ jsx("div", { className: "h-7 border-b border-border/60 bg-muted/20" }),
266
+ group.tasks.map((task) => /* @__PURE__ */ jsx(TaskRow, { geometry, task }, task.id))
267
+ ] }, group.id)),
268
+ milestones.map((milestone) => /* @__PURE__ */ jsx(
269
+ MilestoneMarker,
270
+ {
271
+ geometry,
272
+ label: labels.milestone,
273
+ milestone
274
+ },
275
+ milestone.id
276
+ )),
277
+ /* @__PURE__ */ jsx(TodayLine, { geometry, label: labels.today, now })
278
+ ] })
279
+ ] });
280
+ }
281
+ const GanttChart = forwardRef(
282
+ (props, ref) => {
283
+ const {
284
+ className,
285
+ endDate,
286
+ groups,
287
+ labels,
288
+ locale = DEFAULT_LOCALE,
289
+ milestones = [],
290
+ now,
291
+ scale = "month",
292
+ startDate,
293
+ taskColumnWidth = 200,
294
+ ...rest
295
+ } = props;
296
+ const resolvedLabels = useMemo(
297
+ () => ({ ...DEFAULT_LABELS, ...labels }),
298
+ [labels]
299
+ );
300
+ const geometry = useChartGeometry({ endDate, locale, scale, startDate });
301
+ const nowDate = useMemo(() => now ? toDate(now) : /* @__PURE__ */ new Date(), [now]);
302
+ return /* @__PURE__ */ jsxs(
303
+ "div",
304
+ {
305
+ className: cn(
306
+ "flex w-full overflow-x-auto rounded-2xl border bg-background text-foreground",
307
+ className
308
+ ),
309
+ ref,
310
+ ...rest,
311
+ children: [
312
+ /* @__PURE__ */ jsx(LeftColumn, { groups, taskColumnWidth }),
313
+ /* @__PURE__ */ jsx(
314
+ TimelineColumn,
315
+ {
316
+ geometry,
317
+ groups,
318
+ labels: resolvedLabels,
319
+ milestones,
320
+ now: nowDate
321
+ }
322
+ )
323
+ ]
324
+ }
325
+ );
326
+ }
327
+ );
328
+ GanttChart.displayName = "GanttChart";
329
+ export {
330
+ GanttChart
331
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ GanttChart
3
+ } from "./gantt-chart";
4
+ export {
5
+ GanttChart
6
+ };
@@ -0,0 +1,343 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ createContext,
5
+ forwardRef,
6
+ useCallback,
7
+ useContext,
8
+ useId,
9
+ useMemo,
10
+ useState
11
+ } from "react";
12
+ import { cn } from "../../lib/utils";
13
+ const VIEWBOX_WIDTH = 1e3;
14
+ const VIEWBOX_HEIGHT = 500;
15
+ const FEEDBACK_DURATION_MS = 800;
16
+ const DEFAULT_LABELS = {
17
+ region: "Geography quiz map"
18
+ };
19
+ const QuizContext = createContext(null);
20
+ function useQuizContext() {
21
+ const ctx = useContext(QuizContext);
22
+ if (!ctx) {
23
+ throw new Error("GeographyQuizMap subcomponent used outside its root.");
24
+ }
25
+ return ctx;
26
+ }
27
+ function projectEquirectangular(position) {
28
+ const [lng, lat] = position;
29
+ const x = (lng + 180) / 360 * VIEWBOX_WIDTH;
30
+ const y = (90 - lat) / 180 * VIEWBOX_HEIGHT;
31
+ return { x, y };
32
+ }
33
+ function regionPath(region) {
34
+ const points = region.coordinates.map((coord, index) => {
35
+ const projected = projectEquirectangular(coord);
36
+ return `${index === 0 ? "M" : "L"}${projected.x.toString()},${projected.y.toString()}`;
37
+ }).join(" ");
38
+ return `${points} Z`;
39
+ }
40
+ function RegionShape({
41
+ disabled,
42
+ feedback,
43
+ isAnswerForCurrent,
44
+ isSelected,
45
+ onSelect,
46
+ region,
47
+ showAnswerFlash
48
+ }) {
49
+ let fill = "rgb(226, 232, 240)";
50
+ if (isSelected && feedback === "correct") fill = "rgb(34, 197, 94)";
51
+ else if (isSelected && feedback === "incorrect") fill = "rgb(239, 68, 68)";
52
+ else if (showAnswerFlash && isAnswerForCurrent && !isSelected)
53
+ fill = "rgba(34, 197, 94, 0.4)";
54
+ return /* @__PURE__ */ jsx(
55
+ "path",
56
+ {
57
+ "aria-label": region.name,
58
+ className: cn(
59
+ "stroke-background outline-none transition-colors",
60
+ disabled ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90 focus-visible:opacity-90"
61
+ ),
62
+ d: regionPath(region),
63
+ "data-region-id": region.id,
64
+ "data-state": isSelected ? feedback === "correct" ? "correct" : "incorrect" : showAnswerFlash && isAnswerForCurrent ? "answer" : void 0,
65
+ fill,
66
+ onClick: () => {
67
+ if (!disabled) onSelect(region.id);
68
+ },
69
+ onKeyDown: (event) => {
70
+ if (disabled) return;
71
+ if (event.key !== "Enter" && event.key !== " ") return;
72
+ event.preventDefault();
73
+ onSelect(region.id);
74
+ },
75
+ role: "button",
76
+ strokeWidth: 1,
77
+ tabIndex: disabled ? -1 : 0
78
+ }
79
+ );
80
+ }
81
+ function Stage({
82
+ backdrop,
83
+ backdropAlt,
84
+ current,
85
+ disabled,
86
+ feedback,
87
+ onSelect,
88
+ regions,
89
+ selectedRegionId
90
+ }) {
91
+ return /* @__PURE__ */ jsxs(
92
+ "svg",
93
+ {
94
+ className: "block h-full w-full",
95
+ preserveAspectRatio: "xMidYMid meet",
96
+ role: "img",
97
+ viewBox: `0 0 ${VIEWBOX_WIDTH.toString()} ${VIEWBOX_HEIGHT.toString()}`,
98
+ children: [
99
+ /* @__PURE__ */ jsx(
100
+ "rect",
101
+ {
102
+ className: "fill-muted",
103
+ height: VIEWBOX_HEIGHT,
104
+ width: VIEWBOX_WIDTH,
105
+ x: "0",
106
+ y: "0"
107
+ }
108
+ ),
109
+ backdrop ? /* @__PURE__ */ jsx(
110
+ "image",
111
+ {
112
+ "aria-label": backdropAlt,
113
+ height: VIEWBOX_HEIGHT,
114
+ href: backdrop,
115
+ preserveAspectRatio: "xMidYMid slice",
116
+ width: VIEWBOX_WIDTH,
117
+ x: "0",
118
+ y: "0"
119
+ }
120
+ ) : null,
121
+ regions.map((region) => /* @__PURE__ */ jsx(
122
+ RegionShape,
123
+ {
124
+ disabled,
125
+ feedback,
126
+ isAnswerForCurrent: current?.answerRegionId === region.id,
127
+ isSelected: selectedRegionId === region.id,
128
+ onSelect,
129
+ region,
130
+ showAnswerFlash: feedback === "incorrect"
131
+ },
132
+ region.id
133
+ ))
134
+ ]
135
+ }
136
+ );
137
+ }
138
+ const GeographyQuizMapPrompt = forwardRef(({ className, ...rest }, ref) => {
139
+ const { current, phase } = useQuizContext();
140
+ if (phase !== "playing" || !current) return null;
141
+ return /* @__PURE__ */ jsx(
142
+ "div",
143
+ {
144
+ className: cn(
145
+ "absolute inset-x-3 top-3 z-10 rounded-md border bg-background/95 px-3 py-2 text-center text-sm font-medium text-foreground shadow-sm backdrop-blur",
146
+ className
147
+ ),
148
+ "data-quiz-prompt": true,
149
+ ref,
150
+ ...rest,
151
+ children: current.prompt
152
+ }
153
+ );
154
+ });
155
+ GeographyQuizMapPrompt.displayName = "GeographyQuizMapPrompt";
156
+ const GeographyQuizMapScore = forwardRef(({ className, ...rest }, ref) => {
157
+ const { answers, totalQuestions } = useQuizContext();
158
+ const correct = answers.filter((entry) => entry.correct).length;
159
+ const accuracy = answers.length === 0 ? 0 : Math.round(correct / answers.length * 100);
160
+ return /* @__PURE__ */ jsx(
161
+ "div",
162
+ {
163
+ className: cn(
164
+ "absolute right-3 top-3 z-10 rounded-md border bg-background/95 px-2 py-1 text-xs font-medium text-foreground shadow-sm backdrop-blur",
165
+ className
166
+ ),
167
+ "data-quiz-score": true,
168
+ ref,
169
+ ...rest,
170
+ children: `${correct.toString()} / ${totalQuestions.toString()} \xB7 ${accuracy.toString()}%`
171
+ }
172
+ );
173
+ });
174
+ GeographyQuizMapScore.displayName = "GeographyQuizMapScore";
175
+ const GeographyQuizMapResults = forwardRef(({ className, ...rest }, ref) => {
176
+ const { answers, phase, totalQuestions } = useQuizContext();
177
+ if (phase !== "complete") return null;
178
+ const correct = answers.filter((entry) => entry.correct).length;
179
+ return /* @__PURE__ */ jsxs(
180
+ "div",
181
+ {
182
+ className: cn(
183
+ "absolute inset-3 z-20 flex flex-col gap-3 overflow-auto rounded-md border bg-background/95 p-4 text-sm text-foreground shadow-md backdrop-blur",
184
+ className
185
+ ),
186
+ "data-quiz-results": true,
187
+ ref,
188
+ ...rest,
189
+ children: [
190
+ /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: `Results \xB7 ${correct.toString()} / ${totalQuestions.toString()}` }),
191
+ /* @__PURE__ */ jsx("ol", { className: "space-y-1", children: answers.map((entry) => /* @__PURE__ */ jsxs(
192
+ "li",
193
+ {
194
+ className: cn(
195
+ "flex items-center gap-2 rounded-md border px-2 py-1",
196
+ entry.correct ? "border-emerald-300 bg-emerald-500/10" : "border-red-300 bg-red-500/10"
197
+ ),
198
+ "data-answer-correct": entry.correct ? "true" : "false",
199
+ "data-answer-id": entry.question.id,
200
+ children: [
201
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: entry.correct ? "\u2713" : "\u2717" }),
202
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: entry.question.prompt })
203
+ ]
204
+ },
205
+ entry.question.id
206
+ )) })
207
+ ]
208
+ }
209
+ );
210
+ });
211
+ GeographyQuizMapResults.displayName = "GeographyQuizMapResults";
212
+ function useQuizState(arguments_) {
213
+ const { onComplete, questions } = arguments_;
214
+ const [state, setState] = useState({
215
+ answers: [],
216
+ feedback: void 0,
217
+ questionIndex: 0,
218
+ selectedRegionId: void 0
219
+ });
220
+ const handleSelect = useCallback(
221
+ (regionId) => {
222
+ setState((current) => {
223
+ if (current.feedback) return current;
224
+ const question = questions[current.questionIndex];
225
+ if (!question) return current;
226
+ const correct = question.answerRegionId === regionId;
227
+ const next = {
228
+ ...current,
229
+ answers: [
230
+ ...current.answers,
231
+ { correct, question, selectedRegionId: regionId }
232
+ ],
233
+ feedback: correct ? "correct" : "incorrect",
234
+ selectedRegionId: regionId
235
+ };
236
+ scheduleAdvance(setState, onComplete, questions);
237
+ return next;
238
+ });
239
+ },
240
+ [onComplete, questions]
241
+ );
242
+ return { handleSelect, state };
243
+ }
244
+ function scheduleAdvance(setState, onComplete, questions) {
245
+ if (typeof window === "undefined") return;
246
+ window.setTimeout(() => {
247
+ setState((current) => {
248
+ const nextIndex = current.questionIndex + 1;
249
+ if (nextIndex >= questions.length) {
250
+ onComplete?.(current.answers);
251
+ return {
252
+ ...current,
253
+ feedback: void 0,
254
+ questionIndex: questions.length,
255
+ selectedRegionId: void 0
256
+ };
257
+ }
258
+ return {
259
+ ...current,
260
+ feedback: void 0,
261
+ questionIndex: nextIndex,
262
+ selectedRegionId: void 0
263
+ };
264
+ });
265
+ }, FEEDBACK_DURATION_MS);
266
+ }
267
+ const GeographyQuizMap = forwardRef(
268
+ (props, ref) => {
269
+ const {
270
+ backdrop,
271
+ backdropAlt,
272
+ children,
273
+ className,
274
+ labels,
275
+ onComplete,
276
+ questions,
277
+ regions,
278
+ ...rest
279
+ } = props;
280
+ const titleId = useId();
281
+ const resolvedLabels = useMemo(
282
+ () => ({ ...DEFAULT_LABELS, ...labels }),
283
+ [labels]
284
+ );
285
+ const { handleSelect, state } = useQuizState({ onComplete, questions });
286
+ const phase = state.questionIndex >= questions.length ? "complete" : "playing";
287
+ const current = questions[state.questionIndex];
288
+ const ctx = useMemo(
289
+ () => ({
290
+ answers: state.answers,
291
+ current,
292
+ feedback: state.feedback,
293
+ phase,
294
+ questionIndex: state.questionIndex,
295
+ totalQuestions: questions.length
296
+ }),
297
+ [
298
+ current,
299
+ phase,
300
+ questions.length,
301
+ state.answers,
302
+ state.feedback,
303
+ state.questionIndex
304
+ ]
305
+ );
306
+ return /* @__PURE__ */ jsx(QuizContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxs(
307
+ "section",
308
+ {
309
+ "aria-labelledby": titleId,
310
+ className: cn(
311
+ "relative aspect-[2/1] w-full overflow-hidden rounded-2xl border bg-background text-foreground",
312
+ className
313
+ ),
314
+ ref,
315
+ ...rest,
316
+ children: [
317
+ /* @__PURE__ */ jsx("span", { className: "sr-only", id: titleId, children: resolvedLabels.region }),
318
+ /* @__PURE__ */ jsx(
319
+ Stage,
320
+ {
321
+ backdrop,
322
+ backdropAlt,
323
+ current,
324
+ disabled: phase === "complete" || Boolean(state.feedback),
325
+ feedback: state.feedback,
326
+ onSelect: handleSelect,
327
+ regions,
328
+ selectedRegionId: state.selectedRegionId
329
+ }
330
+ ),
331
+ children
332
+ ]
333
+ }
334
+ ) });
335
+ }
336
+ );
337
+ GeographyQuizMap.displayName = "GeographyQuizMap";
338
+ export {
339
+ GeographyQuizMap,
340
+ GeographyQuizMapPrompt,
341
+ GeographyQuizMapResults,
342
+ GeographyQuizMapScore
343
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ GeographyQuizMap,
3
+ GeographyQuizMapPrompt,
4
+ GeographyQuizMapResults,
5
+ GeographyQuizMapScore
6
+ } from "./geography-quiz-map";
7
+ export {
8
+ GeographyQuizMap,
9
+ GeographyQuizMapPrompt,
10
+ GeographyQuizMapResults,
11
+ GeographyQuizMapScore
12
+ };