@localnerve/jump-scroll 0.8.1 → 0.9.0-rc.2

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 CHANGED
@@ -13,7 +13,7 @@ Navigation options: [next, previous, first, last].
13
13
  Non-browser module exports build helpers (for building CSP rules, etc).
14
14
 
15
15
  ## Web Size
16
- + ~11.8k full, ~4k gzip
16
+ + ~12.7k full, ~4.3k gzip
17
17
 
18
18
  ## Attributes
19
19
 
@@ -27,9 +27,10 @@ Non-browser module exports build helpers (for building CSP rules, etc).
27
27
  **"both"** - The control displays both [top, previous] **AND** [bottom, next] jump scrolling options simulataneously. Larger footprint.
28
28
 
29
29
  * `colormap` - *Optional*. A map of targets to colors. Changes the color of the jump-scroll control over specific elements. Defaults to nothing.
30
- **Format:** `selector@color[;selector@color]*`
30
+ **Format:** `selector@color[/focus-color][;selector@color[/focus-color]]*`
31
31
  **selector** - *String*. Must be a selector of DOM element(s). When a selected element crosses the vertical bounds of the `jump-scroll` control, the `js-bg-color` background will be changed to the color (or variable) provided.
32
32
  **color** - *CssColor|CssCustomProperty*. A css color or a custom property (variable) of a color to use for the `js-bg-color` background of the control.
33
+ **focus-color** - *CssColor|CssCustomProperty*. Optional. A css color or a custom property (variable) of a color to use for the `js-bg-color-focus` of the control. If missing, defaults to `js-bg-color-focus`.
33
34
 
34
35
  * `enablekeyboard` - *Optional*. Values: `"true" | "false"`. Defaults to `"true"`.
35
36
  Enables keyboard scrolling by handling the following keydown events at the defined `scrollcontainer` or common scroll target ancestor:
@@ -44,8 +45,10 @@ Non-browser module exports build helpers (for building CSP rules, etc).
44
45
  * `--js-width` - The overall width of the control. Defaults to 3rem.
45
46
  * `--js-aspect-ratio` - The aspect ratio of the control. Defaults to 1/5.
46
47
  * `--js-bg-color` - The color of the control arrows. Defaults to black.
47
- * `--js-opacity-full` - The opacity of the control arrows at attention. Defaults to 0.7.
48
- * `--js-opacity-rest` - The opacity of the control arrows at rest. Defaults to 0.3.
48
+ * `--js-bg-color-focus` - The color of the arrow on focus. Defaults to `darkorange`.
49
+ * `--js-bg-spread-focus` - The spread of the focus glow indicator. Defaults to 8px.
50
+ * `--js-opacity-full` - The opacity of the control arrows at attention. Defaults to 0.8.
51
+ * `--js-opacity-rest` - The opacity of the control arrows at rest. Defaults to 0.5.
49
52
  * `--js-attach-right` - The distance from the fixed, right-edge attachment. Defaults to 1rem;
50
53
  * `--js-attach-bottom` - The distance form the fixed, bottom-edge attachment. Defaults to 1rem;
51
54
 
