@@ -7,6 +7,9 @@ import React, {
77 useState ,
88} from "react" ;
99import ReactDOM from "react-dom" ;
10+ import { arrayMove } from "@dnd-kit/sortable" ;
11+ import { SortableList , type SortableHandle } from "./SortableList" ;
12+ import { moveRoamBlockToIndex } from "~/utils/moveRoamBlock" ;
1013import {
1114 Button ,
1215 Collapse ,
@@ -132,61 +135,86 @@ const toggleFoldedState = ({
132135 }
133136} ;
134137
138+ type ChildNode = { uid : string ; text : string ; alias ?: { value : string } } ;
139+
140+ const ChildRow = ( {
141+ child,
142+ truncateAt,
143+ onloadArgs,
144+ } : {
145+ child : ChildNode ;
146+ truncateAt ?: number ;
147+ onloadArgs : OnloadArgs ;
148+ } ) => {
149+ const ref = parseReference ( child . text ) ;
150+ const alias = child . alias ?. value ;
151+ const display =
152+ ref . type === "command"
153+ ? ref . display
154+ : ref . type === "page"
155+ ? getPageTitleByPageUid ( ref . display )
156+ : getTextByBlockUid ( ref . uid ) ;
157+ const label = alias || truncate ( display , truncateAt ) ;
158+ const onClick = ( e : React . MouseEvent ) => {
159+ return void openTarget ( e , child . text , onloadArgs ) ;
160+ } ;
161+ return (
162+ < div className = "pl-8 pr-2.5" >
163+ { ref . type === "command" ? (
164+ < span className = "bp3-dark" >
165+ < Button onClick = { onClick } minimal className = "m-px" >
166+ { cleanCommandName ( label ) }
167+ </ Button >
168+ </ span >
169+ ) : (
170+ < div
171+ className = "section-child-item page cursor-pointer rounded-sm leading-normal text-gray-600"
172+ onClick = { onClick }
173+ >
174+ { label }
175+ </ div >
176+ ) }
177+ </ div >
178+ ) ;
179+ } ;
180+
135181const SectionChildren = ( {
136182 childrenNodes,
137183 truncateAt,
138184 onloadArgs,
139185} : {
140- childrenNodes : { uid : string ; text : string ; alias ?: { value : string } } [ ] ;
186+ childrenNodes : ChildNode [ ] ;
141187 truncateAt ?: number ;
142188 onloadArgs : OnloadArgs ;
143189} ) => {
144190 if ( ! childrenNodes ?. length ) return null ;
145191 return (
146192 < >
147- { childrenNodes . map ( ( child ) => {
148- const ref = parseReference ( child . text ) ;
149- const alias = child . alias ?. value ;
150- const display =
151- ref . type === "command"
152- ? ref . display
153- : ref . type === "page"
154- ? getPageTitleByPageUid ( ref . display )
155- : getTextByBlockUid ( ref . uid ) ;
156- const label = alias || truncate ( display , truncateAt ) ;
157- const onClick = ( e : React . MouseEvent ) => {
158- return void openTarget ( e , child . text , onloadArgs ) ;
159- } ;
160- return (
161- < div key = { child . uid } className = "pl-8 pr-2.5" >
162- { ref . type === "command" ? (
163- < span className = "bp3-dark" >
164- < Button onClick = { onClick } minimal className = "m-px" >
165- { cleanCommandName ( label ) }
166- </ Button >
167- </ span >
168- ) : (
169- < div
170- className = {
171- "section-child-item page cursor-pointer rounded-sm leading-normal text-gray-600"
172- }
173- onClick = { onClick }
174- >
175- { label }
176- </ div >
177- ) }
178- </ div >
179- ) ;
180- } ) }
193+ { childrenNodes . map ( ( child ) => (
194+ < ChildRow
195+ key = { child . uid }
196+ child = { child }
197+ truncateAt = { truncateAt }
198+ onloadArgs = { onloadArgs }
199+ />
200+ ) ) }
181201 </ >
182202 ) ;
183203} ;
184204
185205const PersonalSectionItem = ( {
186206 section,
207+ dragHandle,
208+ onChildrenReorder,
187209 onloadArgs,
188210} : {
189211 section : LeftSidebarPersonalSectionConfig ;
212+ dragHandle : SortableHandle ;
213+ onChildrenReorder : ( args : {
214+ sectionUid : string ;
215+ oldIndex : number ;
216+ newIndex : number ;
217+ } ) => void ;
190218 onloadArgs : OnloadArgs ;
191219} ) => {
192220 const titleRef = parseReference ( section . text ) ;
@@ -213,7 +241,11 @@ const PersonalSectionItem = ({
213241
214242 return (
215243 < >
216- < div className = "sidebar-title-button flex w-full cursor-pointer items-center border-none bg-transparent pl-6 pr-2.5 font-semibold outline-none" >
244+ < div
245+ { ...dragHandle . attributes }
246+ { ...dragHandle . listeners }
247+ className = "sidebar-title-button flex w-full cursor-pointer items-center border-none bg-transparent pl-6 pr-2.5 font-semibold outline-none"
248+ >
217249 < div className = "flex w-full items-center justify-between" >
218250 < div
219251 className = "flex items-center"
@@ -236,10 +268,21 @@ const PersonalSectionItem = ({
236268 </ div >
237269 </ div >
238270 < Collapse isOpen = { isOpen } >
239- < SectionChildren
240- childrenNodes = { section . children || [ ] }
241- truncateAt = { truncateAt }
242- onloadArgs = { onloadArgs }
271+ < SortableList
272+ items = { section . children || [ ] }
273+ getId = { ( c ) => c . uid }
274+ onReorder = { ( oldIndex , newIndex ) =>
275+ onChildrenReorder ( { sectionUid : section . uid , oldIndex, newIndex } )
276+ }
277+ renderItem = { ( child , handle ) => (
278+ < div { ...handle . attributes } { ...handle . listeners } >
279+ < ChildRow
280+ child = { child }
281+ truncateAt = { truncateAt }
282+ onloadArgs = { onloadArgs }
283+ />
284+ </ div >
285+ ) }
243286 />
244287 </ Collapse >
245288 </ >
@@ -248,31 +291,92 @@ const PersonalSectionItem = ({
248291
249292const PersonalSections = ( {
250293 config,
294+ setConfig,
251295 onloadArgs,
252296} : {
253297 config : LeftSidebarConfig ;
298+ setConfig : Dispatch < SetStateAction < LeftSidebarConfig > > ;
254299 onloadArgs : OnloadArgs ;
255300} ) => {
256301 const sections = config . personal . sections || [ ] ;
257302
258303 if ( ! sections . length ) return null ;
259304
305+ const reorderSections = ( oldIndex : number , newIndex : number ) => {
306+ const moved = sections [ oldIndex ] ;
307+ if ( ! moved ) return ;
308+ const reordered = arrayMove ( sections , oldIndex , newIndex ) ;
309+ setConfig ( {
310+ ...config ,
311+ personal : { ...config . personal , sections : reordered } ,
312+ } ) ;
313+ void moveRoamBlockToIndex ( {
314+ blockUid : moved . uid ,
315+ parentUid : config . personal . uid ,
316+ sourceIndex : oldIndex ,
317+ destIndex : newIndex ,
318+ } ) . then ( ( ) => {
319+ refreshAndNotify ( ) ;
320+ } ) ;
321+ } ;
322+
323+ const reorderChildren = ( {
324+ sectionUid,
325+ oldIndex,
326+ newIndex,
327+ } : {
328+ sectionUid : string ;
329+ oldIndex : number ;
330+ newIndex : number ;
331+ } ) => {
332+ const section = sections . find ( ( s ) => s . uid === sectionUid ) ;
333+ const children = section ?. children ;
334+ if ( ! section || ! children || ! section . childrenUid ) return ;
335+ const child = children [ oldIndex ] ;
336+ if ( ! child ) return ;
337+ const reorderedChildren = arrayMove ( children , oldIndex , newIndex ) ;
338+ const newSections = sections . map ( ( s ) =>
339+ s . uid === sectionUid ? { ...s , children : reorderedChildren } : s ,
340+ ) ;
341+ setConfig ( {
342+ ...config ,
343+ personal : { ...config . personal , sections : newSections } ,
344+ } ) ;
345+ void moveRoamBlockToIndex ( {
346+ blockUid : child . uid ,
347+ parentUid : section . childrenUid ,
348+ sourceIndex : oldIndex ,
349+ destIndex : newIndex ,
350+ } ) . then ( ( ) => {
351+ refreshAndNotify ( ) ;
352+ } ) ;
353+ } ;
354+
260355 return (
261- < div className = "personal-left-sidebar-sections" >
262- { sections . map ( ( section ) => (
263- < div key = { section . uid } >
264- < PersonalSectionItem section = { section } onloadArgs = { onloadArgs } />
265- </ div >
266- ) ) }
267- </ div >
356+ < SortableList
357+ items = { sections }
358+ getId = { ( s ) => s . uid }
359+ onReorder = { reorderSections }
360+ className = "personal-left-sidebar-sections"
361+ renderItem = { ( section , handle ) => (
362+ < PersonalSectionItem
363+ section = { section }
364+ dragHandle = { handle }
365+ onChildrenReorder = { reorderChildren }
366+ onloadArgs = { onloadArgs }
367+ />
368+ ) }
369+ />
268370 ) ;
269371} ;
270372
271373const GlobalSection = ( {
272374 config,
375+ onGlobalChildrenReorder,
273376 onloadArgs,
274377} : {
275378 config : LeftSidebarConfig [ "global" ] ;
379+ onGlobalChildrenReorder : ( oldIndex : number , newIndex : number ) => void ;
276380 onloadArgs : OnloadArgs ;
277381} ) => {
278382 const [ isOpen , setIsOpen ] = useState < boolean > (
@@ -281,6 +385,19 @@ const GlobalSection = ({
281385 if ( ! config . children ?. length ) return null ;
282386 const isCollapsable = config . settings ?. collapsable . value ;
283387
388+ const children = (
389+ < SortableList
390+ items = { config . children }
391+ getId = { ( c ) => c . uid }
392+ onReorder = { onGlobalChildrenReorder }
393+ renderItem = { ( child , handle ) => (
394+ < div { ...handle . attributes } { ...handle . listeners } >
395+ < ChildRow child = { child } onloadArgs = { onloadArgs } />
396+ </ div >
397+ ) }
398+ />
399+ ) ;
400+
284401 return (
285402 < >
286403 < div
@@ -305,17 +422,9 @@ const GlobalSection = ({
305422 </ div >
306423 </ div >
307424 { isCollapsable ? (
308- < Collapse isOpen = { isOpen } >
309- < SectionChildren
310- childrenNodes = { config . children }
311- onloadArgs = { onloadArgs }
312- />
313- </ Collapse >
425+ < Collapse isOpen = { isOpen } > { children } </ Collapse >
314426 ) : (
315- < SectionChildren
316- childrenNodes = { config . children }
317- onloadArgs = { onloadArgs }
318- />
427+ children
319428 ) }
320429 </ >
321430 ) ;
@@ -453,13 +562,41 @@ const FavoritesPopover = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
453562} ;
454563
455564const LeftSidebarView = ( { onloadArgs } : { onloadArgs : OnloadArgs } ) => {
456- const { config } = useConfig ( ) ;
565+ const { config, setConfig } = useConfig ( ) ;
566+
567+ const reorderGlobalChildren = ( oldIndex : number , newIndex : number ) => {
568+ const children = config . global . children ;
569+ if ( ! children ) return ;
570+ const moved = children [ oldIndex ] ;
571+ if ( ! moved ) return ;
572+ const reordered = arrayMove ( children , oldIndex , newIndex ) ;
573+ setConfig ( {
574+ ...config ,
575+ global : { ...config . global , children : reordered } ,
576+ } ) ;
577+ void moveRoamBlockToIndex ( {
578+ blockUid : moved . uid ,
579+ parentUid : config . global . childrenUid ,
580+ sourceIndex : oldIndex ,
581+ destIndex : newIndex ,
582+ } ) . then ( ( ) => {
583+ refreshAndNotify ( ) ;
584+ } ) ;
585+ } ;
457586
458587 return (
459588 < >
460589 < FavoritesPopover onloadArgs = { onloadArgs } />
461- < GlobalSection config = { config . global } onloadArgs = { onloadArgs } />
462- < PersonalSections config = { config } onloadArgs = { onloadArgs } />
590+ < GlobalSection
591+ config = { config . global }
592+ onGlobalChildrenReorder = { reorderGlobalChildren }
593+ onloadArgs = { onloadArgs }
594+ />
595+ < PersonalSections
596+ config = { config }
597+ setConfig = { setConfig }
598+ onloadArgs = { onloadArgs }
599+ />
463600 </ >
464601 ) ;
465602} ;
0 commit comments