@@ -2025,6 +2025,12 @@ function AllocationProgressBar({ percent }) {
20252025 ) ;
20262026}
20272027
2028+ // Renders modal content as a direct child of document.body, escaping any
2029+ // overflow/stacking-context created by ancestor scrollable containers.
2030+ function ModalPortal ( { children } ) {
2031+ return ReactDOM . createPortal ( children , document . body ) ;
2032+ }
2033+
20282034// Modal for editing a single allocation entry
20292035function AllocationModal ( { allocation, accountName, onSave, onClose } ) {
20302036 const [ form , setForm ] = useState ( allocation ? { ...allocation } : {
@@ -2072,18 +2078,21 @@ function AllocationModal({ allocation, accountName, onSave, onClose }) {
20722078 }
20732079
20742080 return React . createElement (
2075- 'div' ,
2076- { className : 'modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
2081+ ModalPortal ,
2082+ null ,
20772083 React . createElement (
20782084 'div' ,
2079- {
2080- className : 'modal' ,
2081- onClick : e => e . stopPropagation ( ) ,
2082- style : { maxWidth : '520px' , width : '95%' }
2083- } ,
2084- React . createElement ( 'h3' , null , `${ allocation ? 'Edit' : 'Add' } Allocation: ${ accountName } ` ) ,
2085- error && React . createElement ( 'p' , { className : 'error' } , error ) ,
2085+ { className : 'sl-modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
20862086 React . createElement (
2087+ 'div' ,
2088+ {
2089+ className : 'sl-modal' ,
2090+ onClick : e => e . stopPropagation ( ) ,
2091+ style : { maxWidth : '520px' , width : '95%' }
2092+ } ,
2093+ React . createElement ( 'h3' , null , `${ allocation ? 'Edit' : 'Add' } Allocation: ${ accountName } ` ) ,
2094+ error && React . createElement ( 'p' , { className : 'error' } , error ) ,
2095+ React . createElement (
20872096 'div' ,
20882097 { style : { marginBottom : '0.75em' } } ,
20892098 React . createElement ( 'label' , null , 'Type: ' ) ,
@@ -2180,6 +2189,7 @@ function AllocationModal({ allocation, accountName, onSave, onClose }) {
21802189 React . createElement ( 'button' , { onClick : handleSave } , 'Save' )
21812190 )
21822191 )
2192+ )
21832193 ) ;
21842194}
21852195
@@ -2719,14 +2729,17 @@ function BillingRuleModal({ rule, onSave, onClose }) {
27192729 : '' ;
27202730
27212731 return React . createElement (
2722- 'div' ,
2723- { className : 'modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
2732+ ModalPortal ,
2733+ null ,
27242734 React . createElement (
27252735 'div' ,
2726- { className : 'modal' , onClick : e => e . stopPropagation ( ) , style : { maxWidth : '560px' , width : '95%' } } ,
2727- React . createElement ( 'h3' , null , isNew ? 'Add Billing Rule' : `Edit Rule: ${ rule . name } ` ) ,
2728- error && React . createElement ( 'p' , { className : 'error' } , error ) ,
2729- React . createElement ( 'div' , { style : { marginBottom : '0.75em' } } ,
2736+ { className : 'sl-modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
2737+ React . createElement (
2738+ 'div' ,
2739+ { className : 'sl-modal' , onClick : e => e . stopPropagation ( ) , style : { maxWidth : '560px' , width : '95%' } } ,
2740+ React . createElement ( 'h3' , null , isNew ? 'Add Billing Rule' : `Edit Rule: ${ rule . name } ` ) ,
2741+ error && React . createElement ( 'p' , { className : 'error' } , error ) ,
2742+ React . createElement ( 'div' , { style : { marginBottom : '0.75em' } } ,
27302743 React . createElement ( 'label' , null , 'Rule ID: ' ) ,
27312744 React . createElement ( 'input' , {
27322745 ref : firstInputRef , type : 'text' , value : form . id , disabled : ! isNew ,
@@ -2830,6 +2843,7 @@ function BillingRuleModal({ rule, onSave, onClose }) {
28302843 React . createElement ( 'button' , { onClick : handleSave } , 'Save' )
28312844 )
28322845 )
2846+ )
28332847 ) ;
28342848}
28352849
@@ -4329,16 +4343,19 @@ function RefundModal({ invoice, currentUser, onClose, onIssue }) {
43294343 }
43304344
43314345 return React . createElement (
4332- 'div' ,
4333- { className : 'modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
4346+ ModalPortal ,
4347+ null ,
43344348 React . createElement (
43354349 'div' ,
4336- {
4337- className : 'modal' ,
4338- onClick : e => e . stopPropagation ( ) ,
4339- style : { maxWidth : '480px' , width : '90%' }
4340- } ,
4341- React . createElement ( 'h3' , null , `Issue Refund — ${ invoice . id } ` ) ,
4350+ { className : 'sl-modal-overlay' , onClick : onClose , onKeyDown : ( e ) => { if ( e . key === 'Escape' ) onClose ( ) ; } } ,
4351+ React . createElement (
4352+ 'div' ,
4353+ {
4354+ className : 'sl-modal' ,
4355+ onClick : e => e . stopPropagation ( ) ,
4356+ style : { maxWidth : '480px' , width : '90%' }
4357+ } ,
4358+ React . createElement ( 'h3' , null , `Issue Refund — ${ invoice . id } ` ) ,
43424359 React . createElement (
43434360 'div' ,
43444361 { style : { marginBottom : '0.75em' } } ,
@@ -4391,6 +4408,7 @@ function RefundModal({ invoice, currentUser, onClose, onIssue }) {
43914408 )
43924409 )
43934410 )
4411+ )
43944412 ) ;
43954413}
43964414
@@ -6020,14 +6038,19 @@ function SetupWizard({ onComplete }) {
60206038 ) ,
60216039 React . createElement ( 'div' , { style : { flexShrink : 0 } } , progressBar ) ,
60226040
6023- // Step content — single scrollable container that switches content by step
6041+ // Step content — keyed wrapper remounts on step change; renderStep returns
6042+ // a single plain div per step so there is no Fragment ambiguity.
60246043 React . createElement (
60256044 'div' ,
6026- { key : 'step-' + step , style : { overflowY : 'auto' , flex : 1 , paddingRight : '0.5em' } } ,
6045+ { key : step , style : { overflowY : 'auto' , flex : 1 , paddingRight : '0.5em' } } ,
6046+ renderStep ( )
6047+ )
6048+ ) ; // close SetupWizard outer div
60276049
6028- // Step 1: Institution Profile
6029- step === 1 ? React . createElement (
6030- React . Fragment ,
6050+ function renderStep ( ) {
6051+ if ( step === 1 ) {
6052+ return React . createElement (
6053+ 'div' ,
60316054 null ,
60326055 React . createElement ( InstitutionProfile , null ) ,
60336056 React . createElement (
@@ -6039,11 +6062,12 @@ function SetupWizard({ onComplete }) {
60396062 'Next: Set Billing Rates \u2192'
60406063 )
60416064 )
6042- ) :
6065+ ) ;
6066+ }
60436067
6044- // Step 2: Rate Configuration
6045- step === 2 ? React . createElement (
6046- React . Fragment ,
6068+ if ( step === 2 ) {
6069+ return React . createElement (
6070+ 'div' ,
60476071 null ,
60486072 React . createElement ( Rates , { onRatesUpdated : ( ) => { } , username : '' , userRole : 'admin' } ) ,
60496073 React . createElement (
@@ -6056,12 +6080,13 @@ function SetupWizard({ onComplete }) {
60566080 'Next: Test Connection \u2192'
60576081 )
60586082 )
6059- ) :
6083+ ) ;
6084+ }
60606085
6061- // Step 3: Database connection test
6062- React . createElement (
6063- React . Fragment ,
6064- null ,
6086+ // Step 3: Database connection test
6087+ return React . createElement (
6088+ 'div' ,
6089+ null ,
60656090 React . createElement ( 'h3' , null , 'Test Database Connection' ) ,
60666091 React . createElement (
60676092 'p' ,
@@ -6131,9 +6156,8 @@ function SetupWizard({ onComplete }) {
61316156 )
61326157 )
61336158 )
6134- ) // close Step 3 Fragment
6135- ) // close scrollable wrapper div
6136- ) ; // close SetupWizard outer div
6159+ ) ;
6160+ }
61376161}
61386162
61396163function App ( ) {
0 commit comments