Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a horizontal collapsible component for homepage/marketing needs #10258

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Make collapsible work
  • Loading branch information
albandum committed Jan 28, 2025
commit 0f3017a98b28946bad5012ded56667ac343d77db
34 changes: 21 additions & 13 deletions sparkle/src/components/HorizontalCollapsible.stories.tsx
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ export const Default: Story = {
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
value="1"
/>
<div>
<HorizontalCollapsible.Button>
@@ -59,6 +60,7 @@ export const DefaultOpen: Story = {
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
value="1"
/>
<div>
<HorizontalCollapsible.Button>
@@ -90,6 +92,7 @@ export const SecondaryVariant: Story = {
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
value="1"
/>
<div>
<HorizontalCollapsible.Button variant="secondary">
@@ -121,6 +124,7 @@ export const Disabled: Story = {
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
value="1"
/>
<div>
<HorizontalCollapsible.Button disabled>
@@ -152,6 +156,7 @@ export const WithCustomImage: Story = {
src={SAMPLE_IMAGE}
alt="Colorful gradient"
className="s-border-2 s-border-action-500"
value="1"
/>
<div>
<HorizontalCollapsible.Button>
@@ -181,35 +186,38 @@ export const MultipleItems: Story = {
return (
<div className="s-w-[600px]">
<HorizontalCollapsible defaultValue="1">
<HorizontalCollapsible.Item value="1">
<HorizontalCollapsible.ImageContainer>
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
alt="First gradient"
value="1"
/>
<div>
<HorizontalCollapsible.Image
src="https://images.unsplash.com/photo-1557682250-33bd709cbe85?w=400&h=300&fit=crop"
alt="Second gradient"
value="2"
/>
</HorizontalCollapsible.ImageContainer>

<HorizontalCollapsible.Content>
<HorizontalCollapsible.Item value="1">
<HorizontalCollapsible.Button>
First Item
</HorizontalCollapsible.Button>
<HorizontalCollapsible.Panel>
<p className="s-mt-2">Content for the first item.</p>
</HorizontalCollapsible.Panel>
</div>
</HorizontalCollapsible.Item>
</HorizontalCollapsible.Item>

<HorizontalCollapsible.Item value="2">
<HorizontalCollapsible.Image
src={SAMPLE_IMAGE}
alt="Colorful gradient"
/>
<div>
<HorizontalCollapsible.Item value="2">
<HorizontalCollapsible.Button>
Second Item
</HorizontalCollapsible.Button>
<HorizontalCollapsible.Panel>
<p className="s-mt-2">Content for the second item.</p>
</HorizontalCollapsible.Panel>
</div>
</HorizontalCollapsible.Item>
</HorizontalCollapsible.Item>
</HorizontalCollapsible.Content>
</HorizontalCollapsible>
</div>
);
79 changes: 51 additions & 28 deletions sparkle/src/components/HorizontalCollapsible.tsx
Original file line number Diff line number Diff line change
@@ -31,16 +31,42 @@ export const HorizontalCollapsible: React.FC<HorizontalCollapsibleProps> & {
Button: React.FC<HorizontalCollapsibleButtonProps>;
Panel: React.FC<HorizontalCollapsiblePanelProps>;
Image: React.FC<HorizontalCollapsibleImageProps>;
ImageContainer: React.FC<HorizontalCollapsibleImageContainerProps>;
Content: React.FC<HorizontalCollapsibleContentProps>;
} = ({ children, defaultValue = "1" }) => (
<RadioGroup defaultValue={defaultValue}>
{({ value }) => (
<OpenStateContext.Provider value={{ value, isSelected: false }}>
<div className="s-flex s-flex-col s-gap-4">{children}</div>
<div className="s-flex s-flex-row s-gap-6">{children}</div>
</OpenStateContext.Provider>
)}
</RadioGroup>
);

interface HorizontalCollapsibleImageContainerProps {
children: React.ReactNode;
}

HorizontalCollapsible.ImageContainer = function ({
children,
}: HorizontalCollapsibleImageContainerProps) {
return (
<div className="s-relative s-h-48 s-w-48 s-flex-shrink-0">{children}</div>
);
};

interface HorizontalCollapsibleContentProps {
children: React.ReactNode;
}

HorizontalCollapsible.Content = function ({
children,
}: HorizontalCollapsibleContentProps) {
return (
<div className="s-flex s-flex-grow s-flex-col s-gap-2">{children}</div>
);
};

interface HorizontalCollapsibleItemProps {
children: React.ReactNode;
value: string;
@@ -56,7 +82,7 @@ HorizontalCollapsible.Item = function ({
<OpenStateContext.Provider value={{ value, isSelected: checked }}>
<div
className={classNames(
"s-flex s-cursor-pointer s-flex-row s-items-start s-gap-6 s-rounded-lg s-p-2",
"s-flex s-cursor-pointer s-flex-col s-gap-2 s-rounded-lg s-p-2",
checked ? "s-bg-gray-50 dark:s-bg-gray-800" : ""
)}
>
@@ -72,37 +98,38 @@ interface HorizontalCollapsibleImageProps {
src: string;
alt: string;
className?: string;
value: string;
}

HorizontalCollapsible.Image = function ({
src,
alt,
className = "",
value,
}: HorizontalCollapsibleImageProps) {
const { isSelected } = useOpenState();
const { value: selectedValue } = useOpenState();
const isSelected = value === selectedValue;

return (
<div className="s-relative s-h-48 s-w-48 s-flex-shrink-0">
<Transition
show={isSelected}
enter="s-transition s-duration-300 s-ease-out"
enterFrom="s-opacity-0"
enterTo="s-opacity-100"
leave="s-transition s-duration-300 s-ease-out"
leaveFrom="s-opacity-100"
leaveTo="s-opacity-0"
className="s-absolute s-inset-0"
>
<img
src={src}
alt={alt}
className={classNames(
"s-h-full s-w-full s-rounded-lg s-object-cover s-shadow-sm",
className
)}
/>
</Transition>
</div>
<Transition
show={isSelected}
enter="s-transition s-duration-300 s-ease-out"
enterFrom="s-opacity-0"
enterTo="s-opacity-100"
leave="s-transition s-duration-300 s-ease-out"
leaveFrom="s-opacity-100"
leaveTo="s-opacity-0"
className="s-absolute s-inset-0"
>
<img
src={src}
alt={alt}
className={classNames(
"s-h-full s-w-full s-rounded-lg s-object-cover s-shadow-sm",
className
)}
/>
</Transition>
);
};

@@ -232,10 +259,6 @@ HorizontalCollapsible.Panel = function ({
show={isSelected}
enter="s-transition s-duration-300 s-ease-out"
enterFrom="s-transform s-scale-95 s-opacity-0"
enterTo="s-transform s-scale-100 s-opacity-100"
leave="s-transition s-duration-300 s-ease-out"
leaveFrom="s-transform s-scale-100 s-opacity-100"
leaveTo="s-transform s-scale-95 s-opacity-0"
>
<div className={className}>
<div className="s-text-gray-500">{children}</div>
Loading