feat: collapible component
This commit is contained in:
74
src/components/Collapsible/index.tsx
Normal file
74
src/components/Collapsible/index.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function Collapsible({
|
||||||
|
alwaysExpand = false,
|
||||||
|
children,
|
||||||
|
threshold = 1000,
|
||||||
|
collapsedHeight = 600,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
alwaysExpand?: boolean
|
||||||
|
threshold?: number
|
||||||
|
collapsedHeight?: number
|
||||||
|
} & React.HTMLProps<HTMLDivElement>) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
const [shouldCollapse, setShouldCollapse] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (alwaysExpand || shouldCollapse) return
|
||||||
|
|
||||||
|
const contentEl = containerRef.current
|
||||||
|
if (!contentEl) return
|
||||||
|
|
||||||
|
const checkHeight = () => {
|
||||||
|
const fullHeight = contentEl.scrollHeight
|
||||||
|
if (fullHeight > threshold) {
|
||||||
|
setShouldCollapse(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkHeight()
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
checkHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
observer.observe(contentEl)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
}, [alwaysExpand, shouldCollapse])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} {...props}>
|
||||||
|
<div
|
||||||
|
className="relative text-left overflow-hidden"
|
||||||
|
style={{
|
||||||
|
maxHeight: !shouldCollapse || expanded ? 'none' : `${collapsedHeight}px`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{shouldCollapse && !expanded && (
|
||||||
|
<div className="absolute bottom-0 h-40 w-full bg-gradient-to-b from-transparent to-background/90 flex items-end justify-center pb-4">
|
||||||
|
<div className="bg-background rounded-md">
|
||||||
|
<Button
|
||||||
|
className="bg-foreground hover:bg-foreground/80"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setExpanded(!expanded)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Show more')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import { Button } from '@/components/ui/button'
|
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { toNote } from '@/lib/link'
|
import { toNote } from '@/lib/link'
|
||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
import { Event } from 'nostr-tools'
|
import { Event } from 'nostr-tools'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import Collapsible from '../Collapsible'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
import Note from '../Note'
|
import Note from '../Note'
|
||||||
import NoteStats from '../NoteStats'
|
import NoteStats from '../NoteStats'
|
||||||
import RepostDescription from './RepostDescription'
|
import RepostDescription from './RepostDescription'
|
||||||
@@ -20,41 +18,10 @@ export default function MainNoteCard({
|
|||||||
reposter?: string
|
reposter?: string
|
||||||
embedded?: boolean
|
embedded?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
|
||||||
const { push } = useSecondaryPage()
|
const { push } = useSecondaryPage()
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
|
||||||
const [expanded, setExpanded] = useState(false)
|
|
||||||
const [shouldCollapse, setShouldCollapse] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (embedded || shouldCollapse) return
|
|
||||||
|
|
||||||
const contentEl = containerRef.current
|
|
||||||
if (!contentEl) return
|
|
||||||
|
|
||||||
const checkHeight = () => {
|
|
||||||
const fullHeight = contentEl.scrollHeight
|
|
||||||
if (fullHeight > 1000) {
|
|
||||||
setShouldCollapse(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkHeight()
|
|
||||||
|
|
||||||
const observer = new ResizeObserver(() => {
|
|
||||||
checkHeight()
|
|
||||||
})
|
|
||||||
|
|
||||||
observer.observe(contentEl)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
observer.disconnect()
|
|
||||||
}
|
|
||||||
}, [embedded, shouldCollapse])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={containerRef}
|
|
||||||
className={className}
|
className={className}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -62,34 +29,14 @@ export default function MainNoteCard({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={`clickable ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'py-3'}`}>
|
<div className={`clickable ${embedded ? 'p-2 sm:p-3 border rounded-lg' : 'py-3'}`}>
|
||||||
<div
|
<Collapsible alwaysExpand={embedded}>
|
||||||
className="relative text-left overflow-hidden"
|
|
||||||
style={{
|
|
||||||
maxHeight: !shouldCollapse || expanded ? 'none' : '600px'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RepostDescription className={embedded ? '' : 'px-4'} reposter={reposter} />
|
<RepostDescription className={embedded ? '' : 'px-4'} reposter={reposter} />
|
||||||
<Note
|
<Note
|
||||||
className={embedded ? '' : 'px-4'}
|
className={embedded ? '' : 'px-4'}
|
||||||
size={embedded ? 'small' : 'normal'}
|
size={embedded ? 'small' : 'normal'}
|
||||||
event={event}
|
event={event}
|
||||||
/>
|
/>
|
||||||
{shouldCollapse && !expanded && (
|
</Collapsible>
|
||||||
<div className="absolute bottom-0 h-40 w-full bg-gradient-to-b from-transparent to-background/90 flex items-end justify-center pb-4">
|
|
||||||
<div className="bg-background rounded-md">
|
|
||||||
<Button
|
|
||||||
className="bg-foreground hover:bg-foreground/80"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setExpanded(!expanded)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('Show more')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!embedded && <NoteStats className="mt-3 px-4" event={event} />}
|
{!embedded && <NoteStats className="mt-3 px-4" event={event} />}
|
||||||
</div>
|
</div>
|
||||||
{!embedded && <Separator />}
|
{!embedded && <Separator />}
|
||||||
|
|||||||
Reference in New Issue
Block a user