@@ -1 +1 @@
1
- :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%)}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}.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}
1
+ :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)}
@@ -1,2 +1,2 @@
1
- /*! jump-scroll@0.8.1, Copyright (c) 2023 Alex Grant (alex@localnerve.com), LocalNerve LLC, BSD-3-Clause */
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;#d=null;static#p=15;#u=null;#b=null;#g=null;#v=!1;#m=!1;#T=0;static#f=800;static#y=["target"];static#w={target:"section",display:"best",colormap:"",enablekeyboard:!0,scrollcontainer:""};static get observedAttributes(){return[...t.#y,"display","colormap","enablekeyboard","scrollcontainer"]}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)}#k(e){if(this.hasAttribute(e)){const t=this.getAttribute(e);return/^\s*(?:true|false)\s*$/i.test(t)?"false"!==t:t}return t.#w[e]}get currentTarget(){return this.#h}set currentTarget(t){this.#d.has(t)&&(this.#h=t,this.#I())}get target(){return this.#k("target")}set target(t){const e="target";t?this.setAttribute(e,t):this.removeAttribute(e),this.#C(e)}get display(){return this.#k("display")}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.#x(),this.#A(),this.#b&&this.#b.classList.add(t)):this.removeAttribute(e)}get colormap(){return this.#k("colormap")}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)}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.#k("enablekeyboard")}set enableKeyboard(t){if(!this.#g)return;const e=t?"addEventListener":"removeEventListener";this.#g[e]("keydown",this.keydownHandler),this.setAttribute("enablekeyboard",t)}#S(){if(!this.#a||!this.#d)return;let t,e,s=1e6;const r=[],i=this.#d.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}get scrollContainer(){return this.#k("scrollcontainer")}set scrollContainer(t){if(!this.#a||!this.#d)return;let e;if(this.#g?.id&&this.#g.id.startsWith("js-")&&(this.#g.id=""),t&&(e=document.querySelector(t)),this.#g=e??this.#S(),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.#d){const t=this.#d.size-1;this.setAttribute("aria-valuemax",t>0?t:1)}}}#I(){this.#d&&this.setAttribute("aria-valuenow",this.#d.get(this.#h).index)}#j(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)}#O(e,s){const r=this.#d.get(this.#h);if(r[e]){const i=r[e],o=this.#d.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.#O(e,s):window.scrollBy({top:n,behavior:"smooth"}))}),16.67*t.#p)}else this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"})}#z(t="start"){this.currentTarget="start"===t?this.#a:this.#l,this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),this.#j(t)}clickTop(t){t&&t.preventDefault(),this.#z("start")}clickBottom(t){t&&t.preventDefault(),this.#z("end")}clickNext(t){t&&t.preventDefault(),this.#O("next",(t=>t<window.innerHeight))}clickPrev(t){t&&t.preventDefault(),this.#O("prev",(t=>t>0))}#C(e,s){if(!t.#y.includes(e)||!this.#v)return;let r,i;this.#a=null,this.#l=null,this.#c=null,this.#h=null,this.#d?this.#d.clear():this.#d=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.#d.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.#j("start"):t===o.length-1?this.#j("end"):this.#j("mid")}}targetIntersection(t){const e=t=>{const e=this.#d.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.#d.get(this.#h),e=this.#c.has(o.target),i=this.#d.get(o.target),n=o.boundingClientRect.top,a=o.intersectionRatio,c=o.target,h=e?0:l,{pos:d,down:p}=this.#c.get(c)??{pos:"mid",down:void 0===i.lastTop?this.#r:n<i.lastTop};if(i.lastTop=n,"mid"!==d)this.#i=a>l&&d;else if(this.#i){const t="start"===this.#i?s:r;t&&t.intersectionRatio<=l&&(this.#i=!1)}if(!this.#i||"mid"!==d){(this.#r?i.index>t.index:i.index<t.index)&&a>h&&(this.currentTarget=c),this.#j(d),this.#r=p}this.#i&&o.intersectionRatio>=.98&&(this.currentTarget=c)}else o.intersectionRatio<=l&&(this.#d.get(o.target).lastTop=void 0)}controlIntersection(t,e){if(this.#u&&!this.#m){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)}}}#A(){if(!this.#d)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.#d.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)}#x(){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.#m||(this.#m=!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.#m=!1}),t.#f))}#E(){this.#m=!1,window.addEventListener("resize",this.resizeHandler)}#L(){window.removeEventListener("resize",this.resizeHandler),this.#m=!1}setup(e=!0,s=!0){let r=!1;this.#v?this.teardown(e,s):r=!0,this.#v=!0,s&&t.#y.forEach((t=>{this.#C(t)})),r&&(this.#T=window.innerWidth,this.setAttribute("aria-label","Alternate scroller, jump directly to author's sections"),this.scrollContainer=this.scrollContainer,this.enableKeyboard=this.enableKeyboard),this.#A(),e&&this.#E()}teardown(t=!0,e=!0){t&&this.#L(),this.#x(),e&&(this.#a=null,this.#l=null,this.#c=null,this.#h=null,this.#d&&this.#d.clear(),this.#d=null,this.#g=null,this.enableKeyboard=!1,this.#v=!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%)}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}.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);
1
+ /*! jump-scroll@0.9.0-rc.2, Copyright (c) 2023 Alex Grant (alex@localnerve.com), LocalNerve LLC, BSD-3-Clause */
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;#d=null;static#p=15;#u=null;#b=null;#g=null;#v=!1;#m=!1;#T=0;static#f=800;static#y=["target"];static#w={target:"section",display:"best",colormap:"",enablekeyboard:!0,scrollcontainer:""};static get observedAttributes(){return[...t.#y,"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.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)}#k(e){if(this.hasAttribute(e)){const t=this.getAttribute(e);return/^\s*(?:true|false)\s*$/i.test(t)?"false"!==t:t}return t.#w[e]}get currentTarget(){return this.#h}set currentTarget(t){this.#d.has(t)&&(this.#h=t,this.#I())}get target(){return this.#k("target")}set target(t){const e="target";t?this.setAttribute(e,t):this.removeAttribute(e),this.#C(e)}get display(){return this.#k("display")}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.#j(),this.#b&&this.#b.classList.add(t)):this.removeAttribute(e)}get colormap(){return this.#k("colormap")}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("@"),[r,i]=s.replace(/\s/,"").split("/");r.startsWith("--")&&(r=`var(${r})`),i?.startsWith("--")&&(i=`var(${i})`),document.querySelectorAll(e).forEach((t=>{this.#u.set(t,{bgColor:r,bgColorFocus:i})}))}}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.#k("enablekeyboard")}set enableKeyboard(t){if(!this.#g)return;const e=t?"addEventListener":"removeEventListener";this.#g[e]("keydown",this.keydownHandler),this.setAttribute("enablekeyboard",t)}#x(){if(!this.#a||!this.#d)return;let t,e,s=1e6;const r=[],i=this.#d.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}get scrollContainer(){return this.#k("scrollcontainer")}set scrollContainer(t){if(!this.#a||!this.#d)return;let e;if(this.#g?.id&&this.#g.id.startsWith("js-")&&(this.#g.id=""),t&&(e=document.querySelector(t)),this.#g=e??this.#x(),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("aria-controls",t),this.setAttribute("aria-valuemin","0"),this.#d){const t=this.#d.size-1;this.setAttribute("aria-valuemax",t>0?t:1)}}}#I(){this.#d&&this.setAttribute("aria-valuenow",this.#d.get(this.#h).index)}#S(t,e){const s=t=>{Array.from(this.#b.querySelectorAll(`.${t} button`)).forEach((t=>{t.blur(),t.setAttribute("disabled","true"),t.setAttribute("aria-hidden","true")}))},r=t=>{Array.from(this.#b.querySelectorAll(`.${t} button`)).forEach((t=>{t.removeAttribute("disabled"),t.setAttribute("aria-hidden","false")}))};"mid"===t?"both"===this.display?["top","bot"].forEach((t=>r(t))):"down"===e?(r("bot"),s("top")):(r("top"),s("bot")):"start"===t?(r("bot"),s("top")):(r("top"),s("bot"))}#O(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.#S(t,e[e.length-1]),this.#b.classList.remove(...s),this.#b.classList.add(...e),setTimeout((()=>{this.#b.classList.add("rest")}),500)}#E(e,s){const r=this.#d.get(this.#h);if(r[e]){const i=r[e],o=this.#d.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.#E(e,s):window.scrollBy({top:n,behavior:"smooth"}))}),16.67*t.#p)}else this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"})}#z(t="start"){this.currentTarget="start"===t?this.#a:this.#l,this.#h.scrollIntoView({block:"nearest",inline:"start",behavior:"smooth"}),this.#O(t)}clickTop(t){t&&t.preventDefault(),this.#z("start")}clickBottom(t){t&&t.preventDefault(),this.#z("end")}clickNext(t){t&&t.preventDefault(),this.#E("next",(t=>t<window.innerHeight))}clickPrev(t){t&&t.preventDefault(),this.#E("prev",(t=>t>0))}#C(e,s){if(!t.#y.includes(e)||!this.#v)return;let r,i;this.#a=null,this.#l=null,this.#c=null,this.#h=null,this.#d?this.#d.clear():this.#d=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.#d.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.#O("start"):t===o.length-1?this.#O("end"):this.#O("mid")}}targetIntersection(t){const e=t=>{const e=this.#d.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.#d.get(this.#h),e=this.#c.has(o.target),i=this.#d.get(o.target),n=o.boundingClientRect.top,a=o.intersectionRatio,c=o.target,h=e?0:l,{pos:d,down:p}=this.#c.get(c)??{pos:"mid",down:void 0===i.lastTop?this.#r:n<i.lastTop};if(i.lastTop=n,"mid"!==d)this.#i=a>l&&d;else if(this.#i){const t="start"===this.#i?s:r;t&&t.intersectionRatio<=l&&(this.#i=!1)}if(!this.#i||"mid"!==d){(this.#r?i.index>t.index:i.index<t.index)&&a>h&&(this.currentTarget=c),this.#O(d),this.#r=p}this.#i&&o.intersectionRatio>=.98&&(this.currentTarget=c)}else o.intersectionRatio<=l&&(this.#d.get(o.target).lastTop=void 0)}controlIntersection(t,e){if(this.#u&&!this.#m){const s=e.filter((t=>t.isIntersecting));if(s.length>0){const e=s[0],{bgColor:r,bgColorFocus:i}=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),i?this.#b.style.setProperty("--js-bg-color-focus",i):this.#b.style.removeProperty("--js-bg-color-focus")}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.#b.style.removeProperty("--js-bg-color-focus"),this.#o=0)}}}#j(){if(!this.#d)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.#d.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.#m||(this.#m=!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.#m=!1}),t.#f))}#L(){this.#m=!1,window.addEventListener("resize",this.resizeHandler)}#R(){window.removeEventListener("resize",this.resizeHandler),this.#m=!1}setup(e=!0,s=!0){let r=!1;this.#v?this.teardown(e,s):r=!0,this.#v=!0,s&&t.#y.forEach((t=>{this.#C(t)})),r&&(this.#T=window.innerWidth,this.setAttribute("aria-label","Alternate scroller, jump directly to author's sections"),this.scrollContainer=this.scrollContainer,this.enableKeyboard=this.enableKeyboard),this.#j(),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.#d&&this.#d.clear(),this.#d=null,this.#g=null,this.enableKeyboard=!1,this.#v=!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-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.#b=t.querySelector(".container"),this.display=this.display,this.colormap=this.colormap,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.#b=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.teardown()}attributeChangedCallback(t,e,s){s!==e&&(this[t]=this.getAttribute(t))}}customElements.define("jump-scroll",t);
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@localnerve/jump-scroll",
3
- "version": "0.8.1",
3
+ "version": "0.9.0-rc.2",
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",
7
7
  "type": "module",
8
8
  "scripts": {
9
- "a11y": "axe http://localhost:3010/dist/jump-scroll",
9
+ "a11y": "axe http://localhost:3010/dist/jump-scroll --load-delay 2000",
10
10
  "build": "node build.js && webpack-cli --config webpack.prod.config.js",
11
11
  "prepublishOnly": "npm run build"
12
12
  },