diff --git a/common-theme/assets/scripts/solo-view.js b/common-theme/assets/scripts/solo-view.js index 4adfe46e9..cef2f85f7 100644 --- a/common-theme/assets/scripts/solo-view.js +++ b/common-theme/assets/scripts/solo-view.js @@ -1,8 +1,10 @@ const elementName = "solo-view"; + class SoloView extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); + // State object this.state = { currentBlockIndex: 0, @@ -22,6 +24,7 @@ class SoloView extends HTMLElement { ); document.addEventListener("keydown", this.handleKeydown.bind(this)); } + connectedCallback() { this.render(); // Render the component this.cacheDOM(); // Cache necessary DOM elements @@ -29,11 +32,13 @@ class SoloView extends HTMLElement { this.handleFragment(); // Check for a fragment in the URL and set to this view if present this.updateView(); // Initial view update } + disconnectedCallback() { // Removing window and doc event listeners window.removeEventListener("hashchange", this.handleFragment.bind(this)); document.removeEventListener("keydown", this.handleKeydown.bind(this)); } + // Cache DOM elements cacheDOM() { this.state.blocks = [ @@ -49,13 +54,16 @@ class SoloView extends HTMLElement { '[slot="nav"] .c-solo-view__next' ); } + // Add event listeners addEventListeners() { this.state.tocLinks.forEach((link, index) => { link.addEventListener("click", () => this.updateCurrentBlockIndex(index)); }); + this.state.navButtons.back.addEventListener("click", this.navigateBack); this.state.navButtons.next.addEventListener("click", this.navigateNext); + this.addEventListener( "touchstart", (event) => { @@ -66,6 +74,7 @@ class SoloView extends HTMLElement { }, { passive: true } ); + this.addEventListener( "touchend", (event) => { @@ -78,20 +87,25 @@ class SoloView extends HTMLElement { }, { passive: true } ); + this.addEventListener("keydown", this.handleKeydown, { passive: true }); } + // Update current block index updateCurrentBlockIndex(index) { this.state.currentBlockIndex = index; this.updateView(); } + // Navigation logic navigateBack = (event) => { this.navigate(event, this.state.currentBlockIndex - 1); }; + navigateNext = (event) => { this.navigate(event, this.state.currentBlockIndex + 1); }; + navigate = (event, index) => { if (index < 0 || index >= this.state.blocks.length) { return; @@ -101,6 +115,7 @@ class SoloView extends HTMLElement { event.stopPropagation(); this.state.tocLinks[index].click(); }; + // Handle swipe gesture handleSwipeGesture = (startX, endX) => { const deltaX = endX - startX; @@ -113,6 +128,7 @@ class SoloView extends HTMLElement { this.navigateNext(new Event("swipe")); } }; + eventTargetIsHorizontallyScrollable = (event) => { let el = event.target; while (el && el.localName !== elementName) { @@ -123,20 +139,8 @@ class SoloView extends HTMLElement { } return false; } + handleKeydown = (event) => { - // Don't navigate if user is typing in an editable element - const target = event.target; - const tagName = target.tagName; - - if ( - tagName === 'TEXTAREA' || - tagName === 'INPUT' || - tagName === 'SELECT' || - target.isContentEditable - ) { - return; - } - if (event.key === "ArrowLeft") { this.navigateBack(event); } @@ -144,6 +148,7 @@ class SoloView extends HTMLElement { this.navigateNext(event); } }; + // look for a fragment in the URL and navigate to it if one exists so we can link directly to a view handleFragment = () => { const fragment = window.location.hash.substring(1); @@ -157,17 +162,20 @@ class SoloView extends HTMLElement { } } }; + // Update view updateView() { this.state.blocks.forEach((block, index) => { block.hidden = index !== this.state.currentBlockIndex; }); + this.state.tocLinks.forEach((link, index) => { link.classList.toggle( "is-active", index === this.state.currentBlockIndex ); }); + // Hide unusable buttons this.state.navButtons.back.style.display = this.state.currentBlockIndex === 0 ? "none" : "inline-flex"; @@ -176,11 +184,13 @@ class SoloView extends HTMLElement { ? "none" : "inline-flex"; } + // Render method with slots and CSS render() { this.shadowRoot.innerHTML = ` @@ -208,4 +220,5 @@ class SoloView extends HTMLElement { `; } } + customElements.define(elementName, SoloView);