Caleb Porzio 2 lat temu
rodzic
commit
532c10f1d1

+ 44 - 26
packages/navigate/src/index.js

@@ -1,6 +1,6 @@
 import { updateCurrentPageHtmlInHistoryStateForLaterBackButtonClicks, updateUrlAndStoreLatestHtmlForFutureBackButtons, whenTheBackOrForwardButtonIsClicked } from "./history"
 import { getPretchedHtmlOr, prefetchHtml, storeThePrefetchedHtmlForWhenALinkIsClicked } from "./prefetch"
-import { extractDestinationFromLink, whenThisLinkIsClicked, whenThisLinkIsHovered } from "./links"
+import { createUrlObjectFromString, extractDestinationFromLink, whenThisLinkIsHoveredFor, whenThisLinkIsPressed } from "./links"
 import { restoreScrollPosition, storeScrollInformationInHtmlBeforeNavigatingAway } from "./scroll"
 import { putPersistantElementsBack, storePersistantElementsForLater } from "./persist"
 import { finishAndHideProgressBar, showAndStartProgressBar } from "./bar"
@@ -14,52 +14,70 @@ let restoreScroll = true
 let autofocus = false
 
 export default function (Alpine) {
+    Alpine.navigate = (url) => {
+        navigateTo(
+            createUrlObjectFromString(url)
+        )
+    }
+
     Alpine.directive('navigate', (el, { value, expression, modifiers }, { evaluateLater, cleanup }) => {
-        let shouldPrefetch = modifiers.includes('prefetch')
+        // "persisted" elements will be picked up by .querySelector, not this callback...
+        if (value === 'persist') return
+
+        let shouldPrefetch = modifiers.includes('prefetch') && modifiers.includes('hover')
 
-        shouldPrefetch && whenThisLinkIsHovered(el, () => {
-            let forDestination = extractDestinationFromLink(el)
+        shouldPrefetch && whenThisLinkIsHoveredFor(el, 60, () => {
+            let destination = extractDestinationFromLink(el)
 
-            prefetchHtml(forDestination, html => {
-                storeThePrefetchedHtmlForWhenALinkIsClicked(html, forDestination)
+            prefetchHtml(destination, html => {
+                storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination)
             })
         })
 
-        whenThisLinkIsClicked(el, () => {
-            showProgressBar && showAndStartProgressBar()
+        whenThisLinkIsPressed(el, (whenItIsReleased) => {
+            let destination = extractDestinationFromLink(el)
+
+            prefetchHtml(destination, html => {
+                storeThePrefetchedHtmlForWhenALinkIsClicked(html, destination)
+            })
+
+            whenItIsReleased(() => {
+                navigateTo(destination)
+            })
+        })
+    })
 
-            let fromDestination = extractDestinationFromLink(el)
+    function navigateTo(destination) {
+        showProgressBar && showAndStartProgressBar()
 
-            fetchHtmlOrUsePrefetchedHtml(fromDestination, html => {
-                restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway()
+        fetchHtmlOrUsePrefetchedHtml(destination, html => {
+            restoreScroll && storeScrollInformationInHtmlBeforeNavigatingAway()
 
-                showProgressBar && finishAndHideProgressBar()
+            showProgressBar && finishAndHideProgressBar()
 
-                updateCurrentPageHtmlInHistoryStateForLaterBackButtonClicks()
+            updateCurrentPageHtmlInHistoryStateForLaterBackButtonClicks()
 
-                preventAlpineFromPickingUpDomChanges(Alpine, andAfterAllThis => {
-                    enablePersist && storePersistantElementsForLater()
+            preventAlpineFromPickingUpDomChanges(Alpine, andAfterAllThis => {
+                enablePersist && storePersistantElementsForLater()
 
-                    swapCurrentPageWithNewHtml(html, () => {
-                        enablePersist && putPersistantElementsBack()
+                swapCurrentPageWithNewHtml(html, () => {
+                    enablePersist && putPersistantElementsBack()
 
-                        restoreScroll && restoreScrollPosition()
+                    restoreScroll && restoreScrollPosition()
 
-                        fireEventForOtherLibariesToHookInto()
+                    fireEventForOtherLibariesToHookInto()
 
-                        updateUrlAndStoreLatestHtmlForFutureBackButtons(html, fromDestination)
+                    updateUrlAndStoreLatestHtmlForFutureBackButtons(html, destination)
 
-                        andAfterAllThis(() => {
-                            autofocus && autofocusElementsWithTheAutofocusAttribute()
+                    andAfterAllThis(() => {
+                        autofocus && autofocusElementsWithTheAutofocusAttribute()
 
-                            nowInitializeAlpineOnTheNewPage(Alpine)
-                        })
+                        nowInitializeAlpineOnTheNewPage(Alpine)
                     })
                 })
             })
         })
-    })
-
+    }
 
     whenTheBackOrForwardButtonIsClicked((html) => {
         // @todo: see if there's a way to update the current HTML BEFORE

+ 37 - 3
packages/navigate/src/links.js

@@ -7,12 +7,46 @@ export function whenThisLinkIsClicked(el, callback) {
     })
 }
 
-export function whenThisLinkIsHovered(el, callback) {
+export function whenThisLinkIsPressed(el, callback) {
+    el.addEventListener('click', e => e.preventDefault())
+
+    el.addEventListener('mousedown', e => {
+        e.preventDefault()
+
+        callback((whenReleased) => {
+            let handler = e => {
+                e.preventDefault()
+
+                whenReleased()
+
+                el.removeEventListener('mouseup', handler)
+            }
+
+            el.addEventListener('mouseup', handler)
+        })
+    })
+}
+
+export function whenThisLinkIsHoveredFor(el, ms = 60, callback) {
     el.addEventListener('mouseenter', e => {
-        callback(e)
+        let timeout = setTimeout(() => {
+            callback(e)
+        }, ms)
+
+        let handler = () => {
+            clearTimeout(timeout)
+
+            el.removeEventListener('mouseleave', handler)
+        }
+
+        el.addEventListener('mouseleave', handler)
     })
 }
 
 export function extractDestinationFromLink(linkEl) {
-    return new URL(linkEl.getAttribute('href'), document.baseURI)
+    return createUrlObjectFromString(linkEl.getAttribute('href'))
+}
+
+export function createUrlObjectFromString(urlString) {
+    return new URL(urlString, document.baseURI)
 }

+ 14 - 2
packages/navigate/src/page.js

@@ -7,6 +7,8 @@ export function swapCurrentPageWithNewHtml(html, andThen) {
 
     mergeNewHead(newHead)
 
+    // mergeNewHead(newHead)
+
     prepNewScriptTagsToRun(newBody)
 
     transitionOut(document.body)
@@ -58,6 +60,12 @@ function mergeNewHead(newHead) {
     for (child of Array.from(newHead.children)) {
         if (isAsset(child)) {
             if (! headChildrenHtmlLookup.includes(child.outerHTML)) {
+                if (isTracked(child)) {
+                    setTimeout(() => window.location.reload())
+
+                    return
+                }
+
                 if (isScript(child)) {
                     document.head.appendChild(cloneScriptTag(child))
                 } else {
@@ -95,13 +103,17 @@ function cloneScriptTag(el) {
     return script
 }
 
-function isAsset (el) {
+function isTracked(el) {
+    return el.hasAttribute('data-navigate-track')
+}
+
+function isAsset(el) {
     return (el.tagName.toLowerCase() === 'link' && el.getAttribute('rel').toLowerCase() === 'stylesheet')
         || el.tagName.toLowerCase() === 'style'
         || el.tagName.toLowerCase() === 'script'
 }
 
-function isScript (el)   {
+function isScript(el)   {
     return el.tagName.toLowerCase() === 'script'
 }