sunpeak 0.2.6 → 0.3.2

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 (79) hide show
  1. package/README.md +32 -17
  2. package/dist/chatgpt/chatgpt-simulator-types.d.ts +8 -0
  3. package/dist/chatgpt/chatgpt-simulator.d.ts +11 -0
  4. package/dist/chatgpt/conversation.d.ts +11 -0
  5. package/dist/chatgpt/index.d.ts +3 -0
  6. package/dist/chatgpt/mcp-provider.d.ts +25 -0
  7. package/dist/chatgpt/mock-openai.d.ts +61 -0
  8. package/dist/chatgpt/openai-provider.d.ts +19 -0
  9. package/dist/chatgpt/openai-types.d.ts +81 -0
  10. package/dist/chatgpt/simple-sidebar.d.ts +22 -0
  11. package/dist/chatgpt/theme-provider.d.ts +13 -0
  12. package/dist/hooks/index.d.ts +14 -0
  13. package/dist/hooks/use-display-mode.d.ts +2 -0
  14. package/dist/hooks/use-locale.d.ts +1 -0
  15. package/dist/hooks/use-max-height.d.ts +1 -0
  16. package/dist/hooks/use-mobile.d.ts +1 -0
  17. package/dist/hooks/use-safe-area.d.ts +2 -0
  18. package/dist/hooks/use-theme.d.ts +2 -0
  19. package/dist/hooks/use-tool-input.d.ts +2 -0
  20. package/dist/hooks/use-tool-response-metadata.d.ts +2 -0
  21. package/dist/hooks/use-user-agent.d.ts +2 -0
  22. package/dist/hooks/use-view.d.ts +2 -0
  23. package/dist/hooks/use-widget-api.d.ts +8 -0
  24. package/dist/hooks/use-widget-global.d.ts +9 -0
  25. package/dist/hooks/use-widget-props.d.ts +1 -0
  26. package/dist/hooks/use-widget-state.d.ts +4 -0
  27. package/dist/index.cjs +3310 -666
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +5 -366
  30. package/dist/index.js +3325 -640
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/index.d.ts +2 -0
  33. package/dist/lib/media-queries.d.ts +3 -0
  34. package/dist/lib/utils.d.ts +2 -0
  35. package/dist/mcp/index.cjs +799 -64
  36. package/dist/mcp/index.cjs.map +1 -1
  37. package/dist/mcp/index.d.ts +1 -12
  38. package/dist/mcp/index.js +786 -44
  39. package/dist/mcp/index.js.map +1 -1
  40. package/dist/mcp/server.d.ts +10 -0
  41. package/dist/mcp/types.d.ts +74 -0
  42. package/dist/providers/index.d.ts +40 -0
  43. package/dist/providers/types.d.ts +71 -0
  44. package/dist/style.css +5014 -0
  45. package/dist/test/setup.d.ts +0 -0
  46. package/dist/types/index.d.ts +2 -0
  47. package/package.json +11 -19
  48. package/template/README.md +3 -6
  49. package/template/dev/main.tsx +0 -1
  50. package/template/mcp/server.ts +1 -1
  51. package/template/package.json +4 -14
  52. package/template/src/App.tsx +7 -8
  53. package/template/src/components/index.ts +2 -2
  54. package/template/src/components/openai-card.test.tsx +73 -0
  55. package/template/src/components/openai-card.tsx +126 -0
  56. package/template/src/components/openai-carousel.test.tsx +84 -0
  57. package/template/src/components/openai-carousel.tsx +178 -0
  58. package/template/src/styles/globals.css +5 -216
  59. package/template/vite.config.build.ts +61 -0
  60. package/template/vite.config.ts +0 -2
  61. package/dist/index.d.cts +0 -366
  62. package/dist/mcp/index.d.cts +0 -12
  63. package/dist/styles/chatgpt/index.css +0 -146
  64. package/dist/styles/globals.css +0 -219
  65. package/template/components.json +0 -21
  66. package/template/dev/styles.css +0 -6
  67. package/template/postcss.config.js +0 -5
  68. package/template/src/components/shadcn/button.tsx +0 -60
  69. package/template/src/components/shadcn/card.tsx +0 -76
  70. package/template/src/components/shadcn/carousel.tsx +0 -260
  71. package/template/src/components/shadcn/index.ts +0 -5
  72. package/template/src/components/shadcn/label.tsx +0 -24
  73. package/template/src/components/shadcn/select.tsx +0 -157
  74. package/template/src/components/sunpeak-card.test.tsx +0 -76
  75. package/template/src/components/sunpeak-card.tsx +0 -171
  76. package/template/src/components/sunpeak-carousel.test.tsx +0 -42
  77. package/template/src/components/sunpeak-carousel.tsx +0 -160
  78. package/template/src/styles/chatgpt.css +0 -146
  79. package/template/tsup.config.ts +0 -50
