mirror of
https://github.com/dillonkearns/elm-pages-v3-beta.git
synced 2024-12-25 04:43:03 +03:00
Add a prefetch attribute.
This commit is contained in:
parent
e308b38534
commit
d2bf1e1204
@ -8,6 +8,7 @@ import Date exposing (Date)
|
||||
import OptimizedDecoder
|
||||
import Pages.ImagePath exposing (ImagePath)
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Route
|
||||
|
||||
|
||||
type alias BlogPost =
|
||||
@ -26,7 +27,7 @@ blogPostsGlob =
|
||||
|> Glob.toDataSource
|
||||
|
||||
|
||||
allMetadata : DataSource.DataSource (List ( PagePath, ArticleMetadata ))
|
||||
allMetadata : DataSource.DataSource (List ( Route.Route, ArticleMetadata ))
|
||||
allMetadata =
|
||||
--StaticFile.glob "content/blog/*.md"
|
||||
blogPostsGlob
|
||||
@ -36,7 +37,7 @@ allMetadata =
|
||||
|> List.map
|
||||
(\{ filePath, slug } ->
|
||||
DataSource.map2 Tuple.pair
|
||||
(DataSource.succeed <| "blog/" ++ slug)
|
||||
(DataSource.succeed <| Route.Blog__Slug_ { slug = slug })
|
||||
(StaticFile.request filePath (StaticFile.frontmatter frontmatterDecoder))
|
||||
)
|
||||
)
|
||||
@ -45,15 +46,12 @@ allMetadata =
|
||||
(\articles ->
|
||||
articles
|
||||
|> List.filterMap
|
||||
(\( path, metadata ) ->
|
||||
(\( route, metadata ) ->
|
||||
if metadata.draft then
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just
|
||||
( path |> PagePath.external
|
||||
, metadata
|
||||
)
|
||||
Just ( route, metadata )
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
module Page.Blog exposing (Data, Model, Msg, page)
|
||||
|
||||
import Article
|
||||
import Data.Author
|
||||
import DataSource
|
||||
import Date
|
||||
import Document exposing (Document)
|
||||
@ -11,7 +10,7 @@ import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes as Attr exposing (css)
|
||||
import Page exposing (DynamicContext, PageWithState, StaticPayload)
|
||||
import Pages.ImagePath as ImagePath
|
||||
import Pages.PagePath as PagePath exposing (PagePath)
|
||||
import Route exposing (Route)
|
||||
import Shared
|
||||
import SiteOld
|
||||
import Tailwind.Breakpoints as Bp
|
||||
@ -42,7 +41,7 @@ data =
|
||||
|
||||
|
||||
type alias Data =
|
||||
List ( PagePath, Article.ArticleMetadata )
|
||||
List ( Route, Article.ArticleMetadata )
|
||||
|
||||
|
||||
init :
|
||||
@ -194,11 +193,25 @@ head staticPayload =
|
||||
|> Seo.website
|
||||
|
||||
|
||||
blogCard : ( PagePath, Article.ArticleMetadata ) -> Html msg
|
||||
blogCard ( path, info ) =
|
||||
link : Route.Route -> List (Attribute msg) -> List (Html msg) -> Html msg
|
||||
link route attrs children =
|
||||
a
|
||||
[ Attr.href (PagePath.toString path)
|
||||
, css
|
||||
(Attr.href
|
||||
("/"
|
||||
++ (Route.routeToPath (Just route)
|
||||
|> String.join "/"
|
||||
)
|
||||
)
|
||||
:: Attr.attribute "elm-pages:prefetch" ""
|
||||
:: attrs
|
||||
)
|
||||
children
|
||||
|
||||
|
||||
blogCard : ( Route, Article.ArticleMetadata ) -> Html msg
|
||||
blogCard ( route, info ) =
|
||||
link route
|
||||
[ css
|
||||
[ Tw.flex
|
||||
, Tw.flex_col
|
||||
, Tw.rounded_lg
|
||||
|
@ -138,6 +138,7 @@ headerLink : PagePath -> String -> String -> Html msg
|
||||
headerLink currentPagePath linkTo name =
|
||||
a
|
||||
[ Attr.href ("/" ++ linkTo)
|
||||
, Attr.attribute "elm-pages:prefetch" "true"
|
||||
]
|
||||
[ linkInner currentPagePath linkTo name ]
|
||||
|
||||
|
@ -2,7 +2,6 @@ import userInit from "/index.js";
|
||||
|
||||
let prefetchedPages;
|
||||
let initialLocationHash;
|
||||
let elmViewRendered = false;
|
||||
|
||||
function pagesInit(
|
||||
/** @type { mainElmModule: { init: any } } */ { mainElmModule }
|
||||
@ -12,14 +11,6 @@ function pagesInit(
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
document.addEventListener("DOMContentLoaded", (_) => {
|
||||
new MutationObserver(function () {
|
||||
elmViewRendered = true;
|
||||
}).observe(document.body, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
loadContentAndInitializeApp(mainElmModule).then(resolve, reject);
|
||||
});
|
||||
});
|
||||
@ -58,28 +49,14 @@ function loadContentAndInitializeApp(
|
||||
},
|
||||
});
|
||||
|
||||
app.ports.toJsPort.subscribe((
|
||||
/** @type { { allRoutes: string[] } } */ fromElm
|
||||
) => {
|
||||
window.allRoutes = fromElm.allRoutes.map(
|
||||
(route) => new URL(route, document.baseURI).href
|
||||
);
|
||||
|
||||
setupLinkPrefetching();
|
||||
app.ports.toJsPort.subscribe((fromElm) => {
|
||||
loadNamedAnchor();
|
||||
});
|
||||
|
||||
return app;
|
||||
});
|
||||
}
|
||||
|
||||
function setupLinkPrefetching() {
|
||||
new MutationObserver(observeFirstRender).observe(document.body, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
function loadNamedAnchor() {
|
||||
if (initialLocationHash !== "") {
|
||||
const namedAnchor = document.querySelector(`[name=${initialLocationHash}]`);
|
||||
@ -87,62 +64,14 @@ function loadNamedAnchor() {
|
||||
}
|
||||
}
|
||||
|
||||
function observeFirstRender(
|
||||
/** @type {MutationRecord[]} */ mutationList,
|
||||
/** @type {MutationObserver} */ firstRenderObserver
|
||||
) {
|
||||
loadNamedAnchor();
|
||||
for (let mutation of mutationList) {
|
||||
if (mutation.type === "childList") {
|
||||
setupLinkPrefetchingHelp();
|
||||
}
|
||||
}
|
||||
firstRenderObserver.disconnect();
|
||||
new MutationObserver(observeUrlChanges).observe(document.body.children[0], {
|
||||
attributes: true,
|
||||
childList: false,
|
||||
subtree: false,
|
||||
});
|
||||
}
|
||||
|
||||
function observeUrlChanges(
|
||||
/** @type {MutationRecord[]} */ mutationList,
|
||||
/** @type {MutationObserver} */ _theObserver
|
||||
) {
|
||||
for (let mutation of mutationList) {
|
||||
if (
|
||||
mutation.type === "attributes" &&
|
||||
mutation.attributeName === "data-url"
|
||||
) {
|
||||
setupLinkPrefetchingHelp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupLinkPrefetchingHelp(
|
||||
/** @type {MutationObserver} */ _mutationList,
|
||||
/** @type {MutationObserver} */ _theObserver
|
||||
) {
|
||||
const links = document.querySelectorAll("a");
|
||||
links.forEach((link) => {
|
||||
// console.log(link.pathname);
|
||||
link.addEventListener("mouseenter", function (event) {
|
||||
if (event && event.target && event.target instanceof HTMLAnchorElement) {
|
||||
prefetchIfNeeded(event.target);
|
||||
} else {
|
||||
// console.log("Couldn't prefetch with event", event);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function prefetchIfNeeded(/** @type {HTMLAnchorElement} */ target) {
|
||||
if (target.host === window.location.host) {
|
||||
if (prefetchedPages.includes(target.pathname)) {
|
||||
// console.log("Already preloaded", target.href);
|
||||
// console.log("Not a known route, skipping preload", target.pathname);
|
||||
} else if (
|
||||
!allRoutes.includes(new URL(target.pathname, document.baseURI).href)
|
||||
// !allRoutes.includes(new URL(target.pathname, document.baseURI).href)
|
||||
false
|
||||
) {
|
||||
} else {
|
||||
prefetchedPages.push(target.pathname);
|
||||
@ -182,3 +111,38 @@ if (typeof connect === "function") {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {MouseEvent} event */
|
||||
const trigger_prefetch = (event) => {
|
||||
const a = find_anchor(/** @type {Node} */ (event.target));
|
||||
if (a && a.href && a.hasAttribute("elm-pages:prefetch")) {
|
||||
console.log("PREFETCH", a.href);
|
||||
prefetchIfNeeded(a);
|
||||
// this.prefetch(new URL(/** @type {string} */ (a.href)));
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {NodeJS.Timeout} */
|
||||
let mousemove_timeout;
|
||||
|
||||
/** @param {MouseEvent} event */
|
||||
const handle_mousemove = (event) => {
|
||||
clearTimeout(mousemove_timeout);
|
||||
mousemove_timeout = setTimeout(() => {
|
||||
trigger_prefetch(event);
|
||||
}, 20);
|
||||
};
|
||||
|
||||
addEventListener("touchstart", trigger_prefetch);
|
||||
addEventListener("mousemove", handle_mousemove);
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
// * @rturns {HTMLAnchorElement | SVGAElement}
|
||||
* @returns {HTMLAnchorElement}
|
||||
*/
|
||||
function find_anchor(node) {
|
||||
while (node && node.nodeName.toUpperCase() !== "A") node = node.parentNode; // SVG <a> elements have a lowercase name
|
||||
// return /** @type {HTMLAnchorElement | SVGAElement} */ (node);
|
||||
return /** @type {HTMLAnchorElement} */ (node);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
"strict": true,
|
||||
"sourceMap": false,
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["es2015", "es2017.object", "esnext"]
|
||||
"lib": ["es2015", "es2017.object", "esnext", "dom"]
|
||||
},
|
||||
"exclude": ["node_modules", "generated"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user