@@ -7,6 +7,17 @@ import React, {
77 useState ,
88} from "react" ;
99import ReactDOM from "react-dom" ;
10+ import {
11+ DragDropContext ,
12+ Droppable ,
13+ Draggable ,
14+ DropResult ,
15+ DraggableProvided ,
16+ DraggableStateSnapshot ,
17+ DroppableProvided ,
18+ DragStart ,
19+ DraggableRubric ,
20+ } from "@hello-pangea/dnd" ;
1021import {
1122 Collapse ,
1223 Icon ,
@@ -147,8 +158,10 @@ const SectionChildren = ({
147158
148159const PersonalSectionItem = ( {
149160 section,
161+ activeDragSourceId,
150162} : {
151163 section : LeftSidebarPersonalSectionConfig ;
164+ activeDragSourceId : string | null ;
152165} ) => {
153166 const titleRef = parseReference ( section . text ) ;
154167 const blockText = useMemo (
@@ -160,7 +173,33 @@ const PersonalSectionItem = ({
160173 const [ isOpen , setIsOpen ] = useState < boolean > (
161174 ! ! section . settings ?. folded . value || false ,
162175 ) ;
163- const alias = section . settings ?. alias ?. value ;
176+
177+ const renderChild = (
178+ dragProvided : DraggableProvided ,
179+ child : { text : string ; uid : string } ,
180+ ) => {
181+ const ref = parseReference ( child . text ) ;
182+ const label = truncate ( ref . display , truncateAt ) ;
183+ const onClick = ( e : React . MouseEvent ) => {
184+ return void openTarget ( e , child . text ) ;
185+ } ;
186+ return (
187+ < div
188+ ref = { dragProvided . innerRef }
189+ { ...dragProvided . draggableProps }
190+ { ...dragProvided . dragHandleProps }
191+ style = { dragProvided . draggableProps . style }
192+ className = "pl-8 pr-2.5"
193+ >
194+ < div
195+ className = "section-child-item page cursor-pointer rounded-sm leading-normal text-gray-600"
196+ onClick = { onClick }
197+ >
198+ { label }
199+ </ div >
200+ </ div >
201+ ) ;
202+ } ;
164203
165204 const handleChevronClick = ( ) => {
166205 if ( ! section . settings ) return ;
@@ -187,7 +226,7 @@ const PersonalSectionItem = ({
187226 }
188227 } }
189228 >
190- { ( alias || blockText || titleRef . display ) . toUpperCase ( ) }
229+ { ( blockText || titleRef . display ) . toUpperCase ( ) }
191230 </ div >
192231 { ( section . children ?. length || 0 ) > 0 && (
193232 < span
@@ -200,28 +239,162 @@ const PersonalSectionItem = ({
200239 </ div >
201240 </ div >
202241 < Collapse isOpen = { isOpen } >
203- < SectionChildren
204- childrenNodes = { section . children || [ ] }
205- truncateAt = { truncateAt }
206- />
242+ < Droppable
243+ droppableId = { section . uid }
244+ type = "ITEMS"
245+ isDropDisabled = {
246+ ! ! activeDragSourceId && activeDragSourceId !== section . uid
247+ }
248+ renderClone = { (
249+ provided : DraggableProvided ,
250+ _ : DraggableStateSnapshot ,
251+ rubric : DraggableRubric ,
252+ ) => {
253+ const child = ( section . children || [ ] ) [ rubric . source . index ] ;
254+ return renderChild ( provided , child ) ;
255+ } }
256+ >
257+ { ( provided : DroppableProvided ) => (
258+ < div ref = { provided . innerRef } { ...provided . droppableProps } >
259+ { ( section . children || [ ] ) . map ( ( child , index ) => (
260+ < Draggable
261+ key = { child . uid }
262+ draggableId = { child . uid }
263+ index = { index }
264+ isDragDisabled = { ( section . children || [ ] ) . length <= 1 }
265+ >
266+ { ( dragProvided : DraggableProvided ) => {
267+ return renderChild ( dragProvided , child ) ;
268+ } }
269+ </ Draggable >
270+ ) ) }
271+ { provided . placeholder }
272+ </ div >
273+ ) }
274+ </ Droppable >
207275 </ Collapse >
208276 </ >
209277 ) ;
210278} ;
211279
212280const PersonalSections = ( {
213281 config,
282+ setConfig,
214283} : {
215- config : LeftSidebarConfig [ "personal" ] ;
284+ config : LeftSidebarConfig ;
285+ setConfig : Dispatch < SetStateAction < LeftSidebarConfig > > ;
216286} ) => {
217- const sections = config . sections || [ ] ;
287+ const sections = config . personal . sections || [ ] ;
288+ const [ activeDragSourceId , setActiveDragSourceId ] = useState < string | null > (
289+ null ,
290+ ) ;
291+
218292 if ( ! sections . length ) return null ;
293+
294+ const handleDragStart = ( start : DragStart ) => {
295+ if ( start . type === "ITEMS" ) {
296+ setActiveDragSourceId ( start . source . droppableId ) ;
297+ }
298+ } ;
299+
300+ const handleDragEnd = ( result : DropResult ) => {
301+ setActiveDragSourceId ( null ) ;
302+ const { source, destination, type } = result ;
303+
304+ if ( ! destination ) return ;
305+
306+ if ( type === "SECTIONS" ) {
307+ if ( destination . index === source . index ) return ;
308+
309+ const newPersonalSections = Array . from ( config . personal . sections ) ;
310+ const [ removed ] = newPersonalSections . splice ( source . index , 1 ) ;
311+ newPersonalSections . splice ( destination . index , 0 , removed ) ;
312+
313+ setConfig ( {
314+ ...config ,
315+ personal : { ...config . personal , sections : newPersonalSections } ,
316+ } ) ;
317+ const finalIndex =
318+ destination . index > source . index
319+ ? destination . index + 1
320+ : destination . index ;
321+ void window . roamAlphaAPI . moveBlock ( {
322+ location : { "parent-uid" : config . personal . uid , order : finalIndex } ,
323+ block : { uid : removed . uid } ,
324+ } ) ;
325+ return ;
326+ }
327+
328+ if ( type === "ITEMS" ) {
329+ if ( source . droppableId !== destination . droppableId ) {
330+ return ;
331+ }
332+
333+ if ( destination . index === source . index ) {
334+ return ;
335+ }
336+
337+ const newConfig = JSON . parse ( JSON . stringify ( config ) ) as LeftSidebarConfig ;
338+ const { personal } = newConfig ;
339+
340+ const listToReorder = personal . sections . find (
341+ ( s ) => s . uid === source . droppableId ,
342+ ) ;
343+ const parentUid = listToReorder ?. childrenUid ;
344+ const listToReorderChildren = listToReorder ?. children ;
345+
346+ if ( ! listToReorderChildren ) return ;
347+
348+ const [ removedItem ] = listToReorderChildren . splice ( source . index , 1 ) ;
349+ listToReorderChildren . splice ( destination . index , 0 , removedItem ) ;
350+
351+ setConfig ( newConfig ) ;
352+ const finalIndex =
353+ destination . index > source . index
354+ ? destination . index + 1
355+ : destination . index ;
356+ void window . roamAlphaAPI . moveBlock ( {
357+ location : { "parent-uid" : parentUid || "" , order : finalIndex } ,
358+ block : { uid : removedItem . uid } ,
359+ } ) ;
360+ }
361+ } ;
219362 return (
220- < div className = "personal-left-sidebar-sections" >
221- { sections . map ( ( s ) => (
222- < PersonalSectionItem key = { s . uid } section = { s } />
223- ) ) }
224- </ div >
363+ < DragDropContext onDragEnd = { handleDragEnd } onDragStart = { handleDragStart } >
364+ < Droppable droppableId = "personal-sections" type = "SECTIONS" >
365+ { ( provided : DroppableProvided ) => (
366+ < div
367+ ref = { provided . innerRef }
368+ { ...provided . droppableProps }
369+ className = "personal-left-sidebar-sections"
370+ >
371+ { sections . map ( ( section , index ) => (
372+ < Draggable
373+ key = { section . uid }
374+ draggableId = { section . uid }
375+ index = { index }
376+ isDragDisabled = { sections . length <= 1 }
377+ >
378+ { ( dragProvided : DraggableProvided ) => (
379+ < div
380+ ref = { dragProvided . innerRef }
381+ { ...dragProvided . draggableProps }
382+ { ...dragProvided . dragHandleProps }
383+ style = { dragProvided . draggableProps . style }
384+ >
385+ < PersonalSectionItem
386+ section = { section }
387+ activeDragSourceId = { activeDragSourceId }
388+ />
389+ </ div >
390+ ) }
391+ </ Draggable >
392+ ) ) }
393+ { provided . placeholder }
394+ </ div >
395+ ) }
396+ </ Droppable >
397+ </ DragDropContext >
225398 ) ;
226399} ;
227400
@@ -312,7 +485,6 @@ const FavouritesPopover = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
312485
313486 useEffect ( ( ) => {
314487 if ( ! isMenuOpen ) return ;
315- console . log ( "handleGlobalPointerDownCapture" ) ;
316488 const opts = { capture : true } as AddEventListenerOptions ;
317489 window . addEventListener (
318490 "mousedown" ,
@@ -336,7 +508,7 @@ const FavouritesPopover = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
336508 opts ,
337509 ) ;
338510 } ;
339- } , [ handleGlobalPointerDownCapture ] ) ;
511+ } , [ handleGlobalPointerDownCapture , isMenuOpen ] ) ;
340512
341513 const renderSettingsDialog = ( tabId : TabId ) => {
342514 renderOverlay ( {
@@ -400,12 +572,13 @@ const FavouritesPopover = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
400572} ;
401573
402574const LeftSidebarView = ( { onloadArgs } : { onloadArgs : OnloadArgs } ) => {
403- const config = useConfig ( ) ;
575+ const initialConfig = useConfig ( ) ;
576+ const [ config , setConfig ] = useState ( initialConfig ) ;
404577 return (
405578 < >
406579 < FavouritesPopover onloadArgs = { onloadArgs } />
407580 < GlobalSection config = { config . global } />
408- < PersonalSections config = { config . personal } />
581+ < PersonalSections config = { config } setConfig = { setConfig } />
409582 </ >
410583 ) ;
411584} ;
0 commit comments