@@ -1,157 +0,0 @@
1
- import * as React from "react"
2
- import * as SelectPrimitive from "@radix-ui/react-select"
3
- import { Check, ChevronDown, ChevronUp } from "lucide-react"
4
-
5
- import { cn } from "@/lib/index"
6
-
7
- const Select = SelectPrimitive.Root
8
-
9
- const SelectGroup = SelectPrimitive.Group
10
-
11
- const SelectValue = SelectPrimitive.Value
12
-
13
- const SelectTrigger = React.forwardRef<
14
- React.ElementRef<typeof SelectPrimitive.Trigger>,
15
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
16
- >(({ className, children, ...props }, ref) => (
17
- <SelectPrimitive.Trigger
18
- ref={ref}
19
- className={cn(
20
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
21
- className
22
- )}
23
- {...props}
24
- >
25
- {children}
26
- <SelectPrimitive.Icon asChild>
27
- <ChevronDown className="h-4 w-4 opacity-50" />
28
- </SelectPrimitive.Icon>
29
- </SelectPrimitive.Trigger>
30
- ))
31
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
32
-
33
- const SelectScrollUpButton = React.forwardRef<
34
- React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
35
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
36
- >(({ className, ...props }, ref) => (
37
- <SelectPrimitive.ScrollUpButton
38
- ref={ref}
39
- className={cn(
40
- "flex cursor-default items-center justify-center py-1",
41
- className
42
- )}
43
- {...props}
44
- >
45
- <ChevronUp className="h-4 w-4" />
46
- </SelectPrimitive.ScrollUpButton>
47
- ))
48
- SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
49
-
50
- const SelectScrollDownButton = React.forwardRef<
51
- React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
52
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
53
- >(({ className, ...props }, ref) => (
54
- <SelectPrimitive.ScrollDownButton
55
- ref={ref}
56
- className={cn(
57
- "flex cursor-default items-center justify-center py-1",
58
- className
59
- )}
60
- {...props}
61
- >
62
- <ChevronDown className="h-4 w-4" />
63
- </SelectPrimitive.ScrollDownButton>
64
- ))
65
- SelectScrollDownButton.displayName =
66
- SelectPrimitive.ScrollDownButton.displayName
67
-
68
- const SelectContent = React.forwardRef<
69
- React.ElementRef<typeof SelectPrimitive.Content>,
70
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
71
- >(({ className, children, position = "popper", ...props }, ref) => (
72
- <SelectPrimitive.Portal>
73
- <SelectPrimitive.Content
74
- ref={ref}
75
- className={cn(
76
- "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
77
- position === "popper" &&
78
- "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
79
- className
80
- )}
81
- position={position}
82
- {...props}
83
- >
84
- <SelectScrollUpButton />
85
- <SelectPrimitive.Viewport
86
- className={cn(
87
- "p-1",
88
- position === "popper" &&
89
- "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
90
- )}
91
- >
92
- {children}
93
- </SelectPrimitive.Viewport>
94
- <SelectScrollDownButton />
95
- </SelectPrimitive.Content>
96
- </SelectPrimitive.Portal>
97
- ))
98
- SelectContent.displayName = SelectPrimitive.Content.displayName
99
-
100
- const SelectLabel = React.forwardRef<
101
- React.ElementRef<typeof SelectPrimitive.Label>,
102
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
103
- >(({ className, ...props }, ref) => (
104
- <SelectPrimitive.Label
105
- ref={ref}
106
- className={cn("px-2 py-1.5 text-sm font-semibold", className)}
107
- {...props}
108
- />
109
- ))
110
- SelectLabel.displayName = SelectPrimitive.Label.displayName
111
-
112
- const SelectItem = React.forwardRef<
113
- React.ElementRef<typeof SelectPrimitive.Item>,
114
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
115
- >(({ className, children, ...props }, ref) => (
116
- <SelectPrimitive.Item
117
- ref={ref}
118
- className={cn(
119
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
120
- className
121
- )}
122
- {...props}
123
- >
124
- <span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
125
- <SelectPrimitive.ItemIndicator>
126
- <Check className="h-4 w-4" />
127
- </SelectPrimitive.ItemIndicator>
128
- </span>
129
- <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
130
- </SelectPrimitive.Item>
131
- ))
132
- SelectItem.displayName = SelectPrimitive.Item.displayName
133
-
134
- const SelectSeparator = React.forwardRef<
135
- React.ElementRef<typeof SelectPrimitive.Separator>,
136
- React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
137
- >(({ className, ...props }, ref) => (
138
- <SelectPrimitive.Separator
139
- ref={ref}
140
- className={cn("-mx-1 my-1 h-px bg-muted", className)}
141
- {...props}
142
- />
143
- ))
144
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName
145
-
146
- export {
147
- Select,
148
- SelectGroup,
149
- SelectValue,
150
- SelectTrigger,
151
- SelectContent,
152
- SelectLabel,
153
- SelectItem,
154
- SelectSeparator,
155
- SelectScrollUpButton,
156
- SelectScrollDownButton,
157
- }
@@ -1,76 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import userEvent from '@testing-library/user-event';
3
- import { describe, it, expect, vi } from 'vitest';
4
- import { SunpeakCard } from './sunpeak-card';
5
-
6
- describe('SunpeakCard', () => {
7
- it('renders image, header, metadata, and content', () => {
8
- render(
9
- <SunpeakCard
10
- image="https://example.com/image.jpg"
11
- imageAlt="Test image"
12
- header="Test Header"
13
- metadata="Test metadata"
14
- >
15
- Card content here
16
- </SunpeakCard>
17
- );
18
-
19
- expect(screen.getByRole('img')).toHaveAttribute('src', 'https://example.com/image.jpg');
20
- expect(screen.getByRole('img')).toHaveAttribute('alt', 'Test image');
21
- expect(screen.getByText('Test Header')).toBeInTheDocument();
22
- expect(screen.getByText('Test metadata')).toBeInTheDocument();
23
- expect(screen.getByText('Card content here')).toBeInTheDocument();
24
- });
25
-
26
- it('applies variant styles correctly', () => {
27
- const { rerender } = render(
28
- <SunpeakCard
29
- image="https://example.com/image.jpg"
30
- imageAlt="Test"
31
- variant="bordered"
32
- data-testid="card"
33
- />
34
- );
35
-
36
- expect(screen.getByTestId('card')).toHaveClass('border-2');
37
-
38
- rerender(
39
- <SunpeakCard
40
- image="https://example.com/image.jpg"
41
- imageAlt="Test"
42
- variant="elevated"
43
- data-testid="card"
44
- />
45
- );
46
-
47
- expect(screen.getByTestId('card')).toHaveClass('shadow-lg');
48
- });
49
-
50
- it('renders buttons and handles clicks without propagating to card', async () => {
51
- const user = userEvent.setup();
52
- const cardClick = vi.fn();
53
- const buttonClick = vi.fn();
54
-
55
- render(
56
- <SunpeakCard
57
- image="https://example.com/image.jpg"
58
- imageAlt="Test"
59
- onClick={cardClick}
60
- button1={{ children: 'Primary', onClick: buttonClick, isPrimary: true }}
61
- button2={{ children: 'Secondary', onClick: buttonClick }}
62
- />
63
- );
64
-
65
- const primaryButton = screen.getByRole('button', { name: 'Primary' });
66
- const secondaryButton = screen.getByRole('button', { name: 'Secondary' });
67
-
68
- expect(primaryButton).toBeInTheDocument();
69
- expect(secondaryButton).toBeInTheDocument();
70
-
71
- await user.click(primaryButton);
72
-
73
- expect(buttonClick).toHaveBeenCalledTimes(1);
74
- expect(cardClick).not.toHaveBeenCalled();
75
- });
76
- });
@@ -1,171 +0,0 @@
1
- import * as React from "react"
2
- import { useWidgetState } from "sunpeak"
3
- import { cn } from "@/lib/index"
4
- import {
5
- Card,
6
- CardContent,
7
- CardDescription,
8
- CardFooter,
9
- CardHeader,
10
- CardTitle,
11
- } from "@/components/shadcn/card"
12
- import { Button } from "@/components/shadcn/button"
13
-
14
- export interface SunpeakButtonProps extends Omit<React.ComponentProps<typeof Button>, 'onClick'> {
15
- isPrimary?: boolean
16
- onClick: () => void
17
- }
18
-
19
- export interface SunpeakCardData extends Record<string, unknown> {
20
- image?: string
21
- imageAlt?: string
22
- imageMaxWidth?: number
23
- imageMaxHeight?: number
24
- header?: React.ReactNode
25
- metadata?: React.ReactNode
26
- children?: React.ReactNode
27
- button1?: SunpeakButtonProps
28
- button2?: SunpeakButtonProps
29
- variant?: "default" | "bordered" | "elevated"
30
- }
31
-
32
- export interface SunpeakCardState extends Record<string, unknown> {
33
- selectedVariant?: "default" | "bordered" | "elevated"
34
- }
35
-
36
- export interface SunpeakCardProps extends React.HTMLAttributes<HTMLDivElement> {
37
- children?: React.ReactNode
38
- image?: string
39
- imageAlt?: string
40
- imageMaxWidth?: number
41
- imageMaxHeight?: number
42
- header?: React.ReactNode
43
- metadata?: React.ReactNode
44
- button1?: SunpeakButtonProps
45
- button2?: SunpeakButtonProps
46
- variant?: "default" | "bordered" | "elevated"
47
- }
48
-
49
- export const SunpeakCard = React.forwardRef<HTMLDivElement, SunpeakCardProps>(
50
- (
51
- {
52
- children: childrenProp,
53
- image: imageProp,
54
- imageAlt: imageAltProp,
55
- imageMaxWidth: imageMaxWidthProp = 400,
56
- imageMaxHeight: imageMaxHeightProp = 400,
57
- header: headerProp,
58
- metadata: metadataProp,
59
- button1: button1Prop,
60
- button2: button2Prop,
61
- variant: variantProp = "default",
62
- className,
63
- onClick,
64
- ...props
65
- },
66
- ref
67
- ) => {
68
- const [widgetState] = useWidgetState<SunpeakCardState>(() => ({}))
69
-
70
- const children = childrenProp
71
- const image = imageProp
72
- const imageAlt = imageAltProp
73
- const imageMaxWidth = imageMaxWidthProp
74
- const imageMaxHeight = imageMaxHeightProp
75
- const header = headerProp
76
- const metadata = metadataProp
77
- const button1 = button1Prop
78
- const button2 = button2Prop
79
- const variant = widgetState?.selectedVariant ?? variantProp
80
-
81
- const hasButtons = button1 || button2
82
-
83
- const handleCardClick = (e: React.MouseEvent<HTMLDivElement>) => {
84
- onClick?.(e)
85
- }
86
-
87
- const renderButton = (buttonProps: SunpeakButtonProps) => {
88
- const { isPrimary = false, onClick: buttonOnClick, children, ...restProps } = buttonProps
89
-
90
- const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
91
- e.stopPropagation()
92
- buttonOnClick()
93
- }
94
-
95
- return (
96
- <Button
97
- {...restProps}
98
- variant={isPrimary ? "default" : "outline"}
99
- onClick={handleClick}
100
- >
101
- {children}
102
- </Button>
103
- )
104
- }
105
-
106
- const variantClasses = {
107
- default: "",
108
- bordered: "border-2",
109
- elevated: "shadow-lg",
110
- }
111
-
112
- return (
113
- <Card
114
- ref={ref}
115
- className={cn(
116
- "overflow-hidden cursor-pointer select-none",
117
- variantClasses[variant],
118
- className
119
- )}
120
- onClick={handleCardClick}
121
- {...props}
122
- >
123
- {image && (
124
- <div className="w-full overflow-hidden">
125
- <img
126
- src={image}
127
- alt={imageAlt}
128
- loading="lazy"
129
- className="w-full h-auto aspect-square object-cover"
130
- style={{
131
- maxWidth: `${imageMaxWidth}px`,
132
- maxHeight: `${imageMaxHeight}px`,
133
- }}
134
- />
135
- </div>
136
- )}
137
- <div className="flex flex-col flex-1">
138
- {(header || metadata) && (
139
- <CardHeader className={cn("p-3", image && "pt-2")}>
140
- {header && (
141
- <CardTitle className="text-sm font-medium leading-tight overflow-hidden text-ellipsis whitespace-nowrap">
142
- {header}
143
- </CardTitle>
144
- )}
145
- {metadata && (
146
- <CardDescription className="text-xs leading-normal">
147
- {metadata}
148
- </CardDescription>
149
- )}
150
- </CardHeader>
151
- )}
152
- {children && (
153
- <CardContent className={cn(
154
- "px-3 pb-3 text-sm leading-normal",
155
- (header || metadata) ? "pt-0" : "pt-3"
156
- )}>
157
- <div className="line-clamp-2">{children}</div>
158
- </CardContent>
159
- )}
160
- {hasButtons && (
161
- <CardFooter className="flex gap-2 flex-wrap px-3 pb-3 pt-0">
162
- {button1 && renderButton(button1)}
163
- {button2 && renderButton(button2)}
164
- </CardFooter>
165
- )}
166
- </div>
167
- </Card>
168
- )
169
- }
170
- )
171
- SunpeakCard.displayName = "SunpeakCard"
@@ -1,42 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import { describe, it, expect } from 'vitest';
3
- import { SunpeakCarousel } from './sunpeak-carousel';
4
-
5
- describe('SunpeakCarousel', () => {
6
- it('renders all children as carousel items', () => {
7
- render(
8
- <SunpeakCarousel>
9
- <div>Item 1</div>
10
- <div>Item 2</div>
11
- <div>Item 3</div>
12
- </SunpeakCarousel>
13
- );
14
-
15
- expect(screen.getByText('Item 1')).toBeInTheDocument();
16
- expect(screen.getByText('Item 2')).toBeInTheDocument();
17
- expect(screen.getByText('Item 3')).toBeInTheDocument();
18
- });
19
-
20
- it('applies custom gap spacing to carousel content', () => {
21
- render(
22
- <SunpeakCarousel gap={24} data-testid="carousel">
23
- <div>Item 1</div>
24
- <div>Item 2</div>
25
- </SunpeakCarousel>
26
- );
27
-
28
- const carouselContent = document.querySelector('[class*="ml-0"]');
29
- expect(carouselContent).toHaveStyle({ gap: '24px' });
30
- });
31
-
32
- it('applies custom card width to carousel items', () => {
33
- render(
34
- <SunpeakCarousel cardWidth={300}>
35
- <div>Item 1</div>
36
- </SunpeakCarousel>
37
- );
38
-
39
- const carouselItem = document.querySelector('[class*="pl-0"]');
40
- expect(carouselItem).toHaveStyle({ flexBasis: '300px', minWidth: '300px' });
41
- });
42
- });
@@ -1,160 +0,0 @@
1
- import * as React from "react"
2
- import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures"
3
- import { useWidgetState, useDisplayMode } from "sunpeak"
4
- import { cn } from "@/lib/index"
5
- import {
6
- Carousel,
7
- CarouselContent,
8
- CarouselItem,
9
- CarouselNext,
10
- CarouselPrevious,
11
- type CarouselApi,
12
- } from "@/components/shadcn/carousel"
13
-
14
- export interface SunpeakCarouselData extends Record<string, unknown> {
15
- children?: React.ReactNode
16
- gap?: number
17
- showArrows?: boolean
18
- showEdgeGradients?: boolean
19
- cardWidth?: number | { inline?: number; fullscreen?: number }
20
- }
21
-
22
- export interface SunpeakCarouselState extends Record<string, unknown> {
23
- currentIndex?: number
24
- }
25
-
26
- export type SunpeakCarouselProps = {
27
- children?: React.ReactNode
28
- gap?: number
29
- showArrows?: boolean
30
- showEdgeGradients?: boolean
31
- cardWidth?: number | { inline?: number; fullscreen?: number }
32
- className?: string
33
- }
34
-
35
- export const SunpeakCarousel = React.forwardRef<
36
- HTMLDivElement,
37
- SunpeakCarouselProps
38
- >(
39
- (
40
- {
41
- children: childrenProp,
42
- gap: gapProp = 16,
43
- showArrows: showArrowsProp = true,
44
- showEdgeGradients: showEdgeGradientsProp = true,
45
- cardWidth: cardWidthProp,
46
- className,
47
- },
48
- ref
49
- ) => {
50
- const [widgetState, setWidgetState] = useWidgetState<SunpeakCarouselState>(() => ({
51
- currentIndex: 0,
52
- }))
53
- const displayMode = useDisplayMode()
54
-
55
- const children = childrenProp
56
- const gap = gapProp
57
- const showArrows = showArrowsProp
58
- const showEdgeGradients = showEdgeGradientsProp
59
- const cardWidth = cardWidthProp
60
-
61
- const [api, setApi] = React.useState<CarouselApi>()
62
- const [canScrollPrev, setCanScrollPrev] = React.useState(false)
63
- const [canScrollNext, setCanScrollNext] = React.useState(false)
64
-
65
- React.useEffect(() => {
66
- if (!api) return
67
-
68
- const onSelect = () => {
69
- setCanScrollPrev(api.canScrollPrev())
70
- setCanScrollNext(api.canScrollNext())
71
-
72
- const currentIndex = api.selectedScrollSnap()
73
- if (widgetState?.currentIndex !== currentIndex) {
74
- setWidgetState({ currentIndex })
75
- }
76
- }
77
-
78
- onSelect()
79
- api.on("select", onSelect)
80
- api.on("reInit", onSelect)
81
-
82
- return () => {
83
- api.off("select", onSelect)
84
- api.off("reInit", onSelect)
85
- }
86
- }, [api, widgetState?.currentIndex, setWidgetState])
87
-
88
- const childArray = React.Children.toArray(children)
89
-
90
- const getCardWidth = () => {
91
- if (typeof cardWidth === "number") {
92
- return `${cardWidth}px`
93
- }
94
- if (cardWidth && typeof cardWidth === "object") {
95
- if (displayMode === "fullscreen" && cardWidth.fullscreen) {
96
- return `${cardWidth.fullscreen}px`
97
- }
98
- if (cardWidth.inline) {
99
- return `${cardWidth.inline}px`
100
- }
101
- }
102
- return "220px"
103
- }
104
-
105
- return (
106
- <div ref={ref} className={cn("relative w-full", className)}>
107
- {showEdgeGradients && canScrollPrev && (
108
- <div
109
- className="pointer-events-none absolute left-0 top-0 z-10 h-full w-12 bg-gradient-to-r from-background to-transparent transition-opacity"
110
- aria-hidden="true"
111
- />
112
- )}
113
- {showEdgeGradients && canScrollNext && (
114
- <div
115
- className="pointer-events-none absolute right-0 top-0 z-10 h-full w-12 bg-gradient-to-l from-background to-transparent transition-opacity"
116
- aria-hidden="true"
117
- />
118
- )}
119
-
120
- <Carousel
121
- opts={{
122
- align: "start",
123
- dragFree: true,
124
- }}
125
- plugins={[WheelGesturesPlugin()]}
126
- setApi={setApi}
127
- className="w-full"
128
- >
129
- <CarouselContent
130
- className="ml-0"
131
- style={{
132
- gap: `${gap}px`,
133
- }}
134
- >
135
- {childArray.map((child, index) => (
136
- <CarouselItem
137
- key={index}
138
- className="pl-0"
139
- style={{
140
- flexBasis: getCardWidth(),
141
- minWidth: getCardWidth(),
142
- }}
143
- >
144
- {child}
145
- </CarouselItem>
146
- ))}
147
- </CarouselContent>
148
-
149
- {showArrows && canScrollPrev && (
150
- <CarouselPrevious className="left-2" />
151
- )}
152
- {showArrows && canScrollNext && (
153
- <CarouselNext className="right-2" />
154
- )}
155
- </Carousel>
156
- </div>
157
- )
158
- }
159
- )
160
- SunpeakCarousel.displayName = "SunpeakCarousel"