@tulipfarm/sprout 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +183 -2
- package/dist/components/accordion.d.ts +28 -0
- package/dist/components/accordion.js +26 -0
- package/dist/components/alert-dialog.d.ts +60 -0
- package/dist/components/alert-dialog.js +69 -0
- package/dist/components/alert.d.ts +30 -0
- package/dist/components/alert.js +35 -0
- package/dist/components/aspect-ratio.d.ts +7 -0
- package/dist/components/aspect-ratio.js +6 -0
- package/dist/components/attachment.d.ts +118 -0
- package/dist/components/attachment.js +285 -0
- package/dist/components/avatar.d.ts +32 -0
- package/dist/components/avatar.js +73 -0
- package/dist/components/badge.d.ts +21 -0
- package/dist/components/badge.js +27 -0
- package/dist/components/brand-icon.d.ts +28 -0
- package/dist/components/brand-icon.js +78 -0
- package/dist/components/breadcrumb.d.ts +29 -0
- package/dist/components/breadcrumb.js +44 -0
- package/dist/components/button-group.d.ts +28 -0
- package/dist/components/button-group.js +36 -0
- package/dist/components/button.d.ts +79 -0
- package/dist/components/button.js +93 -0
- package/dist/components/calendar.d.ts +27 -0
- package/dist/components/calendar.js +107 -0
- package/dist/components/canvas.d.ts +78 -0
- package/dist/components/canvas.js +180 -0
- package/dist/components/card.d.ts +43 -0
- package/dist/components/card.js +42 -0
- package/dist/components/carousel.d.ts +57 -0
- package/dist/components/carousel.js +422 -0
- package/dist/components/chart.d.ts +112 -0
- package/dist/components/chart.js +172 -0
- package/dist/components/chat-confirmation.d.ts +42 -0
- package/dist/components/chat-confirmation.js +62 -0
- package/dist/components/chat-plan.d.ts +21 -0
- package/dist/components/chat-plan.js +35 -0
- package/dist/components/chat-sources.d.ts +64 -0
- package/dist/components/chat-sources.js +66 -0
- package/dist/components/checkbox.d.ts +16 -0
- package/dist/components/checkbox.js +17 -0
- package/dist/components/choice-list.d.ts +53 -0
- package/dist/components/choice-list.js +36 -0
- package/dist/components/code-block.d.ts +35 -0
- package/dist/components/code-block.js +95 -0
- package/dist/components/collapsible.d.ts +24 -0
- package/dist/components/collapsible.js +27 -0
- package/dist/components/combobox.d.ts +95 -0
- package/dist/components/combobox.js +106 -0
- package/dist/components/command.d.ts +94 -0
- package/dist/components/command.js +414 -0
- package/dist/components/context-menu.d.ts +57 -0
- package/dist/components/context-menu.js +60 -0
- package/dist/components/dialog.d.ts +60 -0
- package/dist/components/dialog.js +79 -0
- package/dist/components/direction.d.ts +6 -0
- package/dist/components/direction.js +6 -0
- package/dist/components/dropdown-menu.d.ts +62 -0
- package/dist/components/dropdown-menu.js +59 -0
- package/dist/components/field.d.ts +117 -0
- package/dist/components/field.js +150 -0
- package/dist/components/file-viewer.d.ts +120 -0
- package/dist/components/file-viewer.js +373 -0
- package/dist/components/form.d.ts +87 -0
- package/dist/components/form.js +55 -0
- package/dist/components/glass-banner.d.ts +27 -0
- package/dist/components/glass-banner.js +116 -0
- package/dist/components/gradient-banner.d.ts +37 -0
- package/dist/components/gradient-banner.js +43 -0
- package/dist/components/hover-card.d.ts +27 -0
- package/dist/components/hover-card.js +26 -0
- package/dist/components/input-group.d.ts +68 -0
- package/dist/components/input-group.js +88 -0
- package/dist/components/input-otp.d.ts +38 -0
- package/dist/components/input-otp.js +43 -0
- package/dist/components/input.d.ts +16 -0
- package/dist/components/input.js +18 -0
- package/dist/components/kbd.d.ts +12 -0
- package/dist/components/kbd.js +11 -0
- package/dist/components/label.d.ts +8 -0
- package/dist/components/label.js +10 -0
- package/dist/components/mention-list.d.ts +54 -0
- package/dist/components/mention-list.js +31 -0
- package/dist/components/menubar.d.ts +64 -0
- package/dist/components/menubar.js +68 -0
- package/dist/components/message-actions.d.ts +34 -0
- package/dist/components/message-actions.js +68 -0
- package/dist/components/message-response.d.ts +20 -0
- package/dist/components/message-response.js +46 -0
- package/dist/components/message-scroller.d.ts +71 -0
- package/dist/components/message-scroller.js +217 -0
- package/dist/components/message.d.ts +49 -0
- package/dist/components/message.js +59 -0
- package/dist/components/navigation-menu.d.ts +45 -0
- package/dist/components/navigation-menu.js +54 -0
- package/dist/components/notice-banner.d.ts +14 -0
- package/dist/components/notice-banner.js +11 -0
- package/dist/components/page-content.d.ts +7 -0
- package/dist/components/page-content.js +9 -0
- package/dist/components/page-header.d.ts +37 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/pagination.d.ts +29 -0
- package/dist/components/pagination.js +29 -0
- package/dist/components/plan-outline.d.ts +60 -0
- package/dist/components/plan-outline.js +108 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +35 -0
- package/dist/components/progress.d.ts +27 -0
- package/dist/components/progress.js +35 -0
- package/dist/components/prompt-list.d.ts +23 -0
- package/dist/components/prompt-list.js +11 -0
- package/dist/components/questionnaire.d.ts +148 -0
- package/dist/components/questionnaire.js +1001 -0
- package/dist/components/radio-group.d.ts +16 -0
- package/dist/components/radio-group.js +21 -0
- package/dist/components/reasoning-effort.d.ts +24 -0
- package/dist/components/reasoning-effort.js +269 -0
- package/dist/components/resizable.d.ts +67 -0
- package/dist/components/resizable.js +536 -0
- package/dist/components/result-card.d.ts +58 -0
- package/dist/components/result-card.js +88 -0
- package/dist/components/review-card.d.ts +62 -0
- package/dist/components/review-card.js +49 -0
- package/dist/components/rich-text-editor.d.ts +51 -0
- package/dist/components/rich-text-editor.js +248 -0
- package/dist/components/schema-diagram.d.ts +52 -0
- package/dist/components/schema-diagram.js +226 -0
- package/dist/components/scroll-area.d.ts +22 -0
- package/dist/components/scroll-area.js +19 -0
- package/dist/components/search-dialog.d.ts +103 -0
- package/dist/components/search-dialog.js +64 -0
- package/dist/components/search-field.d.ts +17 -0
- package/dist/components/search-field.js +27 -0
- package/dist/components/select.d.ts +57 -0
- package/dist/components/select.js +57 -0
- package/dist/components/selection-slot.d.ts +18 -0
- package/dist/components/selection-slot.js +53 -0
- package/dist/components/separator.d.ts +48 -0
- package/dist/components/separator.js +54 -0
- package/dist/components/settings-list.d.ts +26 -0
- package/dist/components/settings-list.js +17 -0
- package/dist/components/sheet.d.ts +70 -0
- package/dist/components/sheet.js +77 -0
- package/dist/components/sidebar.d.ts +70 -0
- package/dist/components/sidebar.js +187 -0
- package/dist/components/skeleton.d.ts +31 -0
- package/dist/components/skeleton.js +32 -0
- package/dist/components/slider.d.ts +17 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/spinner.d.ts +37 -0
- package/dist/components/spinner.js +43 -0
- package/dist/components/state-panel-samples.d.ts +25 -0
- package/dist/components/state-panel-samples.js +423 -0
- package/dist/components/state-panel.d.ts +35 -0
- package/dist/components/state-panel.js +29 -0
- package/dist/components/state-sample.d.ts +63 -0
- package/dist/components/state-sample.js +42 -0
- package/dist/components/switch.d.ts +12 -0
- package/dist/components/switch.js +14 -0
- package/dist/components/table-nuqs.d.ts +16 -0
- package/dist/components/table-nuqs.js +59 -0
- package/dist/components/table.d.ts +120 -0
- package/dist/components/table.js +663 -0
- package/dist/components/tabs.d.ts +37 -0
- package/dist/components/tabs.js +36 -0
- package/dist/components/textarea.d.ts +18 -0
- package/dist/components/textarea.js +19 -0
- package/dist/components/toast.d.ts +45 -0
- package/dist/components/toast.js +83 -0
- package/dist/components/toggle-group.d.ts +30 -0
- package/dist/components/toggle-group.js +39 -0
- package/dist/components/toggle.d.ts +27 -0
- package/dist/components/toggle.js +21 -0
- package/dist/components/tool-activity.d.ts +79 -0
- package/dist/components/tool-activity.js +226 -0
- package/dist/components/toolbar-status.d.ts +21 -0
- package/dist/components/toolbar-status.js +14 -0
- package/dist/components/tooltip.d.ts +31 -0
- package/dist/components/tooltip.js +99 -0
- package/dist/components/typed-text.d.ts +17 -0
- package/dist/components/typed-text.js +56 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +88 -0
- package/dist/internal/brand-glyphs.d.ts +342 -0
- package/dist/internal/brand-glyphs.js +238 -0
- package/dist/internal/classes.d.ts +3 -0
- package/dist/internal/classes.js +20 -0
- package/dist/internal/fieldset.d.ts +6 -0
- package/dist/internal/fieldset.js +38 -0
- package/dist/internal/file-kind.d.ts +4 -0
- package/dist/internal/file-kind.js +62 -0
- package/dist/internal/languages.d.ts +7 -0
- package/dist/internal/languages.js +1010 -0
- package/dist/internal/message-scroller.d.ts +102 -0
- package/dist/internal/message-scroller.js +637 -0
- package/dist/internal/pressable.d.ts +2 -0
- package/dist/internal/pressable.js +29 -0
- package/dist/internal/syntax.d.ts +14 -0
- package/dist/internal/syntax.js +137 -0
- package/dist/styles/accordion.css +102 -0
- package/dist/styles/alert-dialog.css +198 -0
- package/dist/styles/alert.css +149 -0
- package/dist/styles/aspect-ratio.css +8 -0
- package/dist/styles/attachment.css +720 -0
- package/dist/styles/avatar.css +186 -0
- package/dist/styles/badge.css +178 -0
- package/dist/styles/brand-icon.css +104 -0
- package/dist/styles/breadcrumb.css +121 -0
- package/dist/styles/button-group.css +113 -0
- package/dist/styles/button.css +380 -0
- package/dist/styles/calendar.css +295 -0
- package/dist/styles/canvas.css +327 -0
- package/dist/styles/card.css +146 -0
- package/dist/styles/carousel.css +107 -0
- package/dist/styles/chart.css +227 -0
- package/dist/styles/chat-confirmation.css +29 -0
- package/dist/styles/chat-plan.css +109 -0
- package/dist/styles/chat-sources.css +191 -0
- package/dist/styles/checkbox.css +123 -0
- package/dist/styles/choice-list.css +343 -0
- package/dist/styles/code-block.css +211 -0
- package/dist/styles/collapsible.css +72 -0
- package/dist/styles/combobox.css +347 -0
- package/dist/styles/command.css +233 -0
- package/dist/styles/context-menu.css +151 -0
- package/dist/styles/dialog.css +237 -0
- package/dist/styles/dropdown-menu.css +150 -0
- package/dist/styles/field.css +341 -0
- package/dist/styles/file-viewer.css +278 -0
- package/dist/styles/form.css +155 -0
- package/dist/styles/glass-banner.css +308 -0
- package/dist/styles/gradient-banner.css +256 -0
- package/dist/styles/hover-card.css +95 -0
- package/dist/styles/index.css +262 -0
- package/dist/styles/input-group.css +290 -0
- package/dist/styles/input-otp.css +138 -0
- package/dist/styles/input.css +99 -0
- package/dist/styles/kbd.css +59 -0
- package/dist/styles/label.css +89 -0
- package/dist/styles/mention-list.css +166 -0
- package/dist/styles/menubar.css +226 -0
- package/dist/styles/message-actions.css +83 -0
- package/dist/styles/message-response.css +176 -0
- package/dist/styles/message-scroller.css +136 -0
- package/dist/styles/message.css +310 -0
- package/dist/styles/navigation-menu.css +291 -0
- package/dist/styles/notice-banner.css +68 -0
- package/dist/styles/page-content.css +21 -0
- package/dist/styles/page-header.css +156 -0
- package/dist/styles/pagination.css +72 -0
- package/dist/styles/plan-outline.css +393 -0
- package/dist/styles/popover.css +140 -0
- package/dist/styles/progress.css +93 -0
- package/dist/styles/prompt-list.css +165 -0
- package/dist/styles/questionnaire.css +323 -0
- package/dist/styles/radio-group.css +120 -0
- package/dist/styles/reasoning-effort.css +157 -0
- package/dist/styles/resizable.css +108 -0
- package/dist/styles/result-card.css +215 -0
- package/dist/styles/review-card.css +235 -0
- package/dist/styles/rich-text-editor.css +169 -0
- package/dist/styles/schema-diagram.css +419 -0
- package/dist/styles/scroll-area.css +77 -0
- package/dist/styles/search-dialog.css +363 -0
- package/dist/styles/search-field.css +17 -0
- package/dist/styles/select.css +285 -0
- package/dist/styles/selection-slot.css +31 -0
- package/dist/styles/separator.css +206 -0
- package/dist/styles/settings-list.css +81 -0
- package/dist/styles/sheet.css +361 -0
- package/dist/styles/sidebar.css +363 -0
- package/dist/styles/skeleton.css +80 -0
- package/dist/styles/slider.css +142 -0
- package/dist/styles/spinner.css +39 -0
- package/dist/styles/state-panel-samples.css +41 -0
- package/dist/styles/state-panel.css +182 -0
- package/dist/styles/state-sample.css +285 -0
- package/dist/styles/switch.css +128 -0
- package/dist/styles/table.css +604 -0
- package/dist/styles/tabs.css +223 -0
- package/dist/styles/textarea.css +78 -0
- package/dist/styles/themes/chatgpt.css +125 -0
- package/dist/styles/themes/claude.css +125 -0
- package/dist/styles/themes/cloudflare.css +125 -0
- package/dist/styles/themes/discord.css +125 -0
- package/dist/styles/themes/github.css +125 -0
- package/dist/styles/themes/grok.css +125 -0
- package/dist/styles/themes/linear.css +126 -0
- package/dist/styles/themes/neutral.css +125 -0
- package/dist/styles/themes/notion.css +125 -0
- package/dist/styles/themes/perplexity.css +125 -0
- package/dist/styles/themes/raycast.css +125 -0
- package/dist/styles/themes/spotify.css +125 -0
- package/dist/styles/themes/stripe.css +125 -0
- package/dist/styles/themes/supabase.css +125 -0
- package/dist/styles/themes/tulipfarm.css +125 -0
- package/dist/styles/themes/vercel.css +126 -0
- package/dist/styles/toast.css +197 -0
- package/dist/styles/toggle-group.css +161 -0
- package/dist/styles/toggle.css +119 -0
- package/dist/styles/tool-activity.css +396 -0
- package/dist/styles/toolbar-status.css +52 -0
- package/dist/styles/tooltip.css +122 -0
- package/dist/styles/typed-text.css +17 -0
- package/package.json +443 -4
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
2
|
+
export declare const cardSizeNames: readonly ["default", "sm"];
|
|
3
|
+
export declare const cardSurfaceNames: readonly ["default", "raised"];
|
|
4
|
+
export type CardSize = (typeof cardSizeNames)[number];
|
|
5
|
+
export type CardSurface = (typeof cardSurfaceNames)[number];
|
|
6
|
+
export interface CardVariantOptions {
|
|
7
|
+
size?: CardSize | null;
|
|
8
|
+
surface?: CardSurface | null;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function cardVariants({ size, surface, className, }?: CardVariantOptions): string;
|
|
12
|
+
export interface CardState extends Record<string, unknown> {
|
|
13
|
+
slot: "card";
|
|
14
|
+
size: CardSize;
|
|
15
|
+
surface: CardSurface;
|
|
16
|
+
}
|
|
17
|
+
export interface CardProps extends useRender.ComponentProps<"div", CardState> {
|
|
18
|
+
/** `sm` tightens padding and gaps from 16px to 12px and shrinks the title. */
|
|
19
|
+
size?: CardSize;
|
|
20
|
+
/** `raised` lifts the card off a surface of the same color. */
|
|
21
|
+
surface?: CardSurface;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A bordered panel that groups related content. Compose it with
|
|
25
|
+
* `CardHeader`, `CardContent`, and `CardFooter`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Card({ className, size, surface, render, ref, ...props }: CardProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
28
|
+
type PartSlot = "card-header" | "card-title" | "card-description" | "card-action" | "card-content" | "card-footer";
|
|
29
|
+
export interface CardPartState extends Record<string, unknown> {
|
|
30
|
+
slot: PartSlot;
|
|
31
|
+
}
|
|
32
|
+
export type CardPartProps = useRender.ComponentProps<"div", CardPartState>;
|
|
33
|
+
/** Lays out the title, description, and an optional `CardAction`. */
|
|
34
|
+
export declare const CardHeader: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
35
|
+
/** A `div` by default. Pass `render={<h3 />}` to put it in the outline. */
|
|
36
|
+
export declare const CardTitle: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
37
|
+
export declare const CardDescription: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
38
|
+
/** Pins a badge or compact control to the header's top-right corner. */
|
|
39
|
+
export declare const CardAction: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
40
|
+
export declare const CardContent: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
41
|
+
/** A tinted bar along the bottom edge for actions and status. */
|
|
42
|
+
export declare const CardFooter: ({ className, render, ref, ...props }: CardPartProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cn } from "../internal/classes.js";
|
|
4
|
+
export const cardSizeNames = ["default", "sm"];
|
|
5
|
+
export const cardSurfaceNames = ["default", "raised"];
|
|
6
|
+
export function cardVariants({ size, surface, className, } = {}) {
|
|
7
|
+
return cn("sprout-card", size === "sm" && "sprout-card-sm", surface === "raised" && "sprout-card-raised", className);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* A bordered panel that groups related content. Compose it with
|
|
11
|
+
* `CardHeader`, `CardContent`, and `CardFooter`.
|
|
12
|
+
*/
|
|
13
|
+
export function Card({ className, size = "default", surface = "default", render, ref, ...props }) {
|
|
14
|
+
return useRender({
|
|
15
|
+
defaultTagName: "div",
|
|
16
|
+
ref,
|
|
17
|
+
render,
|
|
18
|
+
props: mergeProps({ className: cardVariants({ size, surface, className }) }, props),
|
|
19
|
+
state: { slot: "card", size, surface },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function part(slot, base) {
|
|
23
|
+
return function CardPart({ className, render, ref, ...props }) {
|
|
24
|
+
return useRender({
|
|
25
|
+
defaultTagName: "div",
|
|
26
|
+
ref,
|
|
27
|
+
render,
|
|
28
|
+
props: mergeProps({ className: cn(base, className) }, props),
|
|
29
|
+
state: { slot },
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Lays out the title, description, and an optional `CardAction`. */
|
|
34
|
+
export const CardHeader = part("card-header", "sprout-card-header");
|
|
35
|
+
/** A `div` by default. Pass `render={<h3 />}` to put it in the outline. */
|
|
36
|
+
export const CardTitle = part("card-title", "sprout-card-title");
|
|
37
|
+
export const CardDescription = part("card-description", "sprout-card-description");
|
|
38
|
+
/** Pins a badge or compact control to the header's top-right corner. */
|
|
39
|
+
export const CardAction = part("card-action", "sprout-card-action");
|
|
40
|
+
export const CardContent = part("card-content", "sprout-card-content");
|
|
41
|
+
/** A tinted bar along the bottom edge for actions and status. */
|
|
42
|
+
export const CardFooter = part("card-footer", "sprout-card-footer");
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ComponentProps, type RefCallback } from "react";
|
|
2
|
+
import { type ButtonProps } from "./button.js";
|
|
3
|
+
export declare const carouselOrientationNames: readonly ["horizontal", "vertical"];
|
|
4
|
+
export declare const carouselAlignNames: readonly ["start", "center", "end"];
|
|
5
|
+
export type CarouselOrientation = (typeof carouselOrientationNames)[number];
|
|
6
|
+
export type CarouselAlign = (typeof carouselAlignNames)[number];
|
|
7
|
+
export type CarouselEvent = "select" | "reInit";
|
|
8
|
+
export interface CarouselOptions {
|
|
9
|
+
/** Where a slide lands in the viewport when it is the current one. Default `start`. */
|
|
10
|
+
align?: CarouselAlign | undefined;
|
|
11
|
+
/** Scroll freely without snapping to slides. Arrows and keys still step one slide. */
|
|
12
|
+
dragFree?: boolean | undefined;
|
|
13
|
+
/** Next on the last slide goes back to the first, and Previous on the first goes to the last. */
|
|
14
|
+
loop?: boolean | undefined;
|
|
15
|
+
/** The slide to show first, without animation. Default `0`. */
|
|
16
|
+
startIndex?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface CarouselApi {
|
|
19
|
+
scrollPrev(): void;
|
|
20
|
+
scrollNext(): void;
|
|
21
|
+
scrollTo(index: number): void;
|
|
22
|
+
canScrollPrev(): boolean;
|
|
23
|
+
canScrollNext(): boolean;
|
|
24
|
+
selectedScrollSnap(): number;
|
|
25
|
+
/** Scroll offsets, in px, of each stop. Slides that share the end stop share one entry. */
|
|
26
|
+
scrollSnapList(): number[];
|
|
27
|
+
slideNodes(): HTMLElement[];
|
|
28
|
+
on(event: CarouselEvent, listener: (api: CarouselApi) => void): CarouselApi;
|
|
29
|
+
off(event: CarouselEvent, listener: (api: CarouselApi) => void): CarouselApi;
|
|
30
|
+
}
|
|
31
|
+
export interface CarouselProps extends ComponentProps<"div"> {
|
|
32
|
+
opts?: CarouselOptions | undefined;
|
|
33
|
+
orientation?: CarouselOrientation | undefined;
|
|
34
|
+
/** Receives the carousel API once it mounts. */
|
|
35
|
+
setApi?: ((api: CarouselApi) => void) | undefined;
|
|
36
|
+
}
|
|
37
|
+
export interface CarouselContextValue {
|
|
38
|
+
/** Attach to the scrolling viewport. `CarouselContent` does this. */
|
|
39
|
+
carouselRef: RefCallback<HTMLDivElement>;
|
|
40
|
+
api: CarouselApi;
|
|
41
|
+
opts: CarouselOptions | undefined;
|
|
42
|
+
orientation: CarouselOrientation;
|
|
43
|
+
scrollPrev: () => void;
|
|
44
|
+
scrollNext: () => void;
|
|
45
|
+
scrollTo: (index: number) => void;
|
|
46
|
+
canScrollPrev: boolean;
|
|
47
|
+
canScrollNext: boolean;
|
|
48
|
+
selectedIndex: number;
|
|
49
|
+
snapCount: number;
|
|
50
|
+
}
|
|
51
|
+
export declare function useCarousel(): CarouselContextValue;
|
|
52
|
+
export declare function Carousel({ orientation, opts, setApi, className, children, onKeyDownCapture, ...props }: CarouselProps): import("react").JSX.Element;
|
|
53
|
+
export declare function CarouselContent({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
54
|
+
export declare function CarouselItem({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
|
|
55
|
+
export type CarouselButtonProps = Omit<ButtonProps, "state" | "pendingLabel">;
|
|
56
|
+
export declare function CarouselPrevious({ className, variant, size, children, ...props }: CarouselButtonProps): import("react").JSX.Element;
|
|
57
|
+
export declare function CarouselNext({ className, variant, size, children, ...props }: CarouselButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
4
|
+
import { ChevronLeft } from "reicon-react/icons/ChevronLeft";
|
|
5
|
+
import { ChevronRight } from "reicon-react/icons/ChevronRight";
|
|
6
|
+
import { cn } from "../internal/classes.js";
|
|
7
|
+
import { Button } from "./button.js";
|
|
8
|
+
export const carouselOrientationNames = ["horizontal", "vertical"];
|
|
9
|
+
export const carouselAlignNames = ["start", "center", "end"];
|
|
10
|
+
const CarouselContext = createContext(null);
|
|
11
|
+
export function useCarousel() {
|
|
12
|
+
const context = useContext(CarouselContext);
|
|
13
|
+
if (!context)
|
|
14
|
+
throw new Error("useCarousel must be used within a <Carousel />");
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
const typing = 'input, textarea, select, [contenteditable="true"]';
|
|
18
|
+
const focusable = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]';
|
|
19
|
+
function measure(viewport, horizontal, align) {
|
|
20
|
+
const position = horizontal ? viewport.scrollLeft : viewport.scrollTop;
|
|
21
|
+
const view = horizontal ? viewport.clientWidth : viewport.clientHeight;
|
|
22
|
+
const max = Math.max(0, (horizontal ? viewport.scrollWidth : viewport.scrollHeight) - view);
|
|
23
|
+
const frame = viewport.getBoundingClientRect();
|
|
24
|
+
const slides = Array.from(viewport.firstElementChild?.children ?? []);
|
|
25
|
+
const snaps = [];
|
|
26
|
+
for (const slide of slides) {
|
|
27
|
+
const box = slide.getBoundingClientRect();
|
|
28
|
+
const start = (horizontal ? box.left - frame.left : box.top - frame.top) + position;
|
|
29
|
+
const length = horizontal ? box.width : box.height;
|
|
30
|
+
const target = align === "start"
|
|
31
|
+
? start
|
|
32
|
+
: align === "center"
|
|
33
|
+
? start + length / 2 - view / 2
|
|
34
|
+
: start + length - view;
|
|
35
|
+
snaps.push(Math.round(Math.min(max, Math.max(0, target))));
|
|
36
|
+
}
|
|
37
|
+
// Like a contained scroll: the first stop is the start and the last is the end.
|
|
38
|
+
if (snaps.length) {
|
|
39
|
+
snaps[0] = 0;
|
|
40
|
+
snaps[snaps.length - 1] = Math.round(max);
|
|
41
|
+
}
|
|
42
|
+
const unique = snaps.filter((snap, index) => index === 0 || Math.abs(snap - snaps[index - 1]) > 1);
|
|
43
|
+
return { position, max, snaps: unique.length ? unique : [0] };
|
|
44
|
+
}
|
|
45
|
+
function nearest(snaps, position) {
|
|
46
|
+
let best = 0;
|
|
47
|
+
for (let index = 1; index < snaps.length; index += 1)
|
|
48
|
+
if (Math.abs(snaps[index] - position) < Math.abs(snaps[best] - position))
|
|
49
|
+
best = index;
|
|
50
|
+
return best;
|
|
51
|
+
}
|
|
52
|
+
const initialState = {
|
|
53
|
+
canScrollPrev: false,
|
|
54
|
+
canScrollNext: false,
|
|
55
|
+
selectedIndex: 0,
|
|
56
|
+
snapCount: 0,
|
|
57
|
+
viewportFocusable: false,
|
|
58
|
+
};
|
|
59
|
+
export function Carousel({ orientation = "horizontal", opts, setApi, className, children, onKeyDownCapture, ...props }) {
|
|
60
|
+
const horizontal = orientation === "horizontal";
|
|
61
|
+
const align = opts?.align ?? "start";
|
|
62
|
+
const loop = opts?.loop ?? false;
|
|
63
|
+
const viewport = useRef(null);
|
|
64
|
+
const settings = useRef({ horizontal, align, loop });
|
|
65
|
+
settings.current = { horizontal, align, loop };
|
|
66
|
+
const pending = useRef(null);
|
|
67
|
+
const selected = useRef(0);
|
|
68
|
+
const listeners = useRef(new Map());
|
|
69
|
+
const [state, setState] = useState(initialState);
|
|
70
|
+
const geometry = useCallback(() => {
|
|
71
|
+
const node = viewport.current;
|
|
72
|
+
if (!node)
|
|
73
|
+
return null;
|
|
74
|
+
const { horizontal, align } = settings.current;
|
|
75
|
+
return measure(node, horizontal, align);
|
|
76
|
+
}, []);
|
|
77
|
+
const apiRef = useRef(null);
|
|
78
|
+
const emit = useCallback((event) => {
|
|
79
|
+
const api = apiRef.current;
|
|
80
|
+
if (!api)
|
|
81
|
+
return;
|
|
82
|
+
listeners.current.get(event)?.forEach((listener) => listener(api));
|
|
83
|
+
}, []);
|
|
84
|
+
const update = useCallback(() => {
|
|
85
|
+
const node = viewport.current;
|
|
86
|
+
const shape = geometry();
|
|
87
|
+
if (!node || !shape)
|
|
88
|
+
return;
|
|
89
|
+
const { position, max, snaps } = shape;
|
|
90
|
+
const target = pending.current?.target;
|
|
91
|
+
if (target !== undefined && Math.abs(position - target) <= 1) {
|
|
92
|
+
window.clearTimeout(pending.current.timer);
|
|
93
|
+
pending.current = null;
|
|
94
|
+
}
|
|
95
|
+
const wraps = settings.current.loop && snaps.length > 1;
|
|
96
|
+
const next = {
|
|
97
|
+
canScrollPrev: wraps || position > 1,
|
|
98
|
+
canScrollNext: wraps || position < max - 1,
|
|
99
|
+
selectedIndex: nearest(snaps, position),
|
|
100
|
+
snapCount: snaps.length,
|
|
101
|
+
viewportFocusable: max > 0 && !node.firstElementChild?.querySelector(focusable),
|
|
102
|
+
};
|
|
103
|
+
const changed = next.selectedIndex !== selected.current;
|
|
104
|
+
selected.current = next.selectedIndex;
|
|
105
|
+
setState((current) => current.canScrollPrev === next.canScrollPrev &&
|
|
106
|
+
current.canScrollNext === next.canScrollNext &&
|
|
107
|
+
current.selectedIndex === next.selectedIndex &&
|
|
108
|
+
current.snapCount === next.snapCount &&
|
|
109
|
+
current.viewportFocusable === next.viewportFocusable
|
|
110
|
+
? current
|
|
111
|
+
: next);
|
|
112
|
+
if (changed)
|
|
113
|
+
emit("select");
|
|
114
|
+
}, [emit, geometry]);
|
|
115
|
+
const go = useCallback((target, instant = false) => {
|
|
116
|
+
const node = viewport.current;
|
|
117
|
+
if (!node)
|
|
118
|
+
return;
|
|
119
|
+
if (pending.current)
|
|
120
|
+
window.clearTimeout(pending.current.timer);
|
|
121
|
+
pending.current = {
|
|
122
|
+
target,
|
|
123
|
+
timer: window.setTimeout(() => {
|
|
124
|
+
pending.current = null;
|
|
125
|
+
update();
|
|
126
|
+
}, 700),
|
|
127
|
+
};
|
|
128
|
+
const key = settings.current.horizontal ? "left" : "top";
|
|
129
|
+
node.scrollTo({
|
|
130
|
+
[key]: target,
|
|
131
|
+
...(instant ? { behavior: "instant" } : {}),
|
|
132
|
+
});
|
|
133
|
+
update();
|
|
134
|
+
}, [update]);
|
|
135
|
+
const scrollTo = useCallback((index, instant = false) => {
|
|
136
|
+
const shape = geometry();
|
|
137
|
+
if (!shape)
|
|
138
|
+
return;
|
|
139
|
+
const clamped = Math.min(shape.snaps.length - 1, Math.max(0, index));
|
|
140
|
+
go(shape.snaps[clamped], instant);
|
|
141
|
+
}, [geometry, go]);
|
|
142
|
+
const step = useCallback((direction) => {
|
|
143
|
+
const shape = geometry();
|
|
144
|
+
if (!shape)
|
|
145
|
+
return;
|
|
146
|
+
const from = pending.current?.target ?? shape.position;
|
|
147
|
+
const target = direction > 0
|
|
148
|
+
? shape.snaps.find((snap) => snap > from + 1)
|
|
149
|
+
: [...shape.snaps].reverse().find((snap) => snap < from - 1);
|
|
150
|
+
if (target !== undefined)
|
|
151
|
+
go(target);
|
|
152
|
+
else if (settings.current.loop && shape.snaps.length > 1)
|
|
153
|
+
go(direction > 0 ? 0 : shape.max);
|
|
154
|
+
}, [geometry, go]);
|
|
155
|
+
const scrollPrev = useCallback(() => step(-1), [step]);
|
|
156
|
+
const scrollNext = useCallback(() => step(1), [step]);
|
|
157
|
+
if (!apiRef.current) {
|
|
158
|
+
const api = {
|
|
159
|
+
scrollPrev: () => step(-1),
|
|
160
|
+
scrollNext: () => step(1),
|
|
161
|
+
scrollTo: (index) => scrollTo(index),
|
|
162
|
+
canScrollPrev: () => {
|
|
163
|
+
const shape = geometry();
|
|
164
|
+
if (!shape)
|
|
165
|
+
return false;
|
|
166
|
+
return ((settings.current.loop && shape.snaps.length > 1) ||
|
|
167
|
+
shape.position > 1);
|
|
168
|
+
},
|
|
169
|
+
canScrollNext: () => {
|
|
170
|
+
const shape = geometry();
|
|
171
|
+
if (!shape)
|
|
172
|
+
return false;
|
|
173
|
+
return ((settings.current.loop && shape.snaps.length > 1) ||
|
|
174
|
+
shape.position < shape.max - 1);
|
|
175
|
+
},
|
|
176
|
+
selectedScrollSnap: () => selected.current,
|
|
177
|
+
scrollSnapList: () => geometry()?.snaps ?? [],
|
|
178
|
+
slideNodes: () => Array.from(viewport.current?.firstElementChild?.children ?? []),
|
|
179
|
+
on: (event, listener) => {
|
|
180
|
+
const set = listeners.current.get(event) ?? new Set();
|
|
181
|
+
set.add(listener);
|
|
182
|
+
listeners.current.set(event, set);
|
|
183
|
+
return api;
|
|
184
|
+
},
|
|
185
|
+
off: (event, listener) => {
|
|
186
|
+
listeners.current.get(event)?.delete(listener);
|
|
187
|
+
return api;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
apiRef.current = api;
|
|
191
|
+
}
|
|
192
|
+
const api = apiRef.current;
|
|
193
|
+
const frame = useRef(0);
|
|
194
|
+
const scheduleUpdate = useCallback(() => {
|
|
195
|
+
if (frame.current)
|
|
196
|
+
return;
|
|
197
|
+
frame.current = requestAnimationFrame(() => {
|
|
198
|
+
frame.current = 0;
|
|
199
|
+
update();
|
|
200
|
+
});
|
|
201
|
+
}, [update]);
|
|
202
|
+
const startIndex = opts?.startIndex ?? 0;
|
|
203
|
+
const started = useRef(false);
|
|
204
|
+
const [node, setNode] = useState(null);
|
|
205
|
+
const carouselRef = useCallback((element) => {
|
|
206
|
+
viewport.current = element;
|
|
207
|
+
setNode(element);
|
|
208
|
+
}, []);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
if (!node)
|
|
211
|
+
return;
|
|
212
|
+
const reInit = () => {
|
|
213
|
+
update();
|
|
214
|
+
emit("reInit");
|
|
215
|
+
};
|
|
216
|
+
if (!started.current) {
|
|
217
|
+
started.current = true;
|
|
218
|
+
if (startIndex > 0)
|
|
219
|
+
scrollTo(startIndex, true);
|
|
220
|
+
}
|
|
221
|
+
update();
|
|
222
|
+
const resize = new ResizeObserver(reInit);
|
|
223
|
+
resize.observe(node);
|
|
224
|
+
const track = node.firstElementChild;
|
|
225
|
+
if (track)
|
|
226
|
+
resize.observe(track);
|
|
227
|
+
const mutation = new MutationObserver(() => {
|
|
228
|
+
if (track)
|
|
229
|
+
for (const slide of track.children)
|
|
230
|
+
resize.observe(slide);
|
|
231
|
+
reInit();
|
|
232
|
+
});
|
|
233
|
+
if (track) {
|
|
234
|
+
for (const slide of track.children)
|
|
235
|
+
resize.observe(slide);
|
|
236
|
+
mutation.observe(track, { childList: true, subtree: true });
|
|
237
|
+
}
|
|
238
|
+
node.addEventListener("scroll", scheduleUpdate, { passive: true });
|
|
239
|
+
node.addEventListener("scrollend", update);
|
|
240
|
+
return () => {
|
|
241
|
+
resize.disconnect();
|
|
242
|
+
mutation.disconnect();
|
|
243
|
+
node.removeEventListener("scroll", scheduleUpdate);
|
|
244
|
+
node.removeEventListener("scrollend", update);
|
|
245
|
+
cancelAnimationFrame(frame.current);
|
|
246
|
+
frame.current = 0;
|
|
247
|
+
};
|
|
248
|
+
}, [node, emit, scheduleUpdate, scrollTo, startIndex, update]);
|
|
249
|
+
// Orientation, alignment, and looping change the stops.
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (!node)
|
|
252
|
+
return;
|
|
253
|
+
update();
|
|
254
|
+
emit("reInit");
|
|
255
|
+
}, [node, horizontal, align, loop, update, emit]);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (!setApi)
|
|
258
|
+
return;
|
|
259
|
+
setApi(api);
|
|
260
|
+
}, [api, setApi]);
|
|
261
|
+
useEffect(() => () => {
|
|
262
|
+
if (pending.current)
|
|
263
|
+
window.clearTimeout(pending.current.timer);
|
|
264
|
+
}, []);
|
|
265
|
+
const handleKeyDown = (event) => {
|
|
266
|
+
onKeyDownCapture?.(event);
|
|
267
|
+
if (event.defaultPrevented ||
|
|
268
|
+
event.altKey ||
|
|
269
|
+
event.ctrlKey ||
|
|
270
|
+
event.metaKey)
|
|
271
|
+
return;
|
|
272
|
+
if (event.target.closest(typing))
|
|
273
|
+
return;
|
|
274
|
+
const back = event.key === "ArrowLeft" || (!horizontal && event.key === "ArrowUp");
|
|
275
|
+
const forward = event.key === "ArrowRight" || (!horizontal && event.key === "ArrowDown");
|
|
276
|
+
if (back || forward) {
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
step(back ? -1 : 1);
|
|
279
|
+
}
|
|
280
|
+
else if (event.key === "Home" || event.key === "End") {
|
|
281
|
+
if (!event.target.closest('[data-slot="carousel-content"]'))
|
|
282
|
+
return;
|
|
283
|
+
event.preventDefault();
|
|
284
|
+
scrollTo(event.key === "Home" ? 0 : Number.MAX_SAFE_INTEGER);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const value = useMemo(() => ({
|
|
288
|
+
carouselRef,
|
|
289
|
+
api,
|
|
290
|
+
opts,
|
|
291
|
+
orientation,
|
|
292
|
+
scrollPrev,
|
|
293
|
+
scrollNext,
|
|
294
|
+
scrollTo: (index) => scrollTo(index),
|
|
295
|
+
canScrollPrev: state.canScrollPrev,
|
|
296
|
+
canScrollNext: state.canScrollNext,
|
|
297
|
+
selectedIndex: state.selectedIndex,
|
|
298
|
+
snapCount: state.snapCount,
|
|
299
|
+
}), [
|
|
300
|
+
carouselRef,
|
|
301
|
+
api,
|
|
302
|
+
opts,
|
|
303
|
+
orientation,
|
|
304
|
+
scrollPrev,
|
|
305
|
+
scrollNext,
|
|
306
|
+
scrollTo,
|
|
307
|
+
state,
|
|
308
|
+
]);
|
|
309
|
+
return (_jsx(CarouselContext.Provider, { value: value, children: _jsx(ViewportFocus.Provider, { value: state.viewportFocusable, children: _jsx("div", { role: "region", "aria-roledescription": "carousel", "data-slot": "carousel", "data-orientation": orientation, className: cn("sprout-carousel", className), onKeyDownCapture: handleKeyDown, ...props, children: children }) }) }));
|
|
310
|
+
}
|
|
311
|
+
const ViewportFocus = createContext(false);
|
|
312
|
+
export function CarouselContent({ className, ...props }) {
|
|
313
|
+
const { carouselRef, orientation, opts, api } = useCarousel();
|
|
314
|
+
const focusableViewport = useContext(ViewportFocus);
|
|
315
|
+
const horizontal = orientation === "horizontal";
|
|
316
|
+
const drag = useRef(null);
|
|
317
|
+
const node = useRef(null);
|
|
318
|
+
const ref = useCallback((element) => {
|
|
319
|
+
node.current = element;
|
|
320
|
+
carouselRef(element);
|
|
321
|
+
}, [carouselRef]);
|
|
322
|
+
const coordinate = (event) => horizontal ? event.clientX : event.clientY;
|
|
323
|
+
const onPointerDown = (event) => {
|
|
324
|
+
if (event.pointerType !== "mouse" || event.button !== 0)
|
|
325
|
+
return;
|
|
326
|
+
if (event.target.closest(typing))
|
|
327
|
+
return;
|
|
328
|
+
const viewport = node.current;
|
|
329
|
+
drag.current = {
|
|
330
|
+
id: event.pointerId,
|
|
331
|
+
start: coordinate(event),
|
|
332
|
+
scroll: horizontal ? viewport.scrollLeft : viewport.scrollTop,
|
|
333
|
+
moved: false,
|
|
334
|
+
};
|
|
335
|
+
};
|
|
336
|
+
const onPointerMove = (event) => {
|
|
337
|
+
const current = drag.current;
|
|
338
|
+
if (!current || current.id !== event.pointerId)
|
|
339
|
+
return;
|
|
340
|
+
const delta = coordinate(event) - current.start;
|
|
341
|
+
if (!current.moved) {
|
|
342
|
+
if (Math.abs(delta) < 5)
|
|
343
|
+
return;
|
|
344
|
+
current.moved = true;
|
|
345
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
346
|
+
event.currentTarget.setAttribute("data-dragging", "");
|
|
347
|
+
}
|
|
348
|
+
const viewport = event.currentTarget;
|
|
349
|
+
if (horizontal)
|
|
350
|
+
viewport.scrollLeft = current.scroll - delta;
|
|
351
|
+
else
|
|
352
|
+
viewport.scrollTop = current.scroll - delta;
|
|
353
|
+
};
|
|
354
|
+
const onPointerEnd = (event) => {
|
|
355
|
+
const current = drag.current;
|
|
356
|
+
if (!current || current.id !== event.pointerId)
|
|
357
|
+
return;
|
|
358
|
+
drag.current = null;
|
|
359
|
+
if (!current.moved)
|
|
360
|
+
return;
|
|
361
|
+
const viewport = event.currentTarget;
|
|
362
|
+
if (viewport.hasPointerCapture(event.pointerId))
|
|
363
|
+
viewport.releasePointerCapture(event.pointerId);
|
|
364
|
+
const swallow = (click) => {
|
|
365
|
+
click.preventDefault();
|
|
366
|
+
click.stopPropagation();
|
|
367
|
+
};
|
|
368
|
+
window.addEventListener("click", swallow, { capture: true, once: true });
|
|
369
|
+
window.setTimeout(() => window.removeEventListener("click", swallow, { capture: true }), 0);
|
|
370
|
+
const release = () => {
|
|
371
|
+
if (!drag.current)
|
|
372
|
+
viewport.removeAttribute("data-dragging");
|
|
373
|
+
};
|
|
374
|
+
if (opts?.dragFree)
|
|
375
|
+
return release();
|
|
376
|
+
const position = horizontal ? viewport.scrollLeft : viewport.scrollTop;
|
|
377
|
+
const view = horizontal ? viewport.clientWidth : viewport.clientHeight;
|
|
378
|
+
const moved = position - current.scroll;
|
|
379
|
+
const snaps = api.scrollSnapList();
|
|
380
|
+
const from = nearest(snaps, current.scroll);
|
|
381
|
+
// A short flick still moves one slide; a long drag lands on the nearest.
|
|
382
|
+
const index = Math.abs(moved) > view * 0.5
|
|
383
|
+
? nearest(snaps, position)
|
|
384
|
+
: Math.abs(moved) > Math.min(40, view * 0.15)
|
|
385
|
+
? from + Math.sign(moved)
|
|
386
|
+
: from;
|
|
387
|
+
const target = snaps[Math.min(snaps.length - 1, Math.max(0, index))];
|
|
388
|
+
if (target === undefined || Math.abs(target - position) < 1)
|
|
389
|
+
return release();
|
|
390
|
+
// Snapping stays off until the settle scroll ends; turning it back on
|
|
391
|
+
// earlier makes the browser pull the viewport to the closest stop instead.
|
|
392
|
+
let timer = 0;
|
|
393
|
+
const settle = () => {
|
|
394
|
+
window.clearTimeout(timer);
|
|
395
|
+
viewport.removeEventListener("scrollend", settle);
|
|
396
|
+
release();
|
|
397
|
+
};
|
|
398
|
+
viewport.addEventListener("scrollend", settle);
|
|
399
|
+
timer = window.setTimeout(settle, 700);
|
|
400
|
+
const smooth = window.matchMedia("(prefers-reduced-motion: no-preference)").matches;
|
|
401
|
+
viewport.scrollTo({
|
|
402
|
+
[horizontal ? "left" : "top"]: target,
|
|
403
|
+
behavior: smooth ? "smooth" : "instant",
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
return (_jsx("div", { ref: ref, "data-slot": "carousel-content", "data-orientation": orientation, "data-align": opts?.align ?? "start", "data-drag-free": opts?.dragFree ? "" : undefined, tabIndex: focusableViewport ? 0 : undefined, className: "sprout-carousel-content", onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerEnd, onPointerCancel: onPointerEnd, onDragStart: (event) => {
|
|
407
|
+
if (drag.current)
|
|
408
|
+
event.preventDefault();
|
|
409
|
+
}, children: _jsx("div", { "data-slot": "carousel-track", className: cn("sprout-carousel-track", className), ...props }) }));
|
|
410
|
+
}
|
|
411
|
+
export function CarouselItem({ className, ...props }) {
|
|
412
|
+
const { orientation } = useCarousel();
|
|
413
|
+
return (_jsx("div", { role: "group", "aria-roledescription": "slide", "data-slot": "carousel-item", "data-orientation": orientation, className: cn("sprout-carousel-item", className), ...props }));
|
|
414
|
+
}
|
|
415
|
+
export function CarouselPrevious({ className, variant = "secondary", size = "icon", children, ...props }) {
|
|
416
|
+
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
417
|
+
return (_jsx(Button, { "data-slot": "carousel-previous", "data-orientation": orientation, variant: variant, size: size, "aria-label": "Previous slide", focusableWhenDisabled: true, className: cn("sprout-carousel-control", "sprout-carousel-previous", className), disabled: !canScrollPrev, onClick: scrollPrev, ...props, children: children ?? _jsx(ChevronLeft, { "aria-hidden": "true" }) }));
|
|
418
|
+
}
|
|
419
|
+
export function CarouselNext({ className, variant = "secondary", size = "icon", children, ...props }) {
|
|
420
|
+
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
421
|
+
return (_jsx(Button, { "data-slot": "carousel-next", "data-orientation": orientation, variant: variant, size: size, "aria-label": "Next slide", focusableWhenDisabled: true, className: cn("sprout-carousel-control", "sprout-carousel-next", className), disabled: !canScrollNext, onClick: scrollNext, ...props, children: children ?? _jsx(ChevronRight, { "aria-hidden": "true" }) }));
|
|
422
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { type ComponentProps, type ComponentType, type ReactNode } from "react";
|
|
2
|
+
import { ResponsiveContainer, Tooltip, type DefaultLegendContentProps, type DefaultTooltipContentProps, type TooltipValueType } from "recharts";
|
|
3
|
+
/** Theme names a `ChartConfig` entry can color, and the selector each one uses. */
|
|
4
|
+
declare const THEMES: {
|
|
5
|
+
readonly light: "";
|
|
6
|
+
readonly dark: ".dark";
|
|
7
|
+
};
|
|
8
|
+
export declare const chartIndicatorNames: readonly ["dot", "line", "dashed"];
|
|
9
|
+
export type ChartIndicator = (typeof chartIndicatorNames)[number];
|
|
10
|
+
export type ChartConfig = Record<string, {
|
|
11
|
+
label?: ReactNode;
|
|
12
|
+
icon?: ComponentType;
|
|
13
|
+
} & ({
|
|
14
|
+
color?: string;
|
|
15
|
+
theme?: never;
|
|
16
|
+
} | {
|
|
17
|
+
color?: never;
|
|
18
|
+
theme: Record<keyof typeof THEMES, string>;
|
|
19
|
+
})>;
|
|
20
|
+
/**
|
|
21
|
+
* The CSS variable a `ChartContainer` sets for one config key. Pass it as a
|
|
22
|
+
* series `fill` or `stroke`, for example `fill={chartColor("tasks")}`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function chartColor(key: string): string;
|
|
25
|
+
export type ChartContainerProps = ComponentProps<"div"> & {
|
|
26
|
+
config: ChartConfig;
|
|
27
|
+
children: ComponentProps<typeof ResponsiveContainer>["children"];
|
|
28
|
+
/** Size used before the container is measured, and on the server. */
|
|
29
|
+
initialDimension?: {
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Sizes a Recharts chart to its box and turns `config` into series colors
|
|
36
|
+
* and labels for the tooltip and legend. The box is 16:9 unless `className`
|
|
37
|
+
* or `style` gives it a height.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ChartContainer({ id, className, children, config, initialDimension, ...props }: ChartContainerProps): import("react").JSX.Element;
|
|
40
|
+
/**
|
|
41
|
+
* Writes `--sprout-chart-color-<key>` for every colored config entry, with a
|
|
42
|
+
* dark value for entries that pass `theme`. `ChartContainer` renders it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ChartStyle({ id, config, }: {
|
|
45
|
+
id: string;
|
|
46
|
+
config: ChartConfig;
|
|
47
|
+
}): import("react").JSX.Element | null;
|
|
48
|
+
export type ChartTooltipProps = ComponentProps<typeof Tooltip>;
|
|
49
|
+
/**
|
|
50
|
+
* Recharts' `Tooltip`, moving in 160ms. Pass `content={<ChartTooltipContent />}`.
|
|
51
|
+
* It stays still when reduced motion is on.
|
|
52
|
+
*/
|
|
53
|
+
export declare function ChartTooltip(props: ChartTooltipProps): import("react").JSX.Element;
|
|
54
|
+
type TooltipNameType = number | string;
|
|
55
|
+
export type ChartTooltipContentProps = ComponentProps<typeof Tooltip> & ComponentProps<"div"> & {
|
|
56
|
+
hideLabel?: boolean;
|
|
57
|
+
hideIndicator?: boolean;
|
|
58
|
+
indicator?: ChartIndicator;
|
|
59
|
+
nameKey?: string;
|
|
60
|
+
labelKey?: string;
|
|
61
|
+
} & Omit<DefaultTooltipContentProps<TooltipValueType, TooltipNameType>, "accessibilityLayer">;
|
|
62
|
+
/**
|
|
63
|
+
* The tooltip card: a label, then one row per series with its indicator,
|
|
64
|
+
* config label, and value. Recharts passes `active`, `payload`, and `label`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function ChartTooltipContent({ active, payload, className, indicator, hideLabel, hideIndicator, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey, }: ChartTooltipContentProps): import("react").JSX.Element | null;
|
|
67
|
+
/** Recharts' `Legend`. Pass `content={<ChartLegendContent />}`. */
|
|
68
|
+
export declare const ChartLegend: import("react").MemoExoticComponent<(outsideProps: import("recharts").LegendProps) => React.ReactPortal | null>;
|
|
69
|
+
export type ChartLegendContentProps = ComponentProps<"div"> & {
|
|
70
|
+
hideIcon?: boolean;
|
|
71
|
+
nameKey?: string;
|
|
72
|
+
} & DefaultLegendContentProps;
|
|
73
|
+
/** A centered row of swatches and config labels, one per series. */
|
|
74
|
+
export declare function ChartLegendContent({ className, hideIcon, payload, verticalAlign, nameKey, }: ChartLegendContentProps): import("react").JSX.Element | null;
|
|
75
|
+
/** One bar series: its data key, legend and tooltip label, and color. */
|
|
76
|
+
export type UsageSeries = {
|
|
77
|
+
key: string;
|
|
78
|
+
label: string;
|
|
79
|
+
color: string;
|
|
80
|
+
};
|
|
81
|
+
export type UsageDay = {
|
|
82
|
+
date: string;
|
|
83
|
+
} & Record<string, number | string | null>;
|
|
84
|
+
export interface UsageChartProps {
|
|
85
|
+
/** One row per UTC day (`YYYY-MM-DD`), keyed by series. */
|
|
86
|
+
days: UsageDay[];
|
|
87
|
+
series: UsageSeries[];
|
|
88
|
+
/** Names the chart for screen readers. */
|
|
89
|
+
title?: string;
|
|
90
|
+
/** Locale for day labels. Defaults to the reader's. */
|
|
91
|
+
locale?: string;
|
|
92
|
+
className?: string;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stacked daily bars, 256px tall. Days are rows keyed by series; a missing or
|
|
96
|
+
* null value keeps that series out of the day's tooltip. The top series gets
|
|
97
|
+
* rounded corners.
|
|
98
|
+
*/
|
|
99
|
+
export declare function UsageChart({ days, series, title, locale, className, }: UsageChartProps): import("react").JSX.Element;
|
|
100
|
+
export interface CategoryChartProps {
|
|
101
|
+
/** One row per category, keyed by `category` and by each series key. */
|
|
102
|
+
rows: Record<string, number | string>[];
|
|
103
|
+
/** The row key whose value names each group of bars. */
|
|
104
|
+
category: string;
|
|
105
|
+
series: UsageSeries[];
|
|
106
|
+
/** Names the chart for screen readers. */
|
|
107
|
+
title?: string;
|
|
108
|
+
className?: string;
|
|
109
|
+
}
|
|
110
|
+
/** Side-by-side bars per category with a legend in series order, 288px tall. */
|
|
111
|
+
export declare function CategoryChart({ rows, category, series, title, className, }: CategoryChartProps): import("react").JSX.Element;
|
|
112
|
+
export {};
|