11'use client' ;
22
3- import { ReactNode , useEffect , useRef , useState } from 'react' ;
3+ import { ReactNode , useCallback , useEffect , useRef , useState } from 'react' ;
44import { ChevronDownIcon , ChevronRightIcon } from '@radix-ui/react-icons' ;
55import * as Sentry from '@sentry/nextjs' ;
66
@@ -38,7 +38,6 @@ export function Expandable({
3838
3939 const [ isExpanded , setIsExpanded ] = useState ( false ) ;
4040 const [ copied , setCopied ] = useState ( false ) ;
41- const [ showCopyButton , setShowCopyButton ] = useState ( false ) ;
4241 const contentRef = useRef < HTMLDivElement > ( null ) ;
4342
4443 // Ensure we scroll to the element if the URL hash matches
@@ -67,55 +66,52 @@ export function Expandable({
6766 } ;
6867 } , [ id ] ) ;
6968
70- useEffect ( ( ) => {
71- if ( copy ) {
72- setShowCopyButton ( true ) ;
73- }
74- } , [ copy ] ) ;
69+ const copyContentOnClick = useCallback (
70+ async ( event : React . MouseEvent < HTMLButtonElement > ) => {
71+ event . stopPropagation ( ) ; // Prevent the details element from toggling
72+ event . preventDefault ( ) ; // Prevent default summary click behavior
7573
76- async function copyContentOnClick ( event : React . MouseEvent < HTMLButtonElement > ) {
77- event . stopPropagation ( ) ; // Prevent the details element from toggling
78- event . preventDefault ( ) ; // Prevent default summary click behavior
79-
80- if ( contentRef . current === null ) {
81- return ;
82- }
74+ if ( contentRef . current === null ) {
75+ return ;
76+ }
8377
84- // Attempt to get text from markdown code blocks if they exist
85- const codeBlocks = contentRef . current . querySelectorAll ( 'code' ) ;
86- let contentToCopy = '' ;
87-
88- if ( codeBlocks . length > 0 ) {
89- // If there are code blocks, concatenate their text content
90- codeBlocks . forEach ( block => {
91- // Exclude code elements within other code elements (e.g. inline code in a block)
92- if ( ! block . closest ( 'code' ) ?. parentElement ?. closest ( 'code' ) ) {
93- contentToCopy += ( block . textContent || '' ) + '\n' ;
94- }
95- } ) ;
96- contentToCopy = contentToCopy . trim ( ) ;
97- }
78+ // Attempt to get text from markdown code blocks if they exist
79+ const codeBlocks = contentRef . current . querySelectorAll ( 'code' ) ;
80+ let contentToCopy = '' ;
81+
82+ if ( codeBlocks . length > 0 ) {
83+ // If there are code blocks, concatenate their text content
84+ codeBlocks . forEach ( block => {
85+ // Exclude code elements within other code elements (e.g. inline code in a block)
86+ if ( ! block . closest ( 'code' ) ?. parentElement ?. closest ( 'code' ) ) {
87+ contentToCopy += ( block . textContent || '' ) + '\n' ;
88+ }
89+ } ) ;
90+ contentToCopy = contentToCopy . trim ( ) ;
91+ }
9892
99- // Fallback to the whole content if no code blocks or if they are empty
100- if ( ! contentToCopy && contentRef . current . textContent ) {
101- contentToCopy = contentRef . current . textContent . trim ( ) ;
102- }
93+ // Fallback to the whole content if no code blocks or if they are empty
94+ if ( ! contentToCopy && contentRef . current . textContent ) {
95+ contentToCopy = contentRef . current . textContent . trim ( ) ;
96+ }
10397
104- if ( ! contentToCopy ) {
105- // if there is no content to copy (e.g. only images), do nothing.
106- return ;
107- }
98+ if ( ! contentToCopy ) {
99+ // if there is no content to copy (e.g. only images), do nothing.
100+ return ;
101+ }
108102
109- try {
110- setCopied ( false ) ;
111- await navigator . clipboard . writeText ( contentToCopy ) ;
112- setCopied ( true ) ;
113- setTimeout ( ( ) => setCopied ( false ) , 1200 ) ;
114- } catch ( error ) {
115- Sentry . captureException ( error ) ;
116- setCopied ( false ) ;
117- }
118- }
103+ try {
104+ setCopied ( false ) ;
105+ await navigator . clipboard . writeText ( contentToCopy ) ;
106+ setCopied ( true ) ;
107+ setTimeout ( ( ) => setCopied ( false ) , 1200 ) ;
108+ } catch ( error ) {
109+ Sentry . captureException ( error ) ;
110+ setCopied ( false ) ;
111+ }
112+ } ,
113+ [ ]
114+ ) ;
119115
120116 function toggleIsExpanded ( event : React . MouseEvent < HTMLDetailsElement > ) {
121117 const newVal = event . currentTarget . open ;
@@ -140,12 +136,11 @@ export function Expandable({
140136 >
141137 < summary className = { `${ styles [ 'expandable-header' ] } callout-header` } >
142138 < div className = { styles [ 'expandable-title-container' ] } >
143- < ChevronDownIcon
144- className = { `${ styles [ 'expandable-icon-expanded' ] } callout-icon` }
145- />
146- < ChevronRightIcon
147- className = { `${ styles [ 'expandable-icon-collapsed' ] } callout-icon` }
148- />
139+ { isExpanded ? (
140+ < ChevronDownIcon className = "callout-icon" />
141+ ) : (
142+ < ChevronRightIcon className = "callout-icon" />
143+ ) }
149144 < div > { title } </ div >
150145 </ div >
151146 { copy && (
@@ -154,8 +149,8 @@ export function Expandable({
154149 onClick = { copyContentOnClick }
155150 type = "button" // Important for buttons in summaries
156151 >
157- { showCopyButton && ! copied && 'Copy Rules' }
158- { showCopyButton && copied && 'Copied!' }
152+ { ! copied && 'Copy Rules' }
153+ { copied && 'Copied!' }
159154 </ button >
160155 ) }
161156 </ summary >
0 commit comments