@localnerve/jump-scroll 0.9.23 → 0.9.25

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/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2024 Alex Grant <alex@localnerve.com> (https://www.localnerve.com), LocalNerve LLC
1
+ Copyright (c) 2025 Alex Grant <alex@localnerve.com> (https://www.localnerve.com), LocalNerve LLC
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions are met:
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * jump-scroll Node entry point
3
3
  *
4
- * Copyright (c) 2024 Alex Grant (@localnerve), LocalNerve LLC
4
+ * Copyright (c) 2025 Alex Grant (@localnerve), LocalNerve LLC
5
5
  * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
6
6
  */
7
7
  import * as path from 'node:path';
@@ -1,2 +1,2 @@
1
- /*! jump-scroll@0.9.23, Copyright (c) 2023-2024 Alex Grant <alex@localnerve.com> (https://www.localnerve.com), LocalNerve LLC, BSD-3-Clause */
1
+ /*! jump-scroll@0.9.25, Copyright (c) 2023-2025 Alex Grant <alex@localnerve.com> (https://www.localnerve.com), LocalNerve LLC, BSD-3-Clause */
2
2
  class t extends HTMLElement{#t=null;#e=null;#s=null;#i=!0;#o=!1;#r=0;#n="";#l=null;#h=null;#c=null;#a=null;#u=null;static#d=15;#b=null;#p=null;#v=null;#g=null;#m=null;#f=null;#y=null;#w=!1;#k=!1;#j=0;static#x=800;static#A=["target"];static#E={target:"section",display:"best",colormap:"",enablekeyboard:!0,scrollcontainer:""};static get observedAttributes(){return[...t.#A,"display","colormap","enablekeyboard","scrollcontainer"]}static getNumber(t){return parseFloat(t.replace(/[^.+\-\d]+/,""))}constructor(){super(),this.attachShadow({mode:"open",delegatesFocus:!0}),this.targetIntersection=this.targetIntersection.bind(this),this.controlIntersection=this.controlIntersection.bind(this),this.resizeHandler=this.resizeHandler.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.controlBlur=this.controlBlur.bind(this),this.keydownHandler=this.keydownHandler.bind(this)}#T(e){if(this.hasAttribute(e)){const t=this.getAttribute(e);return/^\s*(?:true|false)\s*$/i.test(t)?"false"!==t:t}return t.#E[e]}get currentTarget(){return this.#a}set currentTarget(t){this.#u.has(t)&&(this.#a=t,this.#S())}jumpScroll(t){if(t!==this.#a){const e=this.#u.get(t);e&&(this.currentTarget=e.prev,this.#R("next",(t=>t<window.innerHeight)))}}get target(){return this.#T("target")}set target(t){const e="target";t?this.setAttribute(e,t):this.removeAttribute(e),this.#I(e)}get display(){return this.#T("display")}set display(t){const e="display",s=["both","best"];this.#p&&this.#p.classList.remove(...s.concat("up","down")),s.includes(t)?(this.setAttribute(e,t),this.#L(),this.#C(),this.#p&&this.#p.classList.add(t)):this.removeAttribute(e)}get colormap(){return this.#T("colormap")}set colormap(t){if(null===this.#p)return;this.#b&&this.#b.clear(),this.#b=null;const e="colormap",s=t&&t.split(";");if(s&&s.length>0&&s[0].includes("@")){this.setAttribute(e,t),this.#b=new Map;for(const t of s){let[e,s]=t.replace(/\s/,"").split("@"),[i,o]=s.replace(/\s/,"").split("/");i.startsWith("--")&&(i=`var(${i})`),o?.startsWith("--")&&(o=`var(${o})`),document.querySelectorAll(e).forEach((t=>{this.#b.set(t,{bgColor:i,bgColorFocus:o})}))}}else this.removeAttribute(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)}get enableKeyboard(){return this.#T("enablekeyboard")}set enableKeyboard(t){if(!this.#y)return;const e=t?"addEventListener":"removeEventListener";this.#y[e]("keydown",this.keydownHandler),this.setAttribute("enablekeyboard",t)}#q(){if(!this.#l||!this.#u)return;let t,e,s=1e6;const i=[],o=this.#u.keys();for(const r of o){for(e=r,t=0;e.parentElement;e=e.parentElement)t++;t<=s&&(s=t,i.unshift(r))}let r=i[0].parentElement??i[0];if(i.length>1){const t=new Range;t.setStart(i[0],0),t.setEnd(i[1],0),t.collapsed&&(t.setStart(i[1],0),t.setEnd(i[0],0)),r=t.commonAncestorContainer??r}return r}get scrollContainer(){return this.#T("scrollcontainer")}set scrollContainer(t){if(!this.#l||!this.#u)return;let e;if(this.#y?.id&&this.#y.id.startsWith("js-")&&(this.#y.id=""),t&&(e=document.querySelector(t)),this.#y=e??this.#q(),this.#y){let t;if(this.#y.id)t=this.#y.id;else{const e=new Uint8Array(10);t=`js-${btoa(crypto.getRandomValues(e))}`,this.#y.id=t}if(this.setAttribute("aria-controls",t),this.setAttribute("aria-valuemin","0"),this.#u){const t=this.#u.size-1;this.setAttribute("aria-valuemax",t>0?t:1)}}}#S(){this.#u&&this.setAttribute("aria-valuenow",this.#u.get(this.#a).index)}#H(t,e){const s=t=>{const e=this.#f;return Array.from(this.#p.querySelectorAll(`.${t} button`)).forEach((t=>{t.blur(),t.setAttribute("disabled","true"),t.setAttribute("aria-hidden","true")})),this.#v.get(e)},i=(t,e)=>{Array.from(this.#p.querySelectorAll(`.${t} button`)).forEach((t=>{t.removeAttribute("disabled"),t.setAttribute("aria-hidden","false"),t===e&&t.focus()}))};"mid"===t?"both"===this.display?["top","bot"].forEach((t=>i(t))):"down"===e?i("bot",s("top")):i("top",s("bot")):"start"===t?i("bot",s("top")):i("top",s("bot"))}#B(t){const e=[t],s=["mid","start","end","rest","up","down"];null!==this.#l&&s.push("none"),"best"===this.display&&"mid"===t&&(this.#i?e.push("down"):e.push("up")),this.#H(t,e[e.length-1]),this.#p.classList.remove(...s),this.#p.classList.add(...e),setTimeout((()=>{this.#p.classList.add("rest")}),500)}#R(e,s){const i=this.#u.get(this.#a);if(i[e]){const o=i[e],r=this.#u.get(o),n=Math.round(r.lastTop);this.currentTarget=o,this.#a.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),setTimeout((()=>{n===Math.round(r.lastTop)&&(s(n)?this.#R(e,s):window.scrollBy({top:n,behavior:"smooth"}))}),16.67*t.#d)}else this.#a.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"})}#P(t="start"){this.currentTarget="start"===t?this.#l:this.#h,this.#a.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),this.#B(t)}clickTop(t){t&&t.preventDefault(),this.#P("start")}clickBottom(t){t&&t.preventDefault(),this.#P("end")}clickNext(t){t&&t.preventDefault(),this.#R("next",(t=>t<window.innerHeight))}clickPrev(t){t&&t.preventDefault(),this.#R("prev",(t=>t>0))}saveFocus(t){this.#f=t}controlBlur(){this.#f=null}#I(e,s){if(!t.#A.includes(e)||!this.#w)return;let i,o;this.#l=null,this.#h=null,this.#c=null,this.#a=null,this.#u?this.#u.clear():this.#u=new Map;const r=[];if(document.querySelectorAll(this[e]).forEach((t=>{for(i=t.getBoundingClientRect(),o=0;o<r.length&&!(i.top<r[o].top);o++);(0===o||o>0&&r[o-1].top!==i.top)&&r.splice(o,0,{top:i.top,bot:i.bottom,el:t}),"function"==typeof s&&s(t)})),r.length>0){this.#l=r[0].el,this.#h=r[r.length-1].el,this.#c=new WeakMap([[this.#l,{pos:"start",down:!0}],[this.#h,{pos:"end",down:!1}]]);let t=0,e=0;const s=window.innerHeight;for(o=0;o<r.length;o++)0>r[o].top&&(e=r[o].bot<s?1:0,t=o+e<r.length?o+e:o),this.#u.set(r[o].el,{index:o,prev:o>0?r[o-1].el:null,next:o<r.length-1?r[o+1].el:null});this.currentTarget=r[t].el,0===t?this.#B("start"):t===r.length-1?this.#B("end"):this.#B("mid")}}targetIntersection(t){const e=t=>{const e=this.#u.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,i;const o=(t,e,s,i)=>{let o=t;const r=s.target===e&&s||i.target===e&&i;return r&&(o?r.intersectionRatio<o.intersectionRatio&&(o=r):o=r),o},r=t.sort(((t,r)=>{const n=t.isIntersecting&&r.isIntersecting;let l=t.isIntersecting?-1:r.isIntersecting?1:0;return s=o(s,this.#l,t,r),i=o(i,this.#h,t,r),e(t),e(r),n&&(l=r.intersectionRatio-t.intersectionRatio),l}))[0],n=r.rootBounds?.height??1e6,l=r.boundingClientRect.height,h=l<=n?.49:n/2/l;if(r.isIntersecting){const t=this.#u.get(this.#a),e=this.#c.has(r.target),o=this.#u.get(r.target),n=r.boundingClientRect.top,l=r.intersectionRatio,c=r.target,a=e?0:h,{pos:u,down:d}=this.#c.get(c)??{pos:"mid",down:void 0===o.lastTop?this.#i:n<o.lastTop};if(o.lastTop=n,"mid"!==u)this.#o=l>h&&u;else if(this.#o){const t="start"===this.#o?s:i;t&&t.intersectionRatio<=h&&(this.#o=!1)}this.#o&&"mid"===u||((this.#i?o.index>t.index:o.index<t.index)&&l>a&&(this.currentTarget=c),this.#B(u),this.#i=d),this.#o&&r.intersectionRatio>=.98&&(this.currentTarget=c)}else r.intersectionRatio<=h&&(this.#u.get(r.target).lastTop=void 0)}controlIntersection(t,e){if(this.#b&&!this.#k){const s=e.filter((t=>t.isIntersecting));if(s.length>0){const e=s[0],{bgColor:i,bgColorFocus:o}=this.#b.get(e.target);this.#r++,this.#n=t,i?this.#p.style.setProperty("--js-bg-color",i):(this.#p.style.removeProperty("--js-bg-color"),this.#r=0),o?this.#p.style.setProperty("--js-bg-color-focus",o):this.#p.style.removeProperty("--js-bg-color-focus")}else{this.#r--;const e=t!==this.#n,s=this.#r<=0&&t===this.#n;(e||s)&&(this.#p.style.removeProperty("--js-bg-color"),this.#p.style.removeProperty("--js-bg-color-focus"),this.#r=0)}}}#C(){if(!this.#u)return;const e=window.innerHeight,s=this.offsetHeight,i=e-s,o=Math.abs(s-2),r=t.getNumber(window.getComputedStyle(this).bottom),n=e-r-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:`-${i}px 0px -${o}px 0px`}),this.#s=new IntersectionObserver(this.controlIntersection.bind(null,"bot"),{threshold:0,rootMargin:`-${n}px 0px -${r}px 0px`});const l=this.#u.keys();for(const t of l)this.#t.observe(t);const h=this.#b.keys();for(const t of h)this.#e.observe(t),this.#s.observe(t)}#L(){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.#k||(this.#k=!0,setTimeout((()=>{let t=!1;const e=window.innerWidth;e!==this.#j&&(e>this.#j&&(t=!0),this.#j=e),this.#M(!1,t),this.#k=!1}),t.#x))}#z(){this.#k=!1,window.addEventListener("resize",this.resizeHandler)}#$(){window.removeEventListener("resize",this.resizeHandler),this.#k=!1}#M(e=!0,s=!0){let i=!1;this.#w?this.#F(e,s):i=!0,this.#w=!0,s&&t.#A.forEach((t=>{this.#I(t)})),i&&(this.#j=window.innerWidth,this.setAttribute("aria-label","Alternate scroller, jump directly to author's sections"),this.scrollContainer=this.scrollContainer,this.enableKeyboard=this.enableKeyboard),this.#C(),e&&this.#z()}#F(t=!0,e=!0){t&&this.#$(),this.#L(),e&&(this.#l=null,this.#h=null,this.#c=null,this.#a=null,this.#u&&this.#u.clear(),this.#u=null,this.#y=null,this.enableKeyboard=!1,this.#w=!1)}connectedCallback(){"complete"!==document.readyState?window.addEventListener("load",(()=>{this.#M()}),{once:!0}):this.#M();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-bg-color-focus:darkorange;--js-bg-spread-focus:8px;--js-color:white;--js-opacity-full:0.8;--js-opacity-rest:0.5;--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%)}button:hover{cursor:pointer}.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}.bc:focus-within{filter:drop-shadow(0px 0px var(--js-bg-spread-focus) var(--js-bg-color-focus))}.bc{position:absolute;width:var(--js-width)}.bc-end,.bc-start{height:calc(var(--js-height)/ 6)}.bc-next,.bc-prev{height:calc(var(--js-height)/ 4)}.bc-start{top:0}.bc-end{bottom:0}.bc-prev{top:calc((var(--js-height)/ 6) + 10%)}.bc-next{bottom:calc((var(--js-height)/ 6) + 10%)}.bot .end,.bot .next,.top .prev,.top .start{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)}</style><div class="container none"><div class=top><div class="bc bc-start"><button type=button class=start>Scroll to start</button></div><div class="bc bc-prev"><button type=button class=prev>Scroll to previous</button></div></div><div class=bot><div class="bc bc-next"><button type=button class=next>Scroll to next</button></div><div class="bc bc-end"><button type=button class=end>Scroll to end</button></div></div></div>',this.#p=t.querySelector(".container"),this.display=this.display,this.colormap=this.colormap,this.addEventListener("blur",this.controlBlur),this.#g=[this.#p.querySelector('button[class*="next"]'),this.#p.querySelector('button[class*="prev"]'),this.#p.querySelector('button[class*="start"]'),this.#p.querySelector('button[class*="end"]')],this.#m=this.#g.map((t=>this.saveFocus.bind(this,t))),this.#g.forEach(((t,e)=>{t.addEventListener("focus",this.#m[e])})),this.#v=new WeakMap([[this.#g[0],this.#g[1]],[this.#g[1],this.#g[0]],[this.#g[2],this.#g[3]],[this.#g[3],this.#g[2]]]),t.querySelector(".bc-start").addEventListener("click",this.clickTop),t.querySelector(".bc-end").addEventListener("click",this.clickBottom),t.querySelector(".bc-prev").addEventListener("click",this.clickPrev),t.querySelector(".bc-next").addEventListener("click",this.clickNext)}disconnectedCallback(){this.#p=null,this.#f=null,this.#v=null,this.removeEventListener("blur",this.controlBlur),this.#g.forEach(((t,e)=>{t.removeEventListener("focus",this.#m[e])})),this.#g=null,this.#m=null,this.shadowRoot.querySelector(".bc-start").removeEventListener("click",this.clickTop),this.shadowRoot.querySelector(".bc-end").removeEventListener("click",this.clickBottom),this.shadowRoot.querySelector(".bc-prev").removeEventListener("click",this.clickPrev),this.shadowRoot.querySelector(".bc-next").removeEventListener("click",this.clickNext),this.#F()}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.9.23",
3
+ "version": "0.9.25",
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",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "devDependencies": {
14
14
  "@axe-core/cli": "^4.10.1",
15
- "@localnerve/web-component-build": "^1.6.0"
15
+ "@localnerve/web-component-build": "^1.7.0"
16
16
  },
17
17
  "overrides": {
18
18
  "follow-redirects": "latest"