11import { registerBehavior } from '@semantic-ui/query' ;
2- import { each , inArray , isArray , isNumber , isString , range , throttle , wrapFunction } from '@semantic-ui/utils' ;
2+ import {
3+ each ,
4+ idleCallback ,
5+ inArray ,
6+ isArray ,
7+ isNumber ,
8+ isString ,
9+ range ,
10+ throttle ,
11+ wrapFunction ,
12+ } from '@semantic-ui/utils' ;
313
414import css from './attach.css?raw' ;
515
@@ -34,12 +44,12 @@ const defaultSettings = {
3444 containToScroll : true , // whether to contain element to its scroll container
3545
3646 // throttle delays for performance optimization
37- scrollThrottle : 150 , // milliseconds to throttle scroll repositioning
47+ scrollThrottle : 15 , // milliseconds to throttle scroll repositioning
3848 resizeThrottle : 300 , // milliseconds to throttle resize repositioning
3949 throttleSettings : { leading : false , trailing : true } ,
4050} ;
4151
42- const createBehavior = ( { $, $el, el, self, attachEvent, cache, settings, classNames , error, debug, index , warn } ) => ( {
52+ const createBehavior = ( { $, $el, el, self, attachEvent, cache, settings, error, debug, warn } ) => ( {
4353 anchorName : null , // current anchor name
4454
4555 // available positions to try
@@ -59,7 +69,7 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
5969 ] ,
6070
6171 // current position
62- position : null ,
72+ position : undefined ,
6373
6474 // map of starting position to fallbacks using index of 'positions'
6575 // to make this less confusing it is 1-indexed.
@@ -126,12 +136,18 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
126136
127137 // Throttled repositioning methods
128138 onScroll : ( settings . scrollThrottle > 0 )
129- ? throttle ( ( ) => requestAnimationFrame ( self . reposition ) , settings . scrollThrottle , settings . throttleSettings )
130- : requestAnimationFrame ( self . reposition ) ,
139+ ? throttle (
140+ ( ) => {
141+ idleCallback ( self . reposition ) ;
142+ } ,
143+ settings . scrollThrottle ,
144+ settings . throttleSettings ,
145+ )
146+ : idleCallback ( self . reposition ) ,
131147
132148 onResize : ( settings . resizeThrottle > 0 )
133- ? throttle ( ( ) => requestAnimationFrame ( self . reposition ) , settings . resizeThrottle , settings . throttleSettings )
134- : requestAnimationFrame ( self . reposition ) ,
149+ ? throttle ( ( ) => idleCallback ( self . reposition ) , settings . resizeThrottle , settings . throttleSettings )
150+ : idleCallback ( self . reposition ) ,
135151
136152 getNextAnchorName ( ) {
137153 if ( ! cache . count ) {
@@ -179,8 +195,8 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
179195 o : '-50% -50%' , // offset
180196 } ;
181197
182- let css = self . positionMapping [ position ] || { } ;
183- let outputCSS = { } ;
198+ const css = self . positionMapping [ position ] || { } ;
199+ const outputCSS = { } ;
184200
185201 // Skip distance/offset for center position
186202 const isCenter = position === 'center' ;
@@ -256,8 +272,8 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
256272 return $el . computedStyle ( 'display' ) === 'none' ;
257273 } ,
258274
259- setPosition ( position = settings . position ) {
260- if ( self . isHidden ( ) ) {
275+ setPositioningCSS ( position = settings . position ) {
276+ if ( self . isHidden ( ) || position === 'hidden' ) {
261277 return ;
262278 }
263279 const positioningCSS = self . getPositioningCSS ( position ) ;
@@ -268,27 +284,40 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
268284 if ( self . isHidden ( ) ) {
269285 return ;
270286 }
271- self . setPosition ( position ) ;
287+ self . setPositioningCSS ( position ) ;
272288 self . maybeReposition ( position ) ;
273289 } ,
274290
291+ isCurrentlyPositioning ( ) {
292+ return self . fallbackPositions ?. length ;
293+ } ,
294+
275295 reposition ( ) {
276296 // current repositioning
277- if ( self . fallbackPositions ?. length ) {
297+ if ( self . isCurrentlyPositioning ( ) ) {
278298 debug ( 'Already searching' ) ;
279299 return ;
280300 }
301+ if ( self . currentPositionInView ( ) ) {
302+ return ;
303+ }
304+ // but begin from current position if defined
281305 self . testPosition ( settings . position ) ;
282306 } ,
283307
308+ currentPositionInView ( ) {
309+ return self . position && self . maybeReposition ( self . position ) === false ;
310+ } ,
311+
284312 // check if position is in view
285- maybeReposition ( currentPosition = settings . position ) {
313+ maybeReposition ( currentPosition = self . position ) {
314+ const $viewport = ( settings . containToScroll )
315+ ? $el . scrollParent ( )
316+ : $el . clippingParent ( ) ;
286317 const view = $el . isInView ( {
287318 fully : true ,
288319 returnDetails : true ,
289- viewport : ( settings . containToScroll )
290- ? $el . scrollParent ( )
291- : $el . clippingParent ( ) ,
320+ viewport : $viewport ,
292321 } ) ;
293322 if ( ! view . intersects ) {
294323 debug ( 'Position not in view' , currentPosition , view ) ;
@@ -300,14 +329,15 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
300329 }
301330 else {
302331 if ( settings . lastResort ) {
303- self . setPosition ( settings . lastResort ) ;
332+ self . setResolvedPosition ( settings . lastResort ) ;
304333 }
305334 else {
306- error ( 'No positions fit viewport' ) ;
335+ warn ( 'No positions fit viewport' ) ;
307336 }
308337 debug ( 'no positions left to test' ) ;
338+ self . endFallbackSearch ( ) ;
309339 }
310- return false ;
340+ return true ;
311341 }
312342 else if ( self . position !== currentPosition ) {
313343 self . setResolvedPosition ( currentPosition ) ;
@@ -329,7 +359,7 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
329359 }
330360 self . position = resolvedPosition ;
331361 debug ( 'Found position in view' , resolvedPosition ) ;
332- self . setPosition ( resolvedPosition ) ;
362+ self . setPositioningCSS ( resolvedPosition ) ;
333363 } ,
334364
335365 // fallback order is implemented as a lookup table
@@ -339,10 +369,10 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
339369 if ( isArray ( settings . fallbackStrategy ) ) {
340370 return settings . fallbackStrategy ;
341371 }
342- if ( settings . fallbackStrategy == 'adjacent' ) {
372+ if ( settings . fallbackStrategy === 'adjacent' ) {
343373 return self . adjacentFallbacks [ position ] || allPositions ;
344374 }
345- if ( settings . fallbackStrategy == 'opposite' ) {
375+ if ( settings . fallbackStrategy === 'opposite' ) {
346376 return self . oppositeFallbacks [ position ] || allPositions ;
347377 }
348378 return allPositions ;
@@ -359,7 +389,7 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
359389 self . fallbacksTested ++ ;
360390 if ( isNumber ( nextPosition ) ) {
361391 const index = nextPosition - 1 ; // positions are 1-indexed
362- debug ( 'Next position is' , nextPosition , self . positions [ index ] ) ;
392+ debug ( 'Next position is' , self . positions [ nextPosition ] ) ;
363393 return self . positions [ index ] || null ;
364394 }
365395 // if its a string its just the position name (user specified)
@@ -372,10 +402,26 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
372402 }
373403 } ,
374404
375- startFallbackSearch ( startingPosition ) {
405+ startFallbackSearch ( startingPosition , currentPosition ) {
406+ const rotateToFront = ( arr , value ) => {
407+ const index = arr . indexOf ( value ) ;
408+ if ( index === - 1 ) {
409+ return arr ;
410+ }
411+ return [ ...arr . slice ( index ) , ...arr . slice ( 0 , index ) ] ;
412+ } ;
376413 self . fallbacksTested = 0 ;
414+ let positions = self . getFallbackOrder ( startingPosition ) ;
415+
416+ // if we specify current position we want to process array but starting with current position
417+ // this makes sure if its still in view we use the current position
418+ if ( currentPosition ) {
419+ const positionNumber = self . positions . indexOf ( currentPosition ) ;
420+ if ( inArray ( positionNumber , positions ) ) {
421+ positions = rotateToFront ( positions , positionNumber ) ;
422+ }
423+ }
377424 // clone array
378- const positions = self . getFallbackOrder ( startingPosition ) ;
379425 self . fallbackPositions = [
380426 ...positions ,
381427 ] ;
@@ -392,7 +438,7 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
392438 const elParent = $el . positioningParent ( ) . el ( ) ;
393439
394440 // same positioning context
395- if ( anchorParent == elParent ) {
441+ if ( anchorParent === elParent ) {
396442 return ;
397443 }
398444
@@ -409,20 +455,24 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
409455 if ( ! self . anchorName ) {
410456 self . setAnchorName ( ) ;
411457 }
412- let attachCSS = {
458+ const attachCSS = {
413459 'position' : 'absolute' ,
414460 'position-anchor' : self . anchorName ,
415461 } ;
416462 $el . css ( attachCSS ) ;
417463 self . testPosition ( settings . position ) ;
418464 } ,
419465
466+ bindScroll ( ) {
467+ const $scrolls = $el . scrollParent ( { all : true } ) ;
468+ attachEvent ( $scrolls , 'scroll' , self . onScroll ) ;
469+ } ,
470+
420471 bindObservers ( ) {
421472 const viewport = settings . containToScroll
422473 ? $el . scrollParent ( )
423474 : $el . clippingParent ( ) ;
424475
425- console . log ( viewport . el ( ) ) ;
426476 self . observer = new IntersectionObserver (
427477 self . onIntersectionChange ,
428478 {
@@ -431,17 +481,9 @@ const createBehavior = ({ $, $el, el, self, attachEvent, cache, settings, classN
431481 rootMargin : '50px' ,
432482 } ,
433483 ) ;
434-
484+ el . offsetHeight ;
435485 self . observer . observe ( el ) ;
436486 } ,
437-
438- onIntersectionChange ( entries ) {
439- const entry = entries [ 0 ] ;
440- console . log ( entries ) ;
441- if ( ! entry . isIntersecting ) {
442- self . reposition ( ) ;
443- }
444- } ,
445487} ) ;
446488
447489const onCreated = ( { self, settings } ) => {
@@ -453,7 +495,7 @@ const onCreated = ({ self, settings }) => {
453495 self . maybeMoveElement ( ) ;
454496 }
455497 self . attach ( ) ;
456- self . bindObservers ( ) ;
498+ self . bindScroll ( ) ;
457499} ;
458500
459501const onDestroyed = ( { self } ) => {
0 commit comments