@@ -65,6 +65,7 @@ export const PageList: React.FC<PageListProps> = ({
6565 const [ templatesError , setTemplatesError ] = useState < string | null > ( null ) ;
6666 const [ isDeleting , setIsDeleting ] = useState < string | null > ( null ) ;
6767 const [ isMoving , setIsMoving ] = useState ( false ) ;
68+ const [ moveDestination , setMoveDestination ] = useState < string > ( "" ) ;
6869 const [ sortField , setSortField ] = useState < SortField > ( "name" ) ;
6970 const [ sortDirection , setSortDirection ] = useState < SortDirection > ( "asc" ) ;
7071 const [ selectedIndex , setSelectedIndex ] = useState ( 0 ) ;
@@ -332,15 +333,16 @@ export const PageList: React.FC<PageListProps> = ({
332333
333334 const handleMovePage = ( path : string ) => {
334335 setMovingPage ( path ) ;
336+ setMoveDestination ( "" ) ;
335337 setContextMenuPage ( null ) ;
336338 } ;
337339
338- const handleMoveSubmit = async ( targetFolder : string ) => {
340+ const handleMoveSubmit = async ( ) => {
339341 if ( ! movingPage ) return ;
340342
341343 setIsMoving ( true ) ;
342344 try {
343- const result = await api . movePage ( movingPage , targetFolder ) ;
345+ const result = await api . movePage ( movingPage , moveDestination ) ;
344346 loadPages ( ) ;
345347 onRefresh ( ) ;
346348 if ( selectedPage === movingPage ) {
@@ -358,24 +360,25 @@ export const PageList: React.FC<PageListProps> = ({
358360 }
359361 } ;
360362
361- const getAllFolders = (
363+ /** Recursive folder picker with tree structure and indentation */
364+ const renderPickerNodes = (
362365 node : FolderNode ,
363- prefix : string = "" ,
364- ) : Array < { path : string ; display : string } > => {
365- const folders : Array < { path : string ; display : string } > = [ ] ;
366- const currentPath = prefix ? ` ${ prefix } / ${ node . name } ` : node . name ;
367- const displayPath = currentPath === "" ? "/" : currentPath ;
368-
369- folders . push ( {
370- path : node . path === "/" ? "" : node . path ,
371- display : displayPath ,
372- } ) ;
373-
374- for ( const child of node . children ) {
375- folders . push ( ... getAllFolders ( child , currentPath ) ) ;
376- }
377-
378- return folders ;
366+ indent : number = 0 ,
367+ ) : React . ReactNode => {
368+ return node . children . map ( ( child ) => (
369+ < React . Fragment key = { child . path } >
370+ < button
371+ className = { `move-picker-item ${ moveDestination === child . path ? "selected" : "" } ` }
372+ style = { { paddingLeft : ` ${ indent * 16 + 8 } px` } }
373+ onClick = { ( ) => setMoveDestination ( child . path ) }
374+ role = "option"
375+ aria-selected = { moveDestination === child . path }
376+ >
377+ < Folder size = { 14 } aria-hidden = "true" /> { child . name }
378+ </ button >
379+ { renderPickerNodes ( child , indent + 1 ) }
380+ </ React . Fragment >
381+ ) ) ;
379382 } ;
380383
381384 return (
@@ -531,47 +534,51 @@ export const PageList: React.FC<PageListProps> = ({
531534 { /* Move Page Modal */ }
532535 { movingPage && folderTree && (
533536 < div
534- className = "modal-overlay"
537+ className = "move- modal-overlay"
535538 onClick = { ( ) => ! isMoving && setMovingPage ( null ) }
536539 role = "dialog"
537540 aria-modal = "true"
538541 aria-labelledby = "move-modal-title"
539542 >
540- < div className = "modal" onClick = { ( e ) => e . stopPropagation ( ) } >
541- < h3 id = "move-modal-title" > Move Page</ h3 >
542- < p >
543- Select destination folder for{ " " }
544- < strong > { movingPage . split ( "/" ) . pop ( ) } </ strong > :
545- </ p >
543+ < div className = "move-modal" onClick = { ( e ) => e . stopPropagation ( ) } >
544+ < h3 className = "move-modal-title" id = "move-modal-title" >
545+ Move "{ movingPage . split ( "/" ) . pop ( ) } "
546+ </ h3 >
547+ < p className = "move-modal-subtitle" > Select destination folder:</ p >
546548 { isMoving ? (
547549 < div className = "modal-loading" role = "status" aria-live = "polite" >
548550 < Loader2 size = { 24 } className = "loading-spinner" aria-hidden = "true" />
549551 < span > Moving page...</ span >
550552 </ div >
551553 ) : (
552- < div className = "folder-list" role = "list" >
553- { getAllFolders ( folderTree ) . map ( ( folder ) => (
554- < button
555- key = { folder . path }
556- className = "folder-option"
557- onClick = { ( ) => handleMoveSubmit ( folder . path ) }
558- disabled = { isMoving }
559- role = "listitem"
560- aria-label = { `Move to ${ folder . display } ` }
561- >
562- < Folder size = { 14 } aria-hidden = "true" /> { folder . display }
563- </ button >
564- ) ) }
554+ < div className = "move-picker" role = "listbox" aria-label = "Destination folder" >
555+ < button
556+ className = { `move-picker-item move-picker-root ${ moveDestination === "" ? "selected" : "" } ` }
557+ onClick = { ( ) => setMoveDestination ( "" ) }
558+ role = "option"
559+ aria-selected = { moveDestination === "" }
560+ >
561+ < Folder size = { 14 } aria-hidden = "true" /> Root (top level)
562+ </ button >
563+ { renderPickerNodes ( folderTree , 1 ) }
565564 </ div >
566565 ) }
567- < button
568- className = "cancel-btn"
569- onClick = { ( ) => setMovingPage ( null ) }
570- disabled = { isMoving }
571- aria-label = "Cancel moving page"
572- >
573- Cancel
574- </ button >
566+ < div className = "move-modal-actions" >
567+ < button
568+ className = "move-modal-cancel"
569+ onClick = { ( ) => setMovingPage ( null ) }
570+ disabled = { isMoving }
571+ >
572+ Cancel
573+ </ button >
574+ < button
575+ className = "move-modal-confirm"
576+ onClick = { handleMoveSubmit }
577+ disabled = { isMoving }
578+ >
579+ { isMoving ? "Moving..." : "Move Here" }
580+ </ button >
581+ </ div >
575582 </ div >
576583 </ div >
577584 ) }
0 commit comments