1- import { useState } from "react" ;
1+ import { useId , useState } from "react" ;
22import type { ReactNode } from "react" ;
33
44import { Button } from "../../src/ui/button.js" ;
@@ -23,14 +23,22 @@ const MESSAGES = Array.from({ length: 24 }, (_, index) => ({
2323 : `Reply ${ index + 1 } : keep going, and flag anything over the usual range.` ,
2424} ) ) ;
2525
26- /** The main area beside the panel, with a button that reopens it. */
26+ /**
27+ * The main area beside the panel, with the button that opens and closes it.
28+ * The button stays mounted while the panel is open, so the panel records it
29+ * as the opener and gives it focus back on close.
30+ */
2731function Main ( {
2832 open,
29- onOpen,
33+ onToggle,
34+ panelId,
35+ label,
3036 children,
3137} : {
3238 readonly open : boolean ;
33- readonly onOpen : ( ) => void ;
39+ readonly onToggle : ( ) => void ;
40+ readonly panelId : string ;
41+ readonly label : string ;
3442 readonly children ?: ReactNode ;
3543} ) {
3644 return (
@@ -40,11 +48,15 @@ function Main({
4048 The conversation keeps at least 440px however wide the panel is dragged.
4149 </ p >
4250 { children }
43- { open ? null : (
44- < Button variant = "outline" className = "self-start" onClick = { onOpen } >
45- Open thread
46- </ Button >
47- ) }
51+ < Button
52+ variant = "outline"
53+ className = "self-start"
54+ aria-expanded = { open }
55+ aria-controls = { open ? panelId : undefined }
56+ onClick = { onToggle }
57+ >
58+ { label }
59+ </ Button >
4860 </ main >
4961 ) ;
5062}
@@ -68,11 +80,17 @@ function Thread() {
6880
6981/** A wide screen: drag the edge, use the arrow keys, or double-click to reset. */
7082export const Wide = ( ) => {
83+ const panelId = useId ( ) ;
7184 const [ open , setOpen ] = useState ( true ) ;
7285 const [ width , setWidth ] = useState < number | null > ( null ) ;
7386 return (
7487 < div className = "@container relative flex h-[560px] overflow-hidden rounded-lg border border-border" >
75- < Main open = { open } onOpen = { ( ) => setOpen ( true ) } >
88+ < Main
89+ open = { open }
90+ onToggle = { ( ) => setOpen ( ! open ) }
91+ panelId = { panelId }
92+ label = "Thread"
93+ >
7694 < p className = "text-sm" >
7795 Chosen width:{ " " }
7896 < output aria-label = "Panel width" >
@@ -81,7 +99,12 @@ export const Wide = () => {
8199 </ p >
82100 </ Main >
83101 { open ? (
84- < Panel aria-label = "Thread" width = { width } onWidthChange = { setWidth } >
102+ < Panel
103+ id = { panelId }
104+ aria-label = "Thread"
105+ width = { width }
106+ onWidthChange = { setWidth }
107+ >
85108 < PanelHandle label = "Resize thread panel" />
86109 < PanelHeader >
87110 < h2 className = "text-sm font-semibold" > Thread</ h2 >
@@ -102,12 +125,18 @@ export const Wide = () => {
102125
103126/** A row too narrow for both: the panel covers it and hides its handle. */
104127export const NarrowScreenOverlay = ( ) => {
128+ const panelId = useId ( ) ;
105129 const [ open , setOpen ] = useState ( true ) ;
106130 return (
107131 < div className = "@container relative flex h-[560px] max-w-sm overflow-hidden rounded-lg border border-border" >
108- < Main open = { open } onOpen = { ( ) => setOpen ( true ) } />
132+ < Main
133+ open = { open }
134+ onToggle = { ( ) => setOpen ( ! open ) }
135+ panelId = { panelId }
136+ label = "Thread"
137+ />
109138 { open ? (
110- < Panel aria-label = "Thread" >
139+ < Panel id = { panelId } aria-label = "Thread" >
111140 < PanelHandle label = "Resize thread panel" />
112141 < PanelHeader >
113142 < h2 className = "text-sm font-semibold" > Thread</ h2 >
@@ -139,13 +168,19 @@ type PaneId = (typeof PANES)[number]["id"];
139168 * back, and both are where you left them.
140169 */
141170export const SeveralPanes = ( ) => {
171+ const panelId = useId ( ) ;
142172 const [ open , setOpen ] = useState ( true ) ;
143173 const [ shown , setShown ] = useState < PaneId > ( "thread" ) ;
144174 return (
145175 < div className = "@container relative flex h-[560px] overflow-hidden rounded-lg border border-border" >
146- < Main open = { open } onOpen = { ( ) => setOpen ( true ) } />
176+ < Main
177+ open = { open }
178+ onToggle = { ( ) => setOpen ( ! open ) }
179+ panelId = { panelId }
180+ label = "Side panel"
181+ />
147182 { open ? (
148- < Panel aria-label = "Side panel" >
183+ < Panel id = { panelId } aria-label = "Side panel" >
149184 < PanelHandle />
150185 < PanelHeader >
151186 < div
0 commit comments