@@ -107,49 +107,105 @@ const sectionHeader = {
107107 justifyContent : 'space-between' ,
108108} as const ;
109109
110- /** A `prompt` dialog opened from inside the `panel` — the shape the account profile uses. */
110+ /**
111+ * A `prompt` dialog opened from inside the `panel` — the shape the account profile uses.
112+ *
113+ * With `confirmDiscard`, closing it while the field holds anything opens a confirmation stacked on
114+ * top rather than closing: `panel -> prompt -> prompt`, and the veto is nothing more than a
115+ * controlled `open` whose `onOpenChange` declines to commit. Hand-rolled here on purpose — it is
116+ * what the `AlertDialog` and close-confirmation work is meant to replace.
117+ */
111118function AddValueDialog ( {
112119 trigger,
113120 title,
114121 description,
115122 placeholder,
116123 confirmLabel = 'Continue' ,
117124 confirmColor,
125+ confirmDiscard = false ,
118126} : {
119127 trigger : ( props : RenderProps ) => React . ReactElement ;
120128 title : string ;
121129 description : string ;
122130 placeholder : string ;
123131 confirmLabel ?: string ;
124132 confirmColor ?: 'negative' ;
133+ confirmDiscard ?: boolean ;
125134} ) {
135+ const [ open , setOpen ] = React . useState ( false ) ;
136+ const [ discardOpen , setDiscardOpen ] = React . useState ( false ) ;
137+ const [ value , setValue ] = React . useState ( '' ) ;
138+
139+ const dismiss = ( ) => {
140+ setValue ( '' ) ;
141+ setOpen ( false ) ;
142+ } ;
143+
126144 return (
127145 < Dialog
128146 trigger = { trigger }
129147 closedBy = 'closerequest'
148+ open = { open }
149+ onOpenChange = { next => {
150+ // The veto. Every close request lands here — Escape, the corner X, `Dialog.Close` — so
151+ // declining to commit covers all of them at once. A footer button wired to a bare
152+ // `setOpen(false)` would go around it, which is the argument for `Dialog.Close`.
153+ if ( ! next && confirmDiscard && value . trim ( ) !== '' ) {
154+ setDiscardOpen ( true ) ;
155+ return ;
156+ }
157+ if ( ! next ) {
158+ setValue ( '' ) ;
159+ }
160+ setOpen ( next ) ;
161+ } }
130162 >
131- { ( { close } ) => (
132- < >
133- < Dialog . CloseButton />
134- < Dialog . Title render = { < Heading size = 'sm' /> } > { title } </ Dialog . Title >
135- < Dialog . Description render = { < Text /> } > { description } </ Dialog . Description >
136- < Input placeholder = { placeholder } />
163+ < Dialog . CloseButton />
164+ < Dialog . Title render = { < Heading size = 'sm' /> } > { title } </ Dialog . Title >
165+ < Dialog . Description render = { < Text /> } > { description } </ Dialog . Description >
166+ < Input
167+ placeholder = { placeholder }
168+ value = { value }
169+ onChange = { event => setValue ( event . target . value ) }
170+ />
171+ < div style = { { display : 'flex' , gap : '0.5rem' , justifyContent : 'flex-end' } } >
172+ < Dialog . Close render = { < Button variant = 'outline' /> } > Cancel</ Dialog . Close >
173+ < Button
174+ color = { confirmColor }
175+ onClick = { dismiss }
176+ >
177+ { confirmLabel }
178+ </ Button >
179+ </ div >
180+ { confirmDiscard ? (
181+ < Dialog
182+ open = { discardOpen }
183+ onOpenChange = { setDiscardOpen }
184+ closedBy = 'closerequest'
185+ >
186+ < Dialog . Title render = { < Heading size = 'sm' /> } > Discard changes?</ Dialog . Title >
187+ < Dialog . Description render = { < Text /> } >
188+ You have not finished adding this address. It will not be saved.
189+ </ Dialog . Description >
137190 < div style = { { display : 'flex' , gap : '0.5rem' , justifyContent : 'flex-end' } } >
138191 < Button
139192 variant = 'outline'
140- onClick = { close }
193+ onClick = { ( ) => setDiscardOpen ( false ) }
141194 >
142- Cancel
195+ Keep editing
143196 </ Button >
144197 < Button
145- color = { confirmColor }
146- onClick = { close }
198+ color = 'negative'
199+ onClick = { ( ) => {
200+ setDiscardOpen ( false ) ;
201+ dismiss ( ) ;
202+ } }
147203 >
148- { confirmLabel }
204+ Discard
149205 </ Button >
150206 </ div >
151- </ >
152- ) }
207+ </ Dialog >
208+ ) : null }
153209 </ Dialog >
154210 ) ;
155211}
@@ -173,6 +229,7 @@ export function Nested() {
173229 title = 'Add email address'
174230 description = "We'll send a verification code to this address."
175231 placeholder = 'you@example.com'
232+ confirmDiscard
176233 />
177234 </ div >
178235 < Item . Group >
0 commit comments