Skip to content

Commit

Permalink
refactor: simplify collapsible-panel
Browse files Browse the repository at this point in the history
  • Loading branch information
altaywtf committed Dec 15, 2023
1 parent 56d89ca commit 45c9238
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 103 deletions.
29 changes: 0 additions & 29 deletions components/ui/client-only.tsx

This file was deleted.

41 changes: 0 additions & 41 deletions components/ui/collapsible-panel.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,9 @@
import type { Meta, StoryObj } from '@storybook/react';

import { FAKE_EPISODE_SUMMARY } from '@/lib/fixtures/fake-episode-summary';
import { Button, Flex } from '@radix-ui/themes';
import { useState } from 'react';

import { CollapsiblePanel } from './collapsible-panel';

export const Controlled: StoryObj<typeof CollapsiblePanel> = {
render: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks -- storybook
const [open, setOpen] = useState(false);
// eslint-disable-next-line react-hooks/rules-of-hooks -- storybook
const [content, setContent] = useState(FAKE_EPISODE_SUMMARY);

return (
<Flex direction="column" gap="2">
<Flex direction="row" gap="1">
<Button
highContrast
onClick={() => {
setOpen((o) => !o);
}}
size="1"
>
{open ? 'Shrink' : 'Expand'} content
</Button>

<Button
highContrast
onClick={() => {
setContent((c) => c + FAKE_EPISODE_SUMMARY);
}}
size="1"
>
Add more content
</Button>
</Flex>

<CollapsiblePanel open={open} title="Controlled">
{content}
</CollapsiblePanel>
</Flex>
);
},
};

export const Uncontrolled: StoryObj<typeof CollapsiblePanel> = {
render: () => (
<CollapsiblePanel title="Uncontrolled">
Expand Down
59 changes: 26 additions & 33 deletions components/ui/collapsible-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ import { Box, Card, Flex, Heading, IconButton, Text } from '@radix-ui/themes';
import { useEffect, useRef, useState } from 'react';
import { CgClose, CgExpand } from 'react-icons/cg';

import { withClientOnly } from './client-only';
import styles from './collapsible-panel.module.css';

type Props = PropsWithChildren<{
height?: number;
open?: boolean;
title: ReactNode;
}>;

function _CollapsiblePanel({ children, height = 60, ...props }: Props) {
export function CollapsiblePanel({ children, height = 60, ...props }: Props) {
const contentRef = useRef<HTMLDivElement | null>(null);
const controlledRef = useRef(typeof props.open === 'boolean');
const [isHeightCalculated, setIsHeightCalculated] = useState(false);
const [open, setOpen] = useState(controlledRef.current ? props.open : false);
const [open, setOpen] = useState(false);
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (contentRef.current) {
Expand All @@ -29,33 +30,23 @@ function _CollapsiblePanel({ children, height = 60, ...props }: Props) {
'--max-height',
`${contentRef.current.scrollHeight}px`,
);

setIsHeightCalculated(true);
}
}, [height, children]);

useEffect(() => {
if (typeof props.open === 'boolean') {
setOpen(props.open);
}
}, [open, props.open]);

return (
<Card className={styles.Container}>
{controlledRef.current ? null : (
<Flex m="2" position="absolute" right="0" top="0">
<IconButton
onClick={() => {
setOpen((o) => !o);
}}
size="1"
tabIndex={-1}
variant="ghost"
>
{open ? <CgClose /> : <CgExpand />}
</IconButton>
</Flex>
)}
<Flex m="2" position="absolute" right="0" top="0">
<IconButton
onClick={() => {
setOpen((o) => !o);
}}
size="1"
tabIndex={-1}
variant="ghost"
>
{open ? <CgClose /> : <CgExpand />}
</IconButton>
</Flex>

<Heading mb="2" size="2">
{props.title}
Expand All @@ -65,9 +56,13 @@ function _CollapsiblePanel({ children, height = 60, ...props }: Props) {
className={styles.Content}
data-state={open ? 'open' : 'closed'}
ref={contentRef}
style={{
visibility: isHeightCalculated ? 'visible' : 'hidden',
}}
style={
mounted
? {}
: {
maxHeight: `${height}px`,
}
}
>
<Text color="gray" size="2">
{children}
Expand All @@ -76,5 +71,3 @@ function _CollapsiblePanel({ children, height = 60, ...props }: Props) {
</Card>
);
}

export const CollapsiblePanel = withClientOnly(_CollapsiblePanel);

0 comments on commit 45c9238

Please sign in to comment.