@zeix/ui-element
Version:
UIElement - a HTML-first library for reactive Web Components
218 lines (215 loc) • 33.3 kB
HTML
<module-tabgroup>
<div role="tablist">
<button
type="button"
role="tab"
id="trigger_context-router.ts"
aria-controls="panel_context-router.ts"
aria-selected="true"
tabindex="0"
>TypeScript</button>
</div>
<div role="tabpanel" id="panel_context-router.ts" aria-labelledby="trigger_context-router.ts">
<module-codeblock language="ts" copy-success="Copied!" copy-error="Error trying to copy to clipboard!">
<p class="meta">
<span class="file">context-router.ts</span>
<span class="language">ts</span>
</p>
<pre class="shiki monokai" style="background-color:#272822;color:#F8F8F2" tabindex="0"><code><span class="line"><span style="color:#F92672">import</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F92672"> type</span><span style="color:#F8F8F2"> Component,</span></span>
<span class="line"><span style="color:#F92672"> type</span><span style="color:#F8F8F2"> Computed,</span></span>
<span class="line"><span style="color:#F92672"> type</span><span style="color:#F8F8F2"> Context,</span></span>
<span class="line"><span style="color:#F92672"> type</span><span style="color:#F8F8F2"> State,</span></span>
<span class="line"><span style="color:#F8F8F2"> UNSET,</span></span>
<span class="line"><span style="color:#F8F8F2"> component,</span></span>
<span class="line"><span style="color:#F8F8F2"> computed,</span></span>
<span class="line"><span style="color:#F8F8F2"> dangerouslySetInnerHTML,</span></span>
<span class="line"><span style="color:#F8F8F2"> on,</span></span>
<span class="line"><span style="color:#F8F8F2"> provideContexts,</span></span>
<span class="line"><span style="color:#F8F8F2"> setText,</span></span>
<span class="line"><span style="color:#F8F8F2"> show,</span></span>
<span class="line"><span style="color:#F8F8F2"> state,</span></span>
<span class="line"><span style="color:#F8F8F2"> toggleClass,</span></span>
<span class="line"><span style="color:#F8F8F2">} </span><span style="color:#F92672">from</span><span style="color:#E6DB74"> '../../..'</span></span>
<span class="line"><span style="color:#F92672">import</span><span style="color:#F8F8F2"> { fetchWithCache } </span><span style="color:#F92672">from</span><span style="color:#E6DB74"> '../../functions/shared/fetch-with-cache'</span></span>
<span class="line"><span style="color:#F92672">import</span><span style="color:#F8F8F2"> { isInternalLink } </span><span style="color:#F92672">from</span><span style="color:#E6DB74"> '../../functions/shared/is-internal-link'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672">export</span><span style="color:#66D9EF;font-style:italic"> type</span><span> </span><span style="color:#A6E22E;text-decoration:underline">ContextRouterProps</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E6DB74"> 'router-pathname'</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span></span>
<span class="line"><span style="color:#E6DB74"> 'router-query'</span><span style="color:#F92672">:</span><span> </span><span style="color:#A6E22E;text-decoration:underline">Record</span><span style="color:#F8F8F2"><</span><span style="color:#66D9EF;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#66D9EF;font-style:italic">string</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F">/* === Exported Contexts === */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672">export</span><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> ROUTER_PATHNAME </span><span style="color:#F92672">=</span><span style="color:#E6DB74"> 'router-pathname'</span><span style="color:#F92672"> as</span><span> </span><span style="color:#A6E22E;text-decoration:underline">Context</span><span style="color:#F8F8F2"><</span></span>
<span class="line"><span style="color:#E6DB74"> 'router-pathname'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span> </span><span style="color:#A6E22E;text-decoration:underline">State</span><span style="color:#F8F8F2"><</span><span style="color:#66D9EF;font-style:italic">string</span><span style="color:#F8F8F2">></span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672">export</span><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> ROUTER_QUERY </span><span style="color:#F92672">=</span><span style="color:#E6DB74"> 'router-query'</span><span style="color:#F92672"> as</span><span> </span><span style="color:#A6E22E;text-decoration:underline">Context</span><span style="color:#F8F8F2"><</span></span>
<span class="line"><span style="color:#E6DB74"> 'router-query'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span> </span><span style="color:#A6E22E;text-decoration:underline">Computed</span><span style="color:#F8F8F2"><</span><span style="color:#A6E22E;text-decoration:underline">Record</span><span style="color:#F8F8F2"><</span><span style="color:#66D9EF;font-style:italic">string</span><span style="color:#F8F8F2">, </span><span style="color:#66D9EF;font-style:italic">string</span><span style="color:#F8F8F2">>></span></span>
<span class="line"><span style="color:#F8F8F2">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F">/* === Component === */</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672">export</span><span style="color:#F92672"> default</span><span style="color:#A6E22E"> component</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E6DB74"> 'context-router'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2"> [ROUTER_PATHNAME]: window.location.pathname,</span></span>
<span class="line"><span style="color:#F8F8F2"> [ROUTER_QUERY]: () </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> queryMap </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> Map</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F92672"> for</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">const</span><span style="color:#F8F8F2"> [key, value] </span><span style="color:#F92672">of</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> URLSearchParams</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2"> window.location.search,</span></span>
<span class="line"><span style="color:#F8F8F2"> )) {</span></span>
<span class="line"><span style="color:#F8F8F2"> queryMap.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(key, </span><span style="color:#A6E22E">state</span><span style="color:#F8F8F2">(value))</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#A6E22E"> getSetParam</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> (</span><span style="color:#FD971F;font-style:italic">key</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">value</span><span style="color:#F92672">?:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">)</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#66D9EF;font-style:italic"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (</span><span style="color:#F92672">!</span><span style="color:#F8F8F2">queryMap.</span><span style="color:#A6E22E">has</span><span style="color:#F8F8F2">(key)) queryMap.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(key, </span><span style="color:#A6E22E">state</span><span style="color:#F8F8F2">(value </span><span style="color:#F92672">??</span><span style="color:#F8F8F2"> UNSET))</span></span>
<span class="line"><span style="color:#F92672"> else</span><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (value </span><span style="color:#F92672">!=</span><span style="color:#AE81FF"> null</span><span style="color:#F8F8F2">) queryMap.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">(key).</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(value)</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> queryMap.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">(key).</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#A6E22E"> syncToURL</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> params </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> URLSearchParams</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F92672"> for</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">const</span><span style="color:#F8F8F2"> [key, signal] </span><span style="color:#F92672">of</span><span style="color:#F8F8F2"> queryMap) {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> value </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> signal.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (value </span><span style="color:#F92672">&&</span><span style="color:#F8F8F2"> value </span><span style="color:#F92672">!==</span><span style="color:#F8F8F2"> UNSET) params.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(key, value)</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> window.history.</span><span style="color:#A6E22E">replaceState</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#AE81FF"> null</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E6DB74"> ''</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#E6DB74"> `</span><span style="color:#F92672">${</span><span style="color:#F8F8F2">window.location.pathname</span><span style="color:#F92672">}</span><span style="color:#E6DB74">?</span><span style="color:#F92672">${</span><span style="color:#F8F8F2">params.</span><span style="color:#A6E22E">toString</span><span style="color:#F8F8F2">()</span><span style="color:#F92672">}${</span><span style="color:#F8F8F2">window.location.hash</span><span style="color:#F92672">}</span><span style="color:#E6DB74">`</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#F8F8F2"> )</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> () </span><span style="color:#66D9EF;font-style:italic">=></span></span>
<span class="line"><span style="color:#F92672"> new</span><span style="color:#A6E22E"> Proxy</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2"> {},</span></span>
<span class="line"><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#A6E22E"> has</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">_</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">prop</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> queryMap.</span><span style="color:#A6E22E">has</span><span style="color:#F8F8F2">(prop)</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#A6E22E"> get</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">_</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">prop</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#A6E22E"> getSetParam</span><span style="color:#F8F8F2">(prop)</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#A6E22E"> set</span><span style="color:#F8F8F2">(</span><span style="color:#FD971F;font-style:italic">_</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">prop</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">value</span><span style="color:#F92672">:</span><span style="color:#66D9EF;font-style:italic"> string</span><span style="color:#F8F8F2">) {</span></span>
<span class="line"><span style="color:#A6E22E"> getSetParam</span><span style="color:#F8F8F2">(prop, value)</span></span>
<span class="line"><span style="color:#A6E22E"> syncToURL</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#AE81FF"> true</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#A6E22E"> ownKeys</span><span style="color:#F8F8F2">() {</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> [</span><span style="color:#F92672">...</span><span style="color:#F8F8F2">queryMap.</span><span style="color:#A6E22E">keys</span><span style="color:#F8F8F2">()]</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2"> )</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2"> (</span><span style="color:#FD971F;font-style:italic">el</span><span style="color:#F8F8F2">, { </span><span style="color:#FD971F;font-style:italic">all</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">first</span><span style="color:#F8F8F2"> }) </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> outlet </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> el.</span><span style="color:#A6E22E">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'outlet'</span><span style="color:#F8F8F2">) </span><span style="color:#F92672">??</span><span style="color:#E6DB74"> 'main'</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> error </span><span style="color:#F92672">=</span><span style="color:#A6E22E"> state</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">''</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Convert all relative links to absolute URLs during setup</span></span>
<span class="line"><span style="color:#F92672"> for</span><span style="color:#F8F8F2"> (</span><span style="color:#66D9EF;font-style:italic">const</span><span style="color:#F8F8F2"> link </span><span style="color:#F92672">of</span><span style="color:#F8F8F2"> el.</span><span style="color:#A6E22E">querySelectorAll</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'a[href]'</span><span style="color:#F8F8F2">)) {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> href </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> link.</span><span style="color:#A6E22E">getAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'href'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (</span></span>
<span class="line"><span style="color:#F8F8F2"> href </span><span style="color:#F92672">&&</span></span>
<span class="line"><span style="color:#F92672"> !</span><span style="color:#F8F8F2">href.</span><span style="color:#A6E22E">startsWith</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'#'</span><span style="color:#F8F8F2">) </span><span style="color:#F92672">&&</span></span>
<span class="line"><span style="color:#F92672"> !</span><span style="color:#F8F8F2">href.</span><span style="color:#A6E22E">includes</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'://'</span><span style="color:#F8F8F2">) </span><span style="color:#F92672">&&</span></span>
<span class="line"><span style="color:#F92672"> !</span><span style="color:#F8F8F2">href.</span><span style="color:#A6E22E">startsWith</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'/'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#F8F8F2"> ) {</span></span>
<span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> absoluteUrl </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> URL</span><span style="color:#F8F8F2">(href, window.location.href)</span></span>
<span class="line"><span style="color:#F8F8F2"> link.</span><span style="color:#A6E22E">setAttribute</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'href'</span><span style="color:#F8F8F2">, absoluteUrl.pathname)</span></span>
<span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#88846F"> // Skip invalid URLs</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> content </span><span style="color:#F92672">=</span><span style="color:#A6E22E"> computed</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">async</span><span style="color:#FD971F;font-style:italic"> abort</span><span style="color:#66D9EF;font-style:italic"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> currentPath </span><span style="color:#F92672">=</span><span style="color:#A6E22E"> String</span><span style="color:#F8F8F2">(el[ROUTER_PATHNAME])</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> url </span><span style="color:#F92672">=</span><span style="color:#A6E22E"> String</span><span style="color:#F8F8F2">(</span><span style="color:#F92672">new</span><span style="color:#A6E22E"> URL</span><span style="color:#F8F8F2">(currentPath, window.location.origin))</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (abort?.aborted) </span><span style="color:#F92672">return</span><span style="color:#F8F8F2"> content.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672"> try</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2"> error.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">''</span><span style="color:#F8F8F2">)</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> { content: html } </span><span style="color:#F92672">=</span><span style="color:#F92672"> await</span><span style="color:#A6E22E"> fetchWithCache</span><span style="color:#F8F8F2">(url, abort)</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> doc </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> DOMParser</span><span style="color:#F8F8F2">().</span><span style="color:#A6E22E">parseFromString</span><span style="color:#F8F8F2">(html, </span><span style="color:#E6DB74">'text/html'</span><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Update title and URL</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> newTitle </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> doc.</span><span style="color:#A6E22E">querySelector</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'title'</span><span style="color:#F8F8F2">)?.textContent</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (newTitle) document.title </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> newTitle</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (currentPath </span><span style="color:#F92672">!==</span><span style="color:#F8F8F2"> window.location.pathname)</span></span>
<span class="line"><span style="color:#F8F8F2"> window.history.</span><span style="color:#A6E22E">pushState</span><span style="color:#F8F8F2">({}, </span><span style="color:#E6DB74">''</span><span style="color:#F8F8F2">, url)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> doc.</span><span style="color:#A6E22E">querySelector</span><span style="color:#F8F8F2">(outlet)?.innerHTML </span><span style="color:#F92672">??</span><span style="color:#E6DB74"> ''</span></span>
<span class="line"><span style="color:#F8F8F2"> } </span><span style="color:#F92672">catch</span><span style="color:#F8F8F2"> (err) {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> errorMessage </span><span style="color:#F92672">=</span><span style="color:#E6DB74"> `Navigation failed: </span><span style="color:#F92672">${</span><span style="color:#F8F8F2">err </span><span style="color:#F92672">instanceof</span><span> </span><span style="color:#A6E22E;text-decoration:underline">Error</span><span style="color:#F92672"> ?</span><span style="color:#F8F8F2"> err.message </span><span style="color:#F92672">:</span><span style="color:#A6E22E"> String</span><span style="color:#F8F8F2">(err)</span><span style="color:#F92672">}</span><span style="color:#E6DB74">`</span></span>
<span class="line"><span style="color:#F8F8F2"> error.</span><span style="color:#A6E22E">set</span><span style="color:#F8F8F2">(errorMessage)</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> content.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">() </span><span style="color:#88846F">// Keep current content on error</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> })</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> [</span></span>
<span class="line"><span style="color:#88846F"> // Provide contexts</span></span>
<span class="line"><span style="color:#A6E22E"> provideContexts</span><span style="color:#F8F8F2">([ROUTER_PATHNAME, ROUTER_QUERY]),</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Navigate and update 'active' class</span></span>
<span class="line"><span style="color:#A6E22E"> all</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E6DB74"> 'a[href]:not([href^="#"])'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#A6E22E"> toggleClass</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E6DB74"> 'active'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#FD971F;font-style:italic"> target</span><span style="color:#66D9EF;font-style:italic"> =></span></span>
<span class="line"><span style="color:#A6E22E"> isInternalLink</span><span style="color:#F8F8F2">(target) </span><span style="color:#F92672">&&</span></span>
<span class="line"><span style="color:#F8F8F2"> el[ROUTER_PATHNAME] </span><span style="color:#F92672">===</span><span style="color:#F8F8F2"> target.pathname,</span></span>
<span class="line"><span style="color:#F8F8F2"> ),</span></span>
<span class="line"><span style="color:#A6E22E"> on</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'click'</span><span style="color:#F8F8F2">, </span><span style="color:#FD971F;font-style:italic">e</span><span style="color:#66D9EF;font-style:italic"> =></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (</span><span style="color:#F92672">!</span><span style="color:#A6E22E">isInternalLink</span><span style="color:#F8F8F2">(e.target)) </span><span style="color:#F92672">return</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#F8F8F2"> url </span><span style="color:#F92672">=</span><span style="color:#F92672"> new</span><span style="color:#A6E22E"> URL</span><span style="color:#F8F8F2">(e.target.href)</span></span>
<span class="line"><span style="color:#F92672"> if</span><span style="color:#F8F8F2"> (url.origin </span><span style="color:#F92672">===</span><span style="color:#F8F8F2"> window.location.origin) {</span></span>
<span class="line"><span style="color:#F8F8F2"> e.</span><span style="color:#A6E22E">preventDefault</span><span style="color:#F8F8F2">()</span></span>
<span class="line"><span style="color:#F8F8F2"> el[ROUTER_PATHNAME] </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> url.pathname</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> }),</span></span>
<span class="line"><span style="color:#F8F8F2"> ),</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Render content</span></span>
<span class="line"><span style="color:#A6E22E"> first</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#F8F8F2"> outlet,</span></span>
<span class="line"><span style="color:#A6E22E"> dangerouslySetInnerHTML</span><span style="color:#F8F8F2">(content, { allowScripts: </span><span style="color:#AE81FF">true</span><span style="color:#F8F8F2"> }),</span></span>
<span class="line"><span style="color:#F8F8F2"> ),</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Error display with aria-live</span></span>
<span class="line"><span style="color:#A6E22E"> first</span><span style="color:#F8F8F2">(</span></span>
<span class="line"><span style="color:#E6DB74"> 'card-callout'</span><span style="color:#F8F8F2">,</span></span>
<span class="line"><span style="color:#A6E22E"> show</span><span style="color:#F8F8F2">(() </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F92672"> !!</span><span style="color:#F8F8F2">error.</span><span style="color:#A6E22E">get</span><span style="color:#F8F8F2">()),</span></span>
<span class="line"><span style="color:#F8F8F2"> ),</span></span>
<span class="line"><span style="color:#A6E22E"> first</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'.error'</span><span style="color:#F8F8F2">, </span><span style="color:#A6E22E">setText</span><span style="color:#F8F8F2">(error)),</span></span>
<span class="line"></span>
<span class="line"><span style="color:#88846F"> // Handle browser history navigation</span></span>
<span class="line"><span style="color:#F8F8F2"> () </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> const</span><span style="color:#A6E22E"> handlePopState</span><span style="color:#F92672"> =</span><span style="color:#F8F8F2"> () </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2"> el[ROUTER_PATHNAME] </span><span style="color:#F92672">=</span><span style="color:#F8F8F2"> window.location.pathname</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> window.</span><span style="color:#A6E22E">addEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'popstate'</span><span style="color:#F8F8F2">, handlePopState)</span></span>
<span class="line"><span style="color:#F92672"> return</span><span style="color:#F8F8F2"> () </span><span style="color:#66D9EF;font-style:italic">=></span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#F8F8F2"> window.</span><span style="color:#A6E22E">removeEventListener</span><span style="color:#F8F8F2">(</span><span style="color:#E6DB74">'popstate'</span><span style="color:#F8F8F2">, handlePopState)</span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2"> ]</span></span>
<span class="line"><span style="color:#F8F8F2"> },</span></span>
<span class="line"><span style="color:#F8F8F2">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#F92672">declare</span><span style="color:#F8F8F2"> global {</span></span>
<span class="line"><span style="color:#66D9EF;font-style:italic"> interface</span><span> </span><span style="color:#A6E22E;text-decoration:underline">HTMLElementTagNameMap</span><span style="color:#F8F8F2"> {</span></span>
<span class="line"><span style="color:#E6DB74"> 'context-router'</span><span style="color:#F92672">:</span><span> </span><span style="color:#A6E22E;text-decoration:underline">Component</span><span style="color:#F8F8F2"><{}></span></span>
<span class="line"><span style="color:#F8F8F2"> }</span></span>
<span class="line"><span style="color:#F8F8F2">}</span></span>
<span class="line"></span></code></pre>
<basic-button class="copy">
<button type="button" class="secondary small">
<span class="label">Copy</span>
</button>
</basic-button>
</module-codeblock>
</div>
</module-tabgroup>