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

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
@@ -10,18 +10,21 @@
10
10
 
11
11
  A native web component scrolling assistant that allows a user to jump to author defined page target sections.
12
12
  Navigation options: [next, previous, first, last].
13
- Non-browser module exports build helpers (for building CSP rules, etc).
13
+ Non-browser module exports build helpers (for building CSP rules, etc).
14
14
 
15
- ## Web Size
16
- + ~12.7k full, ~4.3k gzip
15
+ ## Web Details
16
+ + ~13k full, ~4.4k gzip
17
+ + Axe core 4.8.2 pass
17
18
 
18
- ## Attributes
19
+ ## Attributes and Properties
19
20
 
20
21
  * `target` - *Required*. A selector that defines all the target elements to vertically "jump scroll" to in a page. Defaults to `"section"`.
21
22
 
22
23
  * `scrollcontainer` - *Optional*. A selector that defines the scroll container. Defaults to the common ancestor of `target` elements.
23
24
  If the selector provided selects more than one element, only the first element found is used as the scroll container.
24
25
 
26
+ > Property name is `scrollContainer` (camel case).
27
+
25
28
  * `display` - *Optional*. Values: `"best" | "both"`. Defaults to `"best"`.
26
29
  **"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.
27
30
  **"both"** - The control displays both [top, previous] **AND** [bottom, next] jump scrolling options simulataneously. Larger footprint.
@@ -40,6 +43,8 @@ Non-browser module exports build helpers (for building CSP rules, etc).
40
43
  + `Shift+PageDown` - Jumps to the bottom scroll target.
41
44
  + `Shift+PageUp` - Jumps to the top scroll target.
42
45
 
46
+ > Property name is `enableKeyboard` (camel case).
47
+
43
48
  ## Overridable CSS Variables
44
49
 
45
50
  * `--js-width` - The overall width of the control. Defaults to 3rem.
@@ -52,6 +57,12 @@ Non-browser module exports build helpers (for building CSP rules, etc).
52
57
  * `--js-attach-right` - The distance from the fixed, right-edge attachment. Defaults to 1rem;
53
58
  * `--js-attach-bottom` - The distance form the fixed, bottom-edge attachment. Defaults to 1rem;
54
59
 
60
+ ## Javascript Properties and Methods
61
+
62
+ * `currentTarget` **Property** - Assign an `HTMLElement` to set the component's internal currentTarget. The supplied `HTMLElement` must be known to the internal component target map of elements (found with the supplied `target` selector). Get this property to audit the component's internal currentTarget.
63
+
64
+ * `jumpScroll(HTMLElement)` **Method** - Jump directly to the given `HTMLElement`. The supplied `HTMLElement` must be known to the internal component target map of elements (found with the supplied `target` selector).
65
+
55
66
  ## Usage Example
56
67
 
57
68
  ```html
@@ -1,2 +1,2 @@
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);
1
+ /*! jump-scroll@0.9.0, 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.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())}jumpScroll(t){const e=this.#d.get(t);e&&(this.currentTarget=e.prev,this.#C("next",(t=>t<window.innerHeight)))}get target(){return this.#k("target")}set target(t){const e="target";t?this.setAttribute(e,t):this.removeAttribute(e),this.#A(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.#j(),this.#S(),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)}#O(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"))}#E(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.#O(t,e[e.length-1]),this.#b.classList.remove(...s),this.#b.classList.add(...e),setTimeout((()=>{this.#b.classList.add("rest")}),500)}#C(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.#C(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.#E(t)}clickTop(t){t&&t.preventDefault(),this.#z("start")}clickBottom(t){t&&t.preventDefault(),this.#z("end")}clickNext(t){t&&t.preventDefault(),this.#C("next",(t=>t<window.innerHeight))}clickPrev(t){t&&t.preventDefault(),this.#C("prev",(t=>t>0))}#A(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.#E("start"):t===o.length-1?this.#E("end"):this.#E("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.#E(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)}}}#S(){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)}#j(){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.#L(!1,t),this.#m=!1}),t.#f))}#R(){this.#m=!1,window.addEventListener("resize",this.resizeHandler)}#M(){window.removeEventListener("resize",this.resizeHandler),this.#m=!1}#L(e=!0,s=!0){let r=!1;this.#v?this.#H(e,s):r=!0,this.#v=!0,s&&t.#y.forEach((t=>{this.#A(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.#S(),e&&this.#R()}#H(t=!0,e=!0){t&&this.#M(),this.#j(),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.#L()}),{once:!0}):this.#L();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.#H()}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.0-rc.2",
3
+ "version": "0.9.0",
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",