@suzumiyaaoba/mdxr 0.1.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 (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/dist/cli.d.mts +1 -0
  4. package/dist/cli.mjs +2902 -0
  5. package/dist/components.d.mts +791 -0
  6. package/dist/components.mjs +2 -0
  7. package/dist/config-SI9IyFiC.mjs +184 -0
  8. package/dist/doc-context-CEqzMYKv.d.mts +568 -0
  9. package/dist/index.d.mts +20 -0
  10. package/dist/index.mjs +4 -0
  11. package/dist/ui-GKFD0mx5.mjs +15986 -0
  12. package/package.json +131 -0
  13. package/skill/SKILL.md +49 -0
  14. package/skill/references/components/charts.md +172 -0
  15. package/skill/references/components/document.md +98 -0
  16. package/skill/references/components/forms.md +35 -0
  17. package/skill/references/components/investigation.md +198 -0
  18. package/skill/references/components/layout.md +68 -0
  19. package/skill/references/components/output.md +158 -0
  20. package/skill/references/components/planning.md +155 -0
  21. package/skill/references/components/reports.md +252 -0
  22. package/skill/references/components/shadcn.md +19 -0
  23. package/skill/references/components.md +186 -0
  24. package/skill/references/extending.md +48 -0
  25. package/src/ask-sheet.ts +40 -0
  26. package/src/assets/css.ts +392 -0
  27. package/src/assets/scripts.ts +78 -0
  28. package/src/catalog.ts +287 -0
  29. package/src/cli.ts +178 -0
  30. package/src/client/doc-events.ts +1108 -0
  31. package/src/client/entry.ts +27 -0
  32. package/src/client-js.ts +47 -0
  33. package/src/component-map.ts +47 -0
  34. package/src/components/ui/accordion.tsx +77 -0
  35. package/src/components/ui/alert-dialog.tsx +185 -0
  36. package/src/components/ui/alert.tsx +76 -0
  37. package/src/components/ui/aspect-ratio.tsx +22 -0
  38. package/src/components/ui/attachment.tsx +208 -0
  39. package/src/components/ui/avatar.tsx +106 -0
  40. package/src/components/ui/badge.tsx +52 -0
  41. package/src/components/ui/breadcrumb.tsx +121 -0
  42. package/src/components/ui/bubble.tsx +128 -0
  43. package/src/components/ui/button-group.tsx +88 -0
  44. package/src/components/ui/button.tsx +58 -0
  45. package/src/components/ui/calendar.tsx +226 -0
  46. package/src/components/ui/card.tsx +102 -0
  47. package/src/components/ui/carousel.tsx +246 -0
  48. package/src/components/ui/chart.tsx +379 -0
  49. package/src/components/ui/checkbox.tsx +27 -0
  50. package/src/components/ui/collapsible.tsx +19 -0
  51. package/src/components/ui/combobox.tsx +298 -0
  52. package/src/components/ui/command.tsx +193 -0
  53. package/src/components/ui/context-menu.tsx +271 -0
  54. package/src/components/ui/dialog.tsx +159 -0
  55. package/src/components/ui/direction.tsx +4 -0
  56. package/src/components/ui/drawer.tsx +227 -0
  57. package/src/components/ui/dropdown-menu.tsx +269 -0
  58. package/src/components/ui/empty.tsx +104 -0
  59. package/src/components/ui/field.tsx +237 -0
  60. package/src/components/ui/fieldset.tsx +32 -0
  61. package/src/components/ui/frame.tsx +87 -0
  62. package/src/components/ui/hover-card.tsx +50 -0
  63. package/src/components/ui/input-group.tsx +159 -0
  64. package/src/components/ui/input-otp.tsx +83 -0
  65. package/src/components/ui/input.tsx +19 -0
  66. package/src/components/ui/item.tsx +202 -0
  67. package/src/components/ui/kbd.tsx +26 -0
  68. package/src/components/ui/label.tsx +19 -0
  69. package/src/components/ui/marker.tsx +71 -0
  70. package/src/components/ui/menubar.tsx +284 -0
  71. package/src/components/ui/message-scroller.tsx +128 -0
  72. package/src/components/ui/message.tsx +91 -0
  73. package/src/components/ui/meter.tsx +80 -0
  74. package/src/components/ui/native-select.tsx +64 -0
  75. package/src/components/ui/navigation-menu.tsx +170 -0
  76. package/src/components/ui/pagination.tsx +133 -0
  77. package/src/components/ui/popover.tsx +87 -0
  78. package/src/components/ui/progress.tsx +82 -0
  79. package/src/components/ui/questionnaire.tsx +328 -0
  80. package/src/components/ui/radio-group.tsx +35 -0
  81. package/src/components/ui/resizable.tsx +49 -0
  82. package/src/components/ui/scroll-area.tsx +50 -0
  83. package/src/components/ui/select.tsx +201 -0
  84. package/src/components/ui/separator.tsx +22 -0
  85. package/src/components/ui/sheet.tsx +135 -0
  86. package/src/components/ui/sidebar.tsx +730 -0
  87. package/src/components/ui/skeleton.tsx +13 -0
  88. package/src/components/ui/slider.tsx +51 -0
  89. package/src/components/ui/spinner.tsx +16 -0
  90. package/src/components/ui/switch.tsx +31 -0
  91. package/src/components/ui/table.tsx +113 -0
  92. package/src/components/ui/tabs.tsx +82 -0
  93. package/src/components/ui/textarea.tsx +17 -0
  94. package/src/components/ui/toast.tsx +229 -0
  95. package/src/components/ui/toggle-group.tsx +87 -0
  96. package/src/components/ui/toggle.tsx +43 -0
  97. package/src/components/ui/tooltip.tsx +65 -0
  98. package/src/components.ts +78 -0
  99. package/src/config.ts +56 -0
  100. package/src/define.ts +122 -0
  101. package/src/doc-context.ts +20 -0
  102. package/src/editor.ts +94 -0
  103. package/src/format-error.ts +78 -0
  104. package/src/guards.ts +84 -0
  105. package/src/hooks/use-mobile.ts +21 -0
  106. package/src/html.ts +91 -0
  107. package/src/hydrate/export-index.ts +232 -0
  108. package/src/hydrate/import-scan.ts +169 -0
  109. package/src/hydrate/plugins.ts +118 -0
  110. package/src/hydrate/runtime-module.ts +145 -0
  111. package/src/hydrate-runtime.ts +67 -0
  112. package/src/hydrate.ts +147 -0
  113. package/src/index.ts +16 -0
  114. package/src/init.ts +57 -0
  115. package/src/langs.ts +148 -0
  116. package/src/lines.ts +53 -0
  117. package/src/load-user-module.ts +191 -0
  118. package/src/mdx.ts +248 -0
  119. package/src/paths.ts +36 -0
  120. package/src/rehype/shiki.ts +533 -0
  121. package/src/remark/alerts.ts +53 -0
  122. package/src/remark/ast.ts +96 -0
  123. package/src/remark/callouts.ts +25 -0
  124. package/src/remark/code-file.ts +85 -0
  125. package/src/remark/code-meta.ts +21 -0
  126. package/src/remark/directives.ts +170 -0
  127. package/src/remark/file-paths.ts +64 -0
  128. package/src/remark/headings.ts +131 -0
  129. package/src/remark/no-js.ts +40 -0
  130. package/src/render.ts +337 -0
  131. package/src/serve.ts +322 -0
  132. package/src/styles/globals.css +134 -0
  133. package/src/styles/shadcn.css +641 -0
  134. package/src/tailwind.ts +119 -0
  135. package/src/ui/approvals.tsx +76 -0
  136. package/src/ui/ask-question.tsx +386 -0
  137. package/src/ui/ask.tsx +206 -0
  138. package/src/ui/attrs.ts +51 -0
  139. package/src/ui/audit.tsx +139 -0
  140. package/src/ui/bar-chart.tsx +334 -0
  141. package/src/ui/benchmarks.tsx +143 -0
  142. package/src/ui/bits.tsx +537 -0
  143. package/src/ui/board.tsx +173 -0
  144. package/src/ui/bridge.tsx +207 -0
  145. package/src/ui/bumps.tsx +178 -0
  146. package/src/ui/callout.tsx +106 -0
  147. package/src/ui/changes.tsx +89 -0
  148. package/src/ui/chart-bits.tsx +52 -0
  149. package/src/ui/chart.ts +577 -0
  150. package/src/ui/checks.tsx +203 -0
  151. package/src/ui/child-index.tsx +44 -0
  152. package/src/ui/children.ts +43 -0
  153. package/src/ui/chips.tsx +49 -0
  154. package/src/ui/cmd.tsx +27 -0
  155. package/src/ui/columns.tsx +31 -0
  156. package/src/ui/comments.tsx +544 -0
  157. package/src/ui/compare.tsx +67 -0
  158. package/src/ui/decision.tsx +79 -0
  159. package/src/ui/deps.tsx +78 -0
  160. package/src/ui/details.tsx +43 -0
  161. package/src/ui/diff-parse.ts +307 -0
  162. package/src/ui/diff.tsx +458 -0
  163. package/src/ui/diffstat.tsx +58 -0
  164. package/src/ui/due.tsx +65 -0
  165. package/src/ui/effort.tsx +29 -0
  166. package/src/ui/endpoints.tsx +118 -0
  167. package/src/ui/envvars.tsx +96 -0
  168. package/src/ui/figure.tsx +37 -0
  169. package/src/ui/file-icon.ts +1020 -0
  170. package/src/ui/file-link.ts +32 -0
  171. package/src/ui/file-ref.tsx +39 -0
  172. package/src/ui/files.tsx +112 -0
  173. package/src/ui/findings.tsx +85 -0
  174. package/src/ui/flow.tsx +73 -0
  175. package/src/ui/funnel.tsx +121 -0
  176. package/src/ui/gantt.tsx +443 -0
  177. package/src/ui/gauges.tsx +135 -0
  178. package/src/ui/glossary.tsx +29 -0
  179. package/src/ui/graph-layout.ts +149 -0
  180. package/src/ui/graph-specs.tsx +102 -0
  181. package/src/ui/graph.tsx +278 -0
  182. package/src/ui/grid.tsx +119 -0
  183. package/src/ui/hypothesis.tsx +94 -0
  184. package/src/ui/icon.tsx +80 -0
  185. package/src/ui/incident.tsx +130 -0
  186. package/src/ui/index.ts +342 -0
  187. package/src/ui/ins-del.tsx +41 -0
  188. package/src/ui/json.tsx +190 -0
  189. package/src/ui/layout.ts +9 -0
  190. package/src/ui/line-chart.tsx +251 -0
  191. package/src/ui/matrix.tsx +208 -0
  192. package/src/ui/meta.tsx +73 -0
  193. package/src/ui/option.tsx +64 -0
  194. package/src/ui/owner.tsx +40 -0
  195. package/src/ui/packages.tsx +104 -0
  196. package/src/ui/pathway.tsx +96 -0
  197. package/src/ui/phase.tsx +41 -0
  198. package/src/ui/pie-chart.tsx +170 -0
  199. package/src/ui/plan.tsx +77 -0
  200. package/src/ui/pre.tsx +175 -0
  201. package/src/ui/priority.tsx +51 -0
  202. package/src/ui/props.tsx +77 -0
  203. package/src/ui/quadrant.tsx +172 -0
  204. package/src/ui/radar.tsx +200 -0
  205. package/src/ui/ref.tsx +160 -0
  206. package/src/ui/release.tsx +178 -0
  207. package/src/ui/req.tsx +42 -0
  208. package/src/ui/review.tsx +133 -0
  209. package/src/ui/risk.tsx +67 -0
  210. package/src/ui/sankey.tsx +287 -0
  211. package/src/ui/scatter.tsx +237 -0
  212. package/src/ui/schema.tsx +113 -0
  213. package/src/ui/score.tsx +105 -0
  214. package/src/ui/search.tsx +120 -0
  215. package/src/ui/series.tsx +38 -0
  216. package/src/ui/severity.tsx +69 -0
  217. package/src/ui/shadcn.tsx +216 -0
  218. package/src/ui/spark.tsx +86 -0
  219. package/src/ui/stack.tsx +57 -0
  220. package/src/ui/stats.tsx +63 -0
  221. package/src/ui/status-badge.tsx +66 -0
  222. package/src/ui/statuspage.tsx +238 -0
  223. package/src/ui/steps.tsx +74 -0
  224. package/src/ui/summary.tsx +41 -0
  225. package/src/ui/symbol-ref.tsx +73 -0
  226. package/src/ui/terminal.tsx +117 -0
  227. package/src/ui/tests.tsx +218 -0
  228. package/src/ui/timeline.tsx +63 -0
  229. package/src/ui/toc.tsx +56 -0
  230. package/src/ui/tones.ts +187 -0
  231. package/src/ui/trace.tsx +69 -0
  232. package/src/ui/tree.tsx +281 -0
  233. package/src/ui/treemap.tsx +128 -0
  234. package/src/ui/venn.tsx +258 -0
  235. package/src/ui/verdict.tsx +78 -0
  236. package/src/ui/waterfall.tsx +142 -0
@@ -0,0 +1,226 @@
1
+ "use client";
2
+
3
+ import { cn } from "cn";
4
+ import {
5
+ ChevronLeftIcon,
6
+ ChevronRightIcon,
7
+ ChevronDownIcon,
8
+ } from "lucide-react";
9
+ import * as React from "react";
10
+ import { DayPicker, getDefaultClassNames } from "react-day-picker";
11
+ import type { DayButton, Locale } from "react-day-picker";
12
+
13
+ import { Button, buttonVariants } from "./button.js";
14
+
15
+ function Calendar({
16
+ className,
17
+ classNames,
18
+ showOutsideDays = true,
19
+ captionLayout = "label",
20
+ buttonVariant = "ghost",
21
+ locale,
22
+ formatters,
23
+ components,
24
+ ...props
25
+ }: React.ComponentProps<typeof DayPicker> & {
26
+ buttonVariant?: React.ComponentProps<typeof Button>["variant"];
27
+ }) {
28
+ const defaultClassNames = getDefaultClassNames();
29
+
30
+ return (
31
+ <DayPicker
32
+ showOutsideDays={showOutsideDays}
33
+ className={cn(
34
+ "group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
35
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
36
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
37
+ className
38
+ )}
39
+ captionLayout={captionLayout}
40
+ locale={locale}
41
+ formatters={{
42
+ formatMonthDropdown: (date) =>
43
+ date.toLocaleString(locale?.code, { month: "short" }),
44
+ ...formatters,
45
+ }}
46
+ classNames={{
47
+ button_next: cn(
48
+ buttonVariants({ variant: buttonVariant }),
49
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
50
+ defaultClassNames.button_next
51
+ ),
52
+ button_previous: cn(
53
+ buttonVariants({ variant: buttonVariant }),
54
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
55
+ defaultClassNames.button_previous
56
+ ),
57
+ caption_label: cn(
58
+ "font-medium select-none",
59
+ captionLayout === "label"
60
+ ? "text-sm"
61
+ : "[&>svg]:text-muted-foreground flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5",
62
+ defaultClassNames.caption_label
63
+ ),
64
+ day: cn(
65
+ "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
66
+ props.showWeekNumber
67
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
68
+ : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
69
+ defaultClassNames.day
70
+ ),
71
+ disabled: cn(
72
+ "text-muted-foreground opacity-50",
73
+ defaultClassNames.disabled
74
+ ),
75
+ dropdown: cn(
76
+ "bg-popover absolute inset-0 opacity-0",
77
+ defaultClassNames.dropdown
78
+ ),
79
+ dropdown_root: cn(
80
+ "relative rounded-(--cell-radius)",
81
+ defaultClassNames.dropdown_root
82
+ ),
83
+ dropdowns: cn(
84
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
85
+ defaultClassNames.dropdowns
86
+ ),
87
+ hidden: cn("invisible", defaultClassNames.hidden),
88
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
89
+ month_caption: cn(
90
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
91
+ defaultClassNames.month_caption
92
+ ),
93
+ month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
94
+ months: cn(
95
+ "relative flex flex-col gap-4 md:flex-row",
96
+ defaultClassNames.months
97
+ ),
98
+ nav: cn(
99
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
100
+ defaultClassNames.nav
101
+ ),
102
+ outside: cn(
103
+ "text-muted-foreground aria-selected:text-muted-foreground",
104
+ defaultClassNames.outside
105
+ ),
106
+ range_end: cn(
107
+ "bg-muted after:bg-muted relative isolate z-0 rounded-r-(--cell-radius) after:absolute after:inset-y-0 after:left-0 after:w-4",
108
+ defaultClassNames.range_end
109
+ ),
110
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
111
+ range_start: cn(
112
+ "bg-muted after:bg-muted relative isolate z-0 rounded-l-(--cell-radius) after:absolute after:inset-y-0 after:right-0 after:w-4",
113
+ defaultClassNames.range_start
114
+ ),
115
+ root: cn("w-fit", defaultClassNames.root),
116
+ today: cn(
117
+ "bg-muted text-foreground rounded-(--cell-radius) data-[selected=true]:rounded-none",
118
+ defaultClassNames.today
119
+ ),
120
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
121
+ week_number: cn(
122
+ "text-muted-foreground text-[0.8rem] select-none",
123
+ defaultClassNames.week_number
124
+ ),
125
+ week_number_header: cn(
126
+ "w-(--cell-size) select-none",
127
+ defaultClassNames.week_number_header
128
+ ),
129
+ weekday: cn(
130
+ "text-muted-foreground flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal select-none",
131
+ defaultClassNames.weekday
132
+ ),
133
+ weekdays: cn("flex", defaultClassNames.weekdays),
134
+ ...classNames,
135
+ }}
136
+ components={{
137
+ Chevron: ({ className, orientation, ...props }) => {
138
+ if (orientation === "left") {
139
+ return (
140
+ <ChevronLeftIcon className={cn("size-4", className)} {...props} />
141
+ );
142
+ }
143
+
144
+ if (orientation === "right") {
145
+ return (
146
+ <ChevronRightIcon
147
+ className={cn("size-4", className)}
148
+ {...props}
149
+ />
150
+ );
151
+ }
152
+
153
+ return (
154
+ <ChevronDownIcon className={cn("size-4", className)} {...props} />
155
+ );
156
+ },
157
+ DayButton: ({ ...props }) => (
158
+ <CalendarDayButton locale={locale} {...props} />
159
+ ),
160
+ Root: ({ className, rootRef, ...props }) => {
161
+ return (
162
+ <div
163
+ data-slot="calendar"
164
+ ref={rootRef}
165
+ className={cn(className)}
166
+ {...props}
167
+ />
168
+ );
169
+ },
170
+ WeekNumber: ({ children, ...props }) => {
171
+ return (
172
+ <td {...props}>
173
+ <div className="flex size-(--cell-size) items-center justify-center text-center">
174
+ {children}
175
+ </div>
176
+ </td>
177
+ );
178
+ },
179
+ ...components,
180
+ }}
181
+ {...props}
182
+ />
183
+ );
184
+ }
185
+
186
+ function CalendarDayButton({
187
+ className,
188
+ day,
189
+ modifiers,
190
+ locale,
191
+ ...props
192
+ }: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
193
+ const defaultClassNames = getDefaultClassNames();
194
+
195
+ const ref = React.useRef<HTMLButtonElement>(null);
196
+ React.useEffect(() => {
197
+ if (modifiers.focused) {
198
+ ref.current?.focus();
199
+ }
200
+ }, [modifiers.focused]);
201
+
202
+ return (
203
+ <Button
204
+ variant="ghost"
205
+ size="icon"
206
+ data-day={day.date.toLocaleDateString(locale?.code)}
207
+ data-selected-single={
208
+ modifiers.selected &&
209
+ !modifiers.range_start &&
210
+ !modifiers.range_end &&
211
+ !modifiers.range_middle
212
+ }
213
+ data-range-start={modifiers.range_start}
214
+ data-range-end={modifiers.range_end}
215
+ data-range-middle={modifiers.range_middle}
216
+ className={cn(
217
+ "group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) [&>span]:text-xs [&>span]:opacity-70",
218
+ defaultClassNames.day,
219
+ className
220
+ )}
221
+ {...props}
222
+ />
223
+ );
224
+ }
225
+
226
+ export { Calendar, CalendarDayButton };
@@ -0,0 +1,102 @@
1
+ import { cn } from "cn";
2
+ import * as React from "react";
3
+
4
+ function Card({
5
+ className,
6
+ size = "default",
7
+ ...props
8
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
9
+ return (
10
+ <div
11
+ data-slot="card"
12
+ data-size={size}
13
+ className={cn(
14
+ "group/card bg-card text-card-foreground ring-foreground/10 flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl py-(--card-spacing) text-sm ring-1 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
23
+ return (
24
+ <div
25
+ data-slot="card-header"
26
+ className={cn(
27
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
36
+ return (
37
+ <div
38
+ data-slot="card-title"
39
+ className={cn(
40
+ "font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
41
+ className
42
+ )}
43
+ {...props}
44
+ />
45
+ );
46
+ }
47
+
48
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
49
+ return (
50
+ <div
51
+ data-slot="card-description"
52
+ className={cn("text-muted-foreground text-sm", className)}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+
58
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
59
+ return (
60
+ <div
61
+ data-slot="card-action"
62
+ className={cn(
63
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
64
+ className
65
+ )}
66
+ {...props}
67
+ />
68
+ );
69
+ }
70
+
71
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
72
+ return (
73
+ <div
74
+ data-slot="card-content"
75
+ className={cn("px-(--card-spacing)", className)}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
82
+ return (
83
+ <div
84
+ data-slot="card-footer"
85
+ className={cn(
86
+ "bg-muted/50 flex items-center rounded-b-xl border-t p-(--card-spacing)",
87
+ className
88
+ )}
89
+ {...props}
90
+ />
91
+ );
92
+ }
93
+
94
+ export {
95
+ Card,
96
+ CardHeader,
97
+ CardFooter,
98
+ CardTitle,
99
+ CardAction,
100
+ CardDescription,
101
+ CardContent,
102
+ };
@@ -0,0 +1,246 @@
1
+ import { cn } from "cn";
2
+ import useEmblaCarousel from "embla-carousel-react";
3
+ import type { UseEmblaCarouselType } from "embla-carousel-react";
4
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
5
+ import * as React from "react";
6
+
7
+ import { Button } from "./button.js";
8
+
9
+ type CarouselApi = UseEmblaCarouselType[1];
10
+ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
11
+ type CarouselOptions = UseCarouselParameters[0];
12
+ type CarouselPlugin = UseCarouselParameters[1];
13
+
14
+ interface CarouselProps {
15
+ opts?: CarouselOptions;
16
+ plugins?: CarouselPlugin;
17
+ orientation?: "horizontal" | "vertical";
18
+ setApi?: (api: CarouselApi) => void;
19
+ }
20
+
21
+ type CarouselContextProps = {
22
+ carouselRef: ReturnType<typeof useEmblaCarousel>[0];
23
+ api: ReturnType<typeof useEmblaCarousel>[1];
24
+ scrollPrev: () => void;
25
+ scrollNext: () => void;
26
+ canScrollPrev: boolean;
27
+ canScrollNext: boolean;
28
+ } & CarouselProps;
29
+
30
+ const CarouselContext = React.createContext<CarouselContextProps | null>(null);
31
+
32
+ function useCarousel() {
33
+ const context = React.useContext(CarouselContext);
34
+
35
+ if (!context) {
36
+ throw new Error("useCarousel must be used within a <Carousel />");
37
+ }
38
+
39
+ return context;
40
+ }
41
+
42
+ function Carousel({
43
+ orientation = "horizontal",
44
+ opts,
45
+ setApi,
46
+ plugins,
47
+ className,
48
+ children,
49
+ ...props
50
+ }: React.ComponentProps<"div"> & CarouselProps) {
51
+ const [carouselRef, api] = useEmblaCarousel(
52
+ {
53
+ ...opts,
54
+ axis: orientation === "horizontal" ? "x" : "y",
55
+ },
56
+ plugins
57
+ );
58
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
59
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
60
+
61
+ const onSelect = React.useCallback((api: CarouselApi) => {
62
+ if (!api) {
63
+ return;
64
+ }
65
+ setCanScrollPrev(api.canScrollPrev());
66
+ setCanScrollNext(api.canScrollNext());
67
+ }, []);
68
+
69
+ const scrollPrev = React.useCallback(() => {
70
+ api?.scrollPrev();
71
+ }, [api]);
72
+
73
+ const scrollNext = React.useCallback(() => {
74
+ api?.scrollNext();
75
+ }, [api]);
76
+
77
+ const handleKeyDown = React.useCallback(
78
+ (event: React.KeyboardEvent<HTMLDivElement>) => {
79
+ if (event.key === "ArrowLeft") {
80
+ event.preventDefault();
81
+ scrollPrev();
82
+ } else if (event.key === "ArrowRight") {
83
+ event.preventDefault();
84
+ scrollNext();
85
+ }
86
+ },
87
+ [scrollPrev, scrollNext]
88
+ );
89
+
90
+ React.useEffect(() => {
91
+ if (!api || !setApi) {
92
+ return;
93
+ }
94
+ setApi(api);
95
+ }, [api, setApi]);
96
+
97
+ React.useEffect(() => {
98
+ if (!api) {
99
+ return;
100
+ }
101
+ onSelect(api);
102
+ api.on("reInit", onSelect);
103
+ api.on("select", onSelect);
104
+
105
+ return () => {
106
+ api?.off("reInit", onSelect);
107
+ api?.off("select", onSelect);
108
+ };
109
+ }, [api, onSelect]);
110
+
111
+ return (
112
+ <CarouselContext.Provider
113
+ value={{
114
+ api: api,
115
+ canScrollNext,
116
+ canScrollPrev,
117
+ carouselRef,
118
+ opts,
119
+ orientation:
120
+ orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
121
+ scrollNext,
122
+ scrollPrev,
123
+ }}
124
+ >
125
+ <div
126
+ onKeyDownCapture={handleKeyDown}
127
+ className={cn("relative", className)}
128
+ role="region"
129
+ aria-roledescription="carousel"
130
+ data-slot="carousel"
131
+ {...props}
132
+ >
133
+ {children}
134
+ </div>
135
+ </CarouselContext.Provider>
136
+ );
137
+ }
138
+
139
+ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
140
+ const { carouselRef, orientation } = useCarousel();
141
+
142
+ return (
143
+ <div
144
+ ref={carouselRef}
145
+ className="overflow-hidden"
146
+ data-slot="carousel-content"
147
+ >
148
+ <div
149
+ className={cn(
150
+ "flex",
151
+ orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
152
+ className
153
+ )}
154
+ {...props}
155
+ />
156
+ </div>
157
+ );
158
+ }
159
+
160
+ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
161
+ const { orientation } = useCarousel();
162
+
163
+ return (
164
+ <div
165
+ role="group"
166
+ aria-roledescription="slide"
167
+ data-slot="carousel-item"
168
+ className={cn(
169
+ "min-w-0 shrink-0 grow-0 basis-full",
170
+ orientation === "horizontal" ? "pl-4" : "pt-4",
171
+ className
172
+ )}
173
+ {...props}
174
+ />
175
+ );
176
+ }
177
+
178
+ function CarouselPrevious({
179
+ className,
180
+ variant = "outline",
181
+ size = "icon-sm",
182
+ ...props
183
+ }: React.ComponentProps<typeof Button>) {
184
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
185
+
186
+ return (
187
+ <Button
188
+ data-slot="carousel-previous"
189
+ variant={variant}
190
+ size={size}
191
+ className={cn(
192
+ "absolute touch-manipulation rounded-full",
193
+ orientation === "horizontal"
194
+ ? "inset-y-0 -left-12 my-auto"
195
+ : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
196
+ className
197
+ )}
198
+ disabled={!canScrollPrev}
199
+ onClick={scrollPrev}
200
+ {...props}
201
+ >
202
+ <ChevronLeftIcon />
203
+ <span className="sr-only">Previous slide</span>
204
+ </Button>
205
+ );
206
+ }
207
+
208
+ function CarouselNext({
209
+ className,
210
+ variant = "outline",
211
+ size = "icon-sm",
212
+ ...props
213
+ }: React.ComponentProps<typeof Button>) {
214
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
215
+
216
+ return (
217
+ <Button
218
+ data-slot="carousel-next"
219
+ variant={variant}
220
+ size={size}
221
+ className={cn(
222
+ "absolute touch-manipulation rounded-full",
223
+ orientation === "horizontal"
224
+ ? "inset-y-0 -right-12 my-auto"
225
+ : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
226
+ className
227
+ )}
228
+ disabled={!canScrollNext}
229
+ onClick={scrollNext}
230
+ {...props}
231
+ >
232
+ <ChevronRightIcon />
233
+ <span className="sr-only">Next slide</span>
234
+ </Button>
235
+ );
236
+ }
237
+
238
+ export {
239
+ type CarouselApi,
240
+ Carousel,
241
+ CarouselContent,
242
+ CarouselItem,
243
+ CarouselPrevious,
244
+ CarouselNext,
245
+ useCarousel,
246
+ };