@@ -82,6 +82,9 @@ django.jQuery(($) => {
82
82
ContentEditor . regions . forEach ( ( region ) => {
83
83
ContentEditor . regionsByKey [ region . key ] = region
84
84
} )
85
+ ContentEditor . hasSections = ContentEditor . plugins . some (
86
+ ( plugin ) => plugin . sections ,
87
+ )
85
88
86
89
// Add basic structure. There is always at least one inline group if
87
90
// we even have any plugins.
@@ -262,13 +265,110 @@ django.jQuery(($) => {
262
265
row [ 0 ] . classList . remove ( "selected" )
263
266
} )
264
267
window . __fs_dragging = null
268
+
269
+ updateSections ( )
265
270
}
266
271
} )
267
272
268
273
arg . find ( ">h3, .card-title" ) . attr ( "draggable" , true ) // Default admin, Jazzmin
269
274
arg . addClass ( "fs-draggable" )
270
275
}
271
276
277
+ function findInlinesInOrder ( context ) {
278
+ const inlines = ( context || orderMachine ) . find (
279
+ `.inline-related:not(.empty-form)[data-region="${ ContentEditor . currentRegion } ` ,
280
+ )
281
+ inlines . sort ( ( a , b ) => a . style . order - b . style . order )
282
+ return inlines
283
+ }
284
+
285
+ let sectionsMap = new Map ( )
286
+
287
+ function updateSections ( context ) {
288
+ /* Bail out early if we wouldn't do nothing anyway */
289
+ if ( ! ContentEditor . hasSections ) return
290
+
291
+ const inlines = findInlinesInOrder ( context )
292
+
293
+ let indent = 0
294
+ let nextIndent
295
+ const stack = [ ]
296
+ const wrapper = orderMachineWrapper [ 0 ]
297
+ const wrapperRect = wrapper . getBoundingClientRect ( )
298
+
299
+ const newSectionsMap = new Map ( )
300
+
301
+ function closeSection ( atInline ) {
302
+ const fromInline = stack . pop ( )
303
+ const from = fromInline . getBoundingClientRect ( )
304
+ const until = atInline . getBoundingClientRect ( )
305
+
306
+ let div = sectionsMap . get ( fromInline )
307
+ if ( div ) {
308
+ sectionsMap . delete ( fromInline )
309
+ } else {
310
+ div = document . createElement ( "div" )
311
+ div . classList . add ( "order-machine-section" )
312
+ wrapper . prepend ( div )
313
+ }
314
+
315
+ newSectionsMap . set ( fromInline , div )
316
+ div . style . top = `${ from . top - wrapperRect . top - 5 } px`
317
+ div . style . left = `${ from . left - wrapperRect . left - 5 } px`
318
+ div . style . right = "5px"
319
+ div . style . height = `${ until . top - from . top + until . height + 10 } px`
320
+ }
321
+
322
+ for ( const inline of inlines ) {
323
+ const prefix = inline . id . replace ( / - [ 0 - 9 ] + $ / , "" )
324
+ inline . style . marginInlineStart = `${ 30 * indent } px`
325
+ nextIndent = Math . max (
326
+ 0 ,
327
+ indent + ContentEditor . pluginsByPrefix [ prefix ] . sections ,
328
+ )
329
+
330
+ while ( indent < nextIndent ) {
331
+ stack . push ( inline )
332
+ ++ indent
333
+ }
334
+
335
+ while ( indent > nextIndent ) {
336
+ closeSection ( inline )
337
+ -- indent
338
+ }
339
+
340
+ indent = nextIndent
341
+ }
342
+
343
+ while ( stack . length ) {
344
+ closeSection ( inlines [ inlines . length - 1 ] )
345
+ }
346
+
347
+ for ( const section of sectionsMap . values ( ) ) {
348
+ section . remove ( )
349
+ }
350
+ sectionsMap = newSectionsMap
351
+ }
352
+
353
+ if ( ContentEditor . hasSections ) {
354
+ /* From https://www.freecodecamp.org/news/javascript-debounce-example/ */
355
+ function debounce ( func , timeout = 300 ) {
356
+ let timer
357
+ return ( ...args ) => {
358
+ clearTimeout ( timer )
359
+ timer = setTimeout ( ( ) => {
360
+ func . apply ( this , args )
361
+ } , timeout )
362
+ }
363
+ }
364
+ const debouncedIndentInlines = debounce ( updateSections , 10 )
365
+
366
+ const resizeObserver = new ResizeObserver ( ( entries ) => {
367
+ debouncedIndentInlines ( )
368
+ } )
369
+ resizeObserver . observe ( orderMachineWrapper [ 0 ] )
370
+ }
371
+
272
372
function reorderInlines ( context ) {
273
373
const inlines = ( context || orderMachine ) . find ( ".inline-related" )
274
374
inlines . not ( ".empty-form" ) . each ( function ( ) {
@@ -512,12 +612,13 @@ django.jQuery(($) => {
512
612
e . target . classList . add ( "selected" )
513
613
514
614
const pos = e . target . getBoundingClientRect ( )
615
+ const wrapperRect = orderMachineWrapper [ 0 ] . getBoundingClientRect ( )
515
616
const buttons = qs ( ".plugin-buttons" )
516
- buttons . style . left = `${ pos . left + window . scrollX + 30 } px`
617
+ buttons . style . left = `${ pos . left - wrapperRect . left + 30 } px`
517
618
518
619
const y =
519
- pos . top +
520
- window . scrollY +
620
+ pos . top -
621
+ wrapperRect . top +
521
622
( e . target . classList . contains ( "last" )
522
623
? 30 - buttons . getBoundingClientRect ( ) . height
523
624
: 0 )
@@ -554,6 +655,8 @@ django.jQuery(($) => {
554
655
$ ( document ) . trigger ( "content-editor:activate" , [ $row ] )
555
656
556
657
$row . find ( "input, select, textarea" ) . first ( ) . focus ( )
658
+
659
+ updateSections ( )
557
660
}
558
661
559
662
function handleFormsetRemoved ( prefix ) {
@@ -580,6 +683,8 @@ django.jQuery(($) => {
580
683
. each ( function ( ) {
581
684
$ ( document ) . trigger ( "content-editor:activate" , [ $ ( this ) ] )
582
685
} )
686
+
687
+ updateSections ( )
583
688
} , 0 )
584
689
}
585
690
@@ -623,6 +728,8 @@ django.jQuery(($) => {
623
728
624
729
// Make sure only allowed plugins are in select
625
730
hideNotAllowedPluginButtons ( )
731
+
732
+ updateSections ( )
626
733
} )
627
734
628
735
const collapseAllInput = $ ( ".collapse-items input" )
0 commit comments