From 5381373c7113f90710ad1e5241ad5a108651d37f Mon Sep 17 00:00:00 2001 From: Yair Even Or Date: Sat, 2 Oct 2021 23:20:36 +0300 Subject: [PATCH] 1.1.0 --- dist/position.js | 23 +++++++++++++++++++---- dist/position.umd.js | 27 +++++++++++++++++---------- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/dist/position.js b/dist/position.js index 45ec55f..e7e1d66 100644 --- a/dist/position.js +++ b/dist/position.js @@ -1,7 +1,7 @@ /** * @yaireo/position - Position a DOM element at a certain X,Y or next to another element * - * @version v1.0.7 + * @version v1.1.0 * @homepage https://jsbin.com/beqosub/edit?html,css,output */ @@ -14,8 +14,8 @@ * @param {Array} offset distance (in pixels) from original placement position ("10px 20px" or just "10px" for both horizontal & vertical) */ - const position = props => { - var {target, ref, offset, placement, prevPlacement, useRaf = true} = props, +const position = props => { + var {target, ref, offset, placement, prevPlacement, useRaf = true, track} = props, pos = {x:ref.x, y:ref.y, h:0, w:0}, refRect = (ref && ref.x) ? {...ref} : {}, refWindow, @@ -81,7 +81,7 @@ target.setAttribute('data-placement', placement.join(' ')); target.setAttribute('data-pos-overflow', Object.entries(overflow).reduce((acc, [k,v]) => v ? `${acc} ${k}` : acc , '').trim()); [ - ['pos-left', pos.x], // overflow.right ? vpSize.w - targetSize.w : pos.x + ['pos-left', overflow.right ? vpSize.w - targetSize.w : pos.x], ['pos-top', pos.y], // pos.y > offset[1] ? pos.y : 0 ['pos-target-width', targetSize.w], ['pos-target-height', targetSize.h], @@ -94,6 +94,21 @@ ].forEach(([k,v]) => target.style.setProperty('--'+k, Math.round(v))) }) + // auto-reposition on any ancestor scroll + if( track?.scroll && !target.position__trackedScroll ){ + // mark the node as tracked + target.position__trackedScroll = true; + + // if any ancestor of refElement was scrolled, re-position the target + window.addEventListener('scroll', onScroll, true) + + function onScroll(e){ + // make sure the scrolled element contains the ref element + if( e.target.contains(refElement) ) + position(props) + } + } + return {pos, placement} } diff --git a/dist/position.umd.js b/dist/position.umd.js index 18ba235..4b0829a 100644 --- a/dist/position.umd.js +++ b/dist/position.umd.js @@ -1,7 +1,14 @@ /** * @yaireo/position - Position a DOM element at a certain X,Y or next to another element * - * @version v1.0.7 + * @version v1.1.0 + * @homepage https://jsbin.com/beqosub/edit?html,css,output + */ + +/** + * @yaireo/position - Position a DOM element at a certain X,Y or next to another element + * + * @version v1.1.0 * @homepage https://jsbin.com/beqosub/edit?html,css,output */ @@ -12,13 +19,13 @@ * @param {String} placement [above/below/center & left/right/center] or mix of two (only works if "ref" is an HTML Element) * @param {Array} prevPlacement used when calculated new position overflows * @param {Array} offset distance (in pixels) from original placement position ("10px 20px" or just "10px" for both horizontal & vertical) - */const c=a=>{var e,{target:f,ref:g,offset:h,placement:i,prevPlacement:j,useRaf:k=!0}=a,l={x:g.x,y:g.y,h:0,w:0},m=g&&g.x?{...g}:{},n=document.documentElement,o={w:n.clientWidth,h:n.clientHeight},p={w:f.clientWidth,h:f.clientHeight};// [horizontal, vertical] + */const c=a=>{var e,{target:f,ref:g,offset:h,placement:i,prevPlacement:j,useRaf:l=!0,track:k}=a,m={x:g.x,y:g.y,h:0,w:0},n=g&&g.x?{...g}:{},o=document.documentElement,p={w:o.clientWidth,h:o.clientHeight},q={w:f.clientWidth,h:f.clientHeight};// [horizontal, vertical] // if "ref" is a DOM element, get [x,y] coordinates and adjust according to desired placement -if(d=k?d:a=>a(),j=j||[],i=(i||" ").split(" ").map((b,a)=>b?b:["center","below"][a]),h=h?[h[0]||0,h[1]||h[0]||0]:[0,0],g.parentNode&&(e=g.ownerDocument.defaultView,m=g.getBoundingClientRect(),l.x=m.x,l.y=m.y,l.w=m.width,l.h=m.height,e!=e.parent))// if ref element is within an iframe, get it's position relative to the viewport and not its local window -for(let a of e.parent.document.getElementsByTagName("iframe"))if(a.contentWindow===e){let b=a.getBoundingClientRect();l.x+=b.x,l.y+=b.y}// horizontal -"left"==i[0]?l.x-=p.w+h[0]:"right"==i[0]?l.x+=l.w+h[0]:l.x-=p.w/2-l.w/2,"above"==i[1]?l.y-=p.h+h[1]:"below"==i[1]?l.y+=l.h+h[1]:l.y-=p.h/2-l.h/2;const q={top:0>l.y,bottom:l.y+p.h>o.h,left:0>l.x,right:l.x+p.w>o.w},r=b=>c({...a,placement:b.join(" "),prevPlacement:i});// horizontal fix for overflows -return q.left&&"right"!=j[0]?r(["right",i[1]]):q.right&&"left"!=j[0]?r(["left",i[1]]):q.bottom&&"above"!=j[1]?r([i[0],"above"]):q.top&&"below"!=j[1]?r([i[0],"below"]):(d(()=>{f.setAttribute("positioned",!0),f.setAttribute("data-placement",i.join(" ")),f.setAttribute("data-pos-overflow",Object.entries(q).reduce((a,[b,c])=>c?`${a} ${b}`:a,"").trim()),[["pos-left",l.x],// overflow.right ? vpSize.w - targetSize.w : pos.x -["pos-top",l.y],// pos.y > offset[1] ? pos.y : 0 -["pos-target-width",p.w],["pos-target-height",p.h],["pos-ref-width",m.width||0],["pos-ref-height",m.height||0],["pos-ref-left",m.x],["pos-ref-top",m.y],["window-scroll-y",window.scrollY],["window-scroll-x",window.scrollX]].forEach(([a,c])=>f.style.setProperty("--"+a,b(c)))}),{pos:l,placement:i});// vertical fix for overflows -// update target's position -};let d=requestAnimationFrame||(a=>setTimeout(a,1e3/60));a.default=c}); +if(d=l?d:a=>a(),j=j||[],i=(i||" ").split(" ").map((b,a)=>b?b:["center","below"][a]),h=h?[h[0]||0,h[1]||h[0]||0]:[0,0],g.parentNode&&(e=g.ownerDocument.defaultView,n=g.getBoundingClientRect(),m.x=n.x,m.y=n.y,m.w=n.width,m.h=n.height,e!=e.parent))// if ref element is within an iframe, get it's position relative to the viewport and not its local window +for(let a of e.parent.document.getElementsByTagName("iframe"))if(a.contentWindow===e){let b=a.getBoundingClientRect();m.x+=b.x,m.y+=b.y}// horizontal +"left"==i[0]?m.x-=q.w+h[0]:"right"==i[0]?m.x+=m.w+h[0]:m.x-=q.w/2-m.w/2,"above"==i[1]?m.y-=q.h+h[1]:"below"==i[1]?m.y+=m.h+h[1]:m.y-=q.h/2-m.h/2;const r={top:0>m.y,bottom:m.y+q.h>p.h,left:0>m.x,right:m.x+q.w>p.w},s=b=>c({...a,placement:b.join(" "),prevPlacement:i});// horizontal fix for overflows +if(r.left&&"right"!=j[0])return s(["right",i[1]]);if(r.right&&"left"!=j[0])return s(["left",i[1]]);// vertical fix for overflows +if(r.bottom&&"above"!=j[1])return s([i[0],"above"]);if(r.top&&"below"!=j[1])return s([i[0],"below"]);// update target's position +// auto-reposition on any ancestor scroll +if(d(()=>{f.setAttribute("positioned",!0),f.setAttribute("data-placement",i.join(" ")),f.setAttribute("data-pos-overflow",Object.entries(r).reduce((a,[b,c])=>c?`${a} ${b}`:a,"").trim()),[["pos-left",r.right?p.w-q.w:m.x],["pos-top",m.y],// pos.y > offset[1] ? pos.y : 0 +["pos-target-width",q.w],["pos-target-height",q.h],["pos-ref-width",n.width||0],["pos-ref-height",n.height||0],["pos-ref-left",n.x],["pos-ref-top",n.y],["window-scroll-y",window.scrollY],["window-scroll-x",window.scrollX]].forEach(([a,c])=>f.style.setProperty("--"+a,b(c)))}),k?.scroll&&!f.position__trackedScroll){f.position__trackedScroll=!0,window.addEventListener("scroll",function(b){b.target.contains(refElement)&&c(a)},!0)}return{pos:m,placement:i}};let d=requestAnimationFrame||(a=>setTimeout(a,1e3/60));a.default=c}); diff --git a/package-lock.json b/package-lock.json index a80c220..1e3aeae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yaireo/position", - "version": "1.0.7", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@yaireo/position", - "version": "1.0.7", + "version": "1.1.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.15.7", diff --git a/package.json b/package.json index 0d6d010..a1e5908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yaireo/position", - "version": "1.0.7", + "version": "1.1.0", "homepage": "https://jsbin.com/beqosub/edit?html,css,output", "description": "Position a DOM element at a certain X,Y or next to another element", "keywords": [