@localnerve/jump-scroll 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/jump-scroll.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
## Summary
|
|
10
10
|
|
|
11
|
-
A native
|
|
11
|
+
A native web component scrolling assistant that allows a user to jump to author defined page target sections.
|
|
12
|
+
Navigation options: [next, previous, first, last].
|
|
12
13
|
Non-browser module exports build helpers (for building CSP rules, etc).
|
|
13
14
|
|
|
14
15
|
## Web Size
|
|
@@ -16,7 +17,7 @@ Non-browser module exports build helpers (for building CSP rules, etc).
|
|
|
16
17
|
|
|
17
18
|
## Attributes
|
|
18
19
|
|
|
19
|
-
* `target` - *Required*. A selector that
|
|
20
|
+
* `target` - *Required*. A selector that defines all the target elements to vertically "jump scroll" to in a page. Defaults to `"section"`.
|
|
20
21
|
|
|
21
22
|
* `display` - *Optional*. Values: `"best" | "both"`. Defaults to `"best"`.
|
|
22
23
|
**"best"** - *Default*. The control displays **either** [top, previous] **OR** [bottom, next] jump scrolling control surface. Which one is displayed depends on the position on the page and the direction of scrolling. If the user is in the middle of the page and scrolls, the control only displays the jump scroll options in the direction of the scroll. If near the end, turns in the opposite direction. Less vertical space required.
|
package/dist/jump-scroll.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! jump-scroll@0.7.
|
|
1
|
+
/*! jump-scroll@0.7.4, Copyright (c) 2023 Alex Grant (alex@localnerve.com), LocalNerve LLC, BSD-3-Clause */
|
|
2
2
|
class t extends HTMLElement{#t=null;#e=null;#s=null;#r=!0;#i=!1;#o=0;#n="";#a=null;#l=null;#c=null;#h=null;#p=null;static#d=15;#u=null;#b=null;#g=null;#m=!1;#v=!1;#T=0;static#f=800;static#y=["target"];static#w={target:"section",display:"best",colormap:"",enablekeyboard:!0};static get observedAttributes(){return[...t.#y,"display","colormap","enablekeyboard"]}static getNumber(t){return parseFloat(t.replace(/[^.+\-\d]+/,""))}constructor(){super(),this.attachShadow({mode:"open"}),this.targetIntersection=this.targetIntersection.bind(this),this.controlIntersection=this.controlIntersection.bind(this),this.resizeHandler=this.resizeHandler.bind(this),this.setup=this.setup.bind(this),this.clickTop=this.clickTop.bind(this),this.clickBottom=this.clickBottom.bind(this),this.clickNext=this.clickNext.bind(this),this.clickPrev=this.clickPrev.bind(this),this.keydownHandler=this.keydownHandler.bind(this),this.#k()}#k(){t.#y.forEach((e=>{Object.defineProperty(this,e,{get(){return this.hasAttribute(e)?this.getAttribute(e):t.#w[e]},set(t){t?this.setAttribute(e,t):this.removeAttribute(e),this.#I(t)}})}))}get display(){const e="display";return this.hasAttribute(e)?this.getAttribute(e):t.#w[e]}set display(t){const e="display",s=["both","best"];this.#b&&this.#b.classList.remove(...s.concat("up","down")),s.includes(t)?(this.setAttribute(e,t),this.#A(),this.#x(),this.#b&&this.#b.classList.add(t)):this.removeAttribute(e)}get colormap(){const e="colormap";return this.hasAttribute(e)?this.getAttribute(e):t.#w[e]}set colormap(t){if(null===this.#b)return;this.#u&&this.#u.clear(),this.#u=null;const e="colormap",s=t&&t.split(";");if(s&&s.length>0&&s[0].includes("@")){this.setAttribute(e,t),this.#u=new Map;for(const t of s){let[e,s]=t.replace(/\s/,"").split("@");s.startsWith("--")&&(s=`var(${s})`),document.querySelectorAll(e).forEach((t=>{this.#u.set(t,s)}))}}else this.removeAttribute(e)}get currentTarget(){return this.#h}set currentTarget(t){this.#p.has(t)&&(this.#h=t,this.#C())}get enableKeyboard(){const e="enablekeyboard";return this.hasAttribute(e)?"false"!==this.getAttribute(e):t.#w[e]}keydownHandler(t){const e={PageDown:{true:this.clickBottom,false:this.clickNext},Space:{true:this.clickPrev,false:this.clickNext},PageUp:{true:this.clickTop,false:this.clickPrev}},s=e[t.code]?.[Boolean(t.shiftKey).toString()];s&&s(t)}set enableKeyboard(t){if(!this.#g)return;const e=t?"addEventListener":"removeEventListener";this.#g[e]("keydown",this.keydownHandler),this.setAttribute("enablekeyboard",t)}#S(t){const e=[t],s=["mid","start","end","rest","up","down"];null!==this.#a&&s.push("none"),"best"===this.display&&"mid"===t&&(this.#r?e.push("down"):e.push("up")),this.#b.classList.remove(...s),this.#b.classList.add(...e),setTimeout((()=>{this.#b.classList.add("rest")}),500)}#j(e,s){const r=this.#p.get(this.#h);if(r[e]){const i=r[e],o=this.#p.get(i),n=Math.round(o.lastTop);this.currentTarget=i,this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),setTimeout((()=>{n===Math.round(o.lastTop)&&(s(n)?this.#j(e,s):window.scrollBy({top:n,behavior:"smooth"}))}),16.67*t.#d)}else this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"})}#O(t="start"){this.currentTarget="start"===t?this.#a:this.#l,this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),this.#S(t)}clickTop(t){t&&t.preventDefault(),this.#O("start")}clickBottom(t){t&&t.preventDefault(),this.#O("end")}clickNext(t){t&&t.preventDefault(),this.#j("next",(t=>t<window.innerHeight))}clickPrev(t){t&&t.preventDefault(),this.#j("prev",(t=>t>0))}#E(){if(!this.#a||!this.#p)return;let t,e,s=1e6;const r=[],i=this.#p.keys();for(const o of i){for(e=o,t=0;e.parentElement;e=e.parentElement)t++;t<=s&&(s=t,r.unshift(o))}let o=r[0].parentElement??r[0];if(r.length>1){const t=new Range;t.setStart(r[0],0),t.setEnd(r[1],0),t.collapsed&&(t.setStart(r[1],0),t.setEnd(r[0],0)),o=t.commonAncestorContainer??o}return o}#z(){if(this.#a&&this.#p&&(this.#g=this.#E(),this.#g)){let t;if(this.#g.id)t=this.#g.id;else{const e=new Uint8Array(10);t=`js-${btoa(crypto.getRandomValues(e))}`,this.#g.id=t}if(this.setAttribute("role","scrollbar"),this.setAttribute("aria-controls",t),this.setAttribute("aria-valuemin","0"),this.setAttribute("aria-label","Alternate scroller, jump directly to author's sections"),this.#p){const t=this.#p.size-1;this.setAttribute("aria-valuemax",t>0?t:1)}}}#C(){this.#p&&this.setAttribute("aria-valuenow",this.#p.get(this.#h).index)}#I(e,s){if(!t.#y.includes(e)||!this.#m)return;let r,i;this.#a=null,this.#l=null,this.#c=null,this.#h=null,this.#p?this.#p.clear():this.#p=new Map;const o=[];if(document.querySelectorAll(this[e]).forEach((t=>{for(r=t.getBoundingClientRect(),i=0;i<o.length&&!(r.top<o[i].top);i++);(0===i||i>0&&o[i-1].top!==r.top)&&o.splice(i,0,{top:r.top,bot:r.bottom,el:t}),"function"==typeof s&&s(t)})),o.length>0){this.#a=o[0].el,this.#l=o[o.length-1].el,this.#c=new WeakMap([[this.#a,{pos:"start",down:!0}],[this.#l,{pos:"end",down:!1}]]);let t=0,e=0;const s=window.innerHeight;for(i=0;i<o.length;i++)0>o[i].top&&(e=o[i].bot<s?1:0,t=i+e<o.length?i+e:i),this.#p.set(o[i].el,{index:i,prev:i>0?o[i-1].el:null,next:i<o.length-1?o[i+1].el:null});this.currentTarget=o[t].el,0===t?this.#S("start"):t===o.length-1?this.#S("end"):this.#S("mid")}}targetIntersection(t){const e=t=>{const e=this.#p.get(t.target);t.isIntersecting&&t.intersectionRatio>=.95?e.lastTop?e.lastTop<t.boundingClientRect.top&&(e.lastTop=t.boundingClientRect.top):e.lastTop=t.boundingClientRect.top:t.isIntersecting||(e.lastTop=void 0)};let s,r;const i=(t,e,s,r)=>{let i=t;const o=s.target===e&&s||r.target===e&&r;return o&&(i?o.intersectionRatio<i.intersectionRatio&&(i=o):i=o),i},o=t.sort(((t,o)=>{const n=t.isIntersecting&&o.isIntersecting;let a=t.isIntersecting?-1:o.isIntersecting?1:0;return s=i(s,this.#a,t,o),r=i(r,this.#l,t,o),e(t),e(o),n&&(a=o.intersectionRatio-t.intersectionRatio),a}))[0],n=o.rootBounds?.height??1e6,a=o.boundingClientRect.height,l=a<=n?.49:n/2/a;if(o.isIntersecting){const t=this.#p.get(this.#h),e=this.#c.has(o.target),i=this.#p.get(o.target),n=o.boundingClientRect.top,a=o.intersectionRatio,c=o.target,h=e?0:l,{pos:p,down:d}=this.#c.get(c)??{pos:"mid",down:void 0===i.lastTop?this.#r:n<i.lastTop};if(i.lastTop=n,"mid"!==p)this.#i=a>l&&p;else if(this.#i){const t="start"===this.#i?s:r;t&&t.intersectionRatio<=l&&(this.#i=!1)}if(!this.#i||"mid"!==p){(this.#r?i.index>t.index:i.index<t.index)&&a>h&&(this.currentTarget=c),this.#S(p),this.#r=d}this.#i&&o.intersectionRatio>=.98&&(this.currentTarget=c)}else o.intersectionRatio<=l&&(this.#p.get(o.target).lastTop=void 0)}controlIntersection(t,e){if(this.#u&&!this.#v){const s=e.filter((t=>t.isIntersecting));if(s.length>0){const e=s[0],r=this.#u.get(e.target);this.#o++,this.#n=t,r?this.#b.style.setProperty("--js-bg-color",r):(this.#b.style.removeProperty("--js-bg-color"),this.#o=0)}else{this.#o--;const e=t!==this.#n,s=this.#o<=0&&t===this.#n;(e||s)&&(this.#b.style.removeProperty("--js-bg-color"),this.#o=0)}}}#x(){if(!this.#p)return;const e=window.innerHeight,s=this.offsetHeight,r=e-s,i=Math.abs(s-2),o=t.getNumber(window.getComputedStyle(this).bottom),n=e-o-2;this.#t=new IntersectionObserver(this.targetIntersection,{threshold:[.1,.2,.3,.4,.5,.6,.7,.8,.9,1]}),this.#e=new IntersectionObserver(this.controlIntersection.bind(null,"top"),{threshold:0,rootMargin:`-${r}px 0px -${i}px 0px`}),this.#s=new IntersectionObserver(this.controlIntersection.bind(null,"bot"),{threshold:0,rootMargin:`-${n}px 0px -${o}px 0px`});const a=this.#p.keys();for(const t of a)this.#t.observe(t);const l=this.#u.keys();for(const t of l)this.#e.observe(t),this.#s.observe(t)}#A(){this.#t&&this.#t.disconnect(),this.#e&&this.#e.disconnect(),this.#s&&this.#s.disconnect(),this.#t=null,this.#e=null,this.#s=null}resizeHandler(){this.#v||(this.#v=!0,setTimeout((()=>{let t=!1;const e=window.innerWidth;e!==this.#T&&(e>this.#T&&(t=!0),this.#T=e),this.setup(!1,t),this.#v=!1}),t.#f))}#L(){this.#v=!1,window.addEventListener("resize",this.resizeHandler)}#R(){window.removeEventListener("resize",this.resizeHandler),this.#v=!1}setup(e=!0,s=!0){let r=!1;this.#m?this.teardown(e,s):r=!0,this.#m=!0,s&&t.#y.forEach((t=>{this.#I(t)})),r&&(this.#T=window.innerWidth,this.#z(),this.enableKeyboard=t.#w.enablekeyboard),this.#x(),e&&this.#L()}teardown(t=!0,e=!0){t&&this.#R(),this.#A(),e&&(this.#a=null,this.#l=null,this.#c=null,this.#h=null,this.#p&&this.#p.clear(),this.#p=null,this.#g=null,this.enableKeyboard=!1,this.#m=!1)}connectedCallback(){"complete"!==document.readyState?window.addEventListener("load",(()=>{this.setup()}),{once:!0}):this.setup();const{shadowRoot:t}=this;t.innerHTML='<style>:host{--js-width:3rem;--js-aspect-ratio:0.2;--js-height:calc(var(--js-width) * (1 / var(--js-aspect-ratio)));--js-bg-color:black;--js-color:white;--js-opacity-full:0.7;--js-opacity-rest:0.3;--js-attach-right:1rem;--js-attach-bottom:1rem;position:fixed;bottom:var(--js-attach-bottom);right:var(--js-attach-right);color:var(--js-color)}.container{display:block;transition:opacity 1s;opacity:var(--js-opacity-full);width:var(--js-width);height:auto;aspect-ratio:var(--js-aspect-ratio)}.container button{padding:0;margin:0;border:none;color:transparent}.container.best{aspect-ratio:calc(var(--js-aspect-ratio) * 2)}.container.none{display:none}.container.rest{opacity:var(--js-opacity-rest)}.container.mid{transform:none}.container.best.mid.down .top,.container.start .top{pointer-events:none;opacity:0}.container.best.mid.up .bot,.container.end .bot{pointer-events:none;opacity:0;transform:translateY(102%)}.container.best.end .top,.container.best.mid.down .top,.container.best.mid.up .top{transform:translateY(5%)}.container.end .top{transform:translateY(102%)}.bot,.top{position:absolute;width:var(--js-width);height:auto;aspect-ratio:calc(var(--js-aspect-ratio) * 2);transition:opacity 1s,transform 1s;pointer-events:auto}.top{top:0}.bot{bottom:0}.bot .end,.bot .next,.top .prev,.top .start{content:\'\';position:absolute;left:50%;width:100%;background:var(--js-bg-color);-webkit-user-select:none;user-select:none}.bot .end,.top .start{height:calc(var(--js-height)/ 6);clip-path:polygon(10% 0%,90% 0%,90% 30%,65% 30%,100% 100%,0% 100%,35% 30%,10% 30%)}.bot .next,.top .prev{height:calc(var(--js-height)/ 4);clip-path:polygon(50% 0%,100% 60%,65% 60%,100% 100%,0% 100%,35% 60%,0% 60%)}.top .prev,.top .start{transform:translateX(-50%)}.bot .end,.bot .next{transform:translateX(-50%) rotateX(180deg)}.top .start{top:0}.top .prev{top:calc((var(--js-height)/ 6) + 10%)}.bot .next{bottom:calc((var(--js-height)/ 6) + 10%)}.bot .end{bottom:0}</style><div class="container none"><div class="top"><button type="button" tabindex="-1" class="start">Scroll to start</button><button type="button" tabindex="-1" class="prev">Scroll to previous</button></div><div class="bot"><button type="button" tabindex="-1" class="next">Scroll to next</button><button type="button" tabindex="-1" class="end">Scroll to end</button></div></div>',this.#b=t.querySelector(".container"),this.display=this.display,this.colormap=this.colormap,t.querySelector(".top .start").addEventListener("click",this.clickTop),t.querySelector(".bot .end").addEventListener("click",this.clickBottom),t.querySelector(".top .prev").addEventListener("click",this.clickPrev),t.querySelector(".bot .next").addEventListener("click",this.clickNext)}disconnectedCallback(){this.#b=null,this.shadowRoot.querySelector(".top .start").removeEventListener("click",this.clickTop),this.shadowRoot.querySelector(".bot .end").removeEventListener("click",this.clickBottom),this.shadowRoot.querySelector(".top. .prev").removeEventListener("click",this.clickPrev),this.shadowRoot.querySelector(".bot .next").removeEventListener("click",this.clickNext),this.teardown()}attributeChangedCallback(t,e,s){s!==e&&(this[t]=this.getAttribute(t))}}customElements.define("jump-scroll",t);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localnerve/jump-scroll",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "A small, fast, no-dep, jump-scroll web component",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/jump-scroll.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"prepublishOnly": "npm run build"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"webpack": "^5.
|
|
13
|
+
"webpack": "^5.89.0",
|
|
14
14
|
"webpack-cli": "^5.1.4",
|
|
15
15
|
"clean-css": "^5.3.2"
|
|
16
16
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"webcomponent",
|
|
26
26
|
"javascript"
|
|
27
27
|
],
|
|
28
|
-
"author": "Alex Grant
|
|
28
|
+
"author": "Alex Grant <alex@localnerve.com> (https://www.localnerve.com)",
|
|
29
29
|
"license": "BSD-3-Clause",
|
|
30
30
|
"bugs": {
|
|
31
31
|
"url": "https://github.com/localnerve/ui-elements/issues"
|