Refactored header autohiding and split overrides

This commit is contained in:
squidfunk 2021-02-12 15:44:53 +01:00
parent f7907fc09c
commit 28d507ecfe
28 changed files with 183 additions and 77 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,16 +1,18 @@
{
"assets/javascripts/bundle.js": "assets/javascripts/bundle.7e5d1283.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.7e5d1283.min.js.map",
"assets/javascripts/vendor.js": "assets/javascripts/vendor.9418a44e.min.js",
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.9418a44e.min.js.map",
"assets/javascripts/bundle.js": "assets/javascripts/bundle.2bc3a92a.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.2bc3a92a.min.js.map",
"assets/javascripts/vendor.js": "assets/javascripts/vendor.6b8ac966.min.js",
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.6b8ac966.min.js.map",
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.b9424174.min.js",
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.b9424174.min.js.map",
"assets/stylesheets/main.css": "assets/stylesheets/main.c7b58751.min.css",
"assets/stylesheets/main.css.map": "assets/stylesheets/main.c7b58751.min.css.map",
"assets/stylesheets/palette.css": "assets/stylesheets/palette.d4a7bf42.min.css",
"assets/stylesheets/palette.css.map": "assets/stylesheets/palette.d4a7bf42.min.css.map",
"overrides/assets/javascripts/bundle.js": "overrides/assets/javascripts/bundle.75e3dcac.min.js",
"overrides/assets/javascripts/bundle.js.map": "overrides/assets/javascripts/bundle.75e3dcac.min.js.map",
"overrides/assets/stylesheets/main.css": "overrides/assets/stylesheets/main.dadc6109.min.css",
"overrides/assets/stylesheets/main.css.map": "overrides/assets/stylesheets/main.dadc6109.min.css.map"
"assets/stylesheets/main.css": "assets/stylesheets/main.6ced4fc6.min.css",
"assets/stylesheets/main.css.map": "assets/stylesheets/main.6ced4fc6.min.css.map",
"assets/stylesheets/palette.css": "assets/stylesheets/palette.e70b70b6.min.css",
"assets/stylesheets/palette.css.map": "assets/stylesheets/palette.e70b70b6.min.css.map",
"overrides/assets/javascripts/bundle.js": "overrides/assets/javascripts/bundle.b79ba6f2.min.js",
"overrides/assets/javascripts/bundle.js.map": "overrides/assets/javascripts/bundle.b79ba6f2.min.js.map",
"overrides/assets/javascripts/vendor.js": "overrides/assets/javascripts/vendor.1aa446d9.min.js",
"overrides/assets/javascripts/vendor.js.map": "overrides/assets/javascripts/vendor.1aa446d9.min.js.map",
"overrides/assets/stylesheets/main.css": "overrides/assets/stylesheets/main.8e98f424.min.css",
"overrides/assets/stylesheets/main.css.map": "overrides/assets/stylesheets/main.8e98f424.min.css.map"
}

View File

@ -39,10 +39,10 @@
{% endif %}
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.c7b58751.min.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.6ced4fc6.min.css' | url }}">
{% if config.theme.palette %}
{% set palette = config.theme.palette %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.d4a7bf42.min.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.e70b70b6.min.css' | url }}">
{% if palette.primary %}
{% import "partials/palette.html" as map %}
{% set primary = map.primary(
@ -191,7 +191,7 @@
"base": base_url,
"features": features,
"translations": {},
"search": "assets/javascripts/worker/search.js" | url,
"search": "assets/javascripts/worker/search.b9424174.min.js" | url,
} -%}
{%- set translations = app.translations -%}
{%- for key in [
@ -216,8 +216,8 @@
</script>
{% endblock %}
{% block scripts %}
<script src="{{ 'assets/javascripts/vendor.9418a44e.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.7e5d1283.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/vendor.6b8ac966.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.2bc3a92a.min.js' | url }}"></script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(t){for(var r,i,a=t[0],s=t[1],u=t[2],f=0,p=[];f<a.length;f++)i=a[f],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&p.push(o[i][0]),o[i]=0;for(r in s)Object.prototype.hasOwnProperty.call(s,r)&&(e[r]=s[r]);for(l&&l(t);p.length;)p.shift()();return c.push.apply(c,u||[]),n()}function n(){for(var e,t=0;t<c.length;t++){for(var n=c[t],r=!0,a=1;a<n.length;a++){var s=n[a];0!==o[s]&&(r=!1)}r&&(c.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},o={0:0},c=[];function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="";var a=window.webpackJsonp=window.webpackJsonp||[],s=a.push.bind(a);a.push=t,a=a.slice();for(var u=0;u<a.length;u++)t(a[u]);var l=s;return c.push([37,1]),n()}({37:function(e,t,n){"use strict";n.r(t);var r=n(20),o=n(3),c=n(38),i=n(45),a=n(29),s=n(25);n(32),n(39);function u(e,t=document){return t.querySelector(e)||void 0}function l(e,t=document){const n=u(e,t);if(void 0===n)throw new ReferenceError(`Missing element: expected "${e}" to be present`);return n}n(50);var f=n(51);var p=n(17),d=n(40),b=n(41),g=n(42),h=n(43),v=n(44);n(46);const O=new p.a;Object(d.a)(()=>Object(b.a)(new ResizeObserver(e=>{for(const t of e)O.next(t)}))).pipe(Object(a.a)(e=>g.a.pipe(Object(f.a)(e)).pipe(Object(h.a)(()=>e.disconnect()))),Object(v.a)(1));n(31);n(47);l("[data-md-toggle=drawer]"),l("[data-md-toggle=search]");n(52),n(53);n(48),n(49);function j(e,t){if("string"==typeof t||"number"==typeof t)e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(const n of t)j(e,n)}function m(e,t,...n){const r=document.createElement(e);if(t)for(const e of Object.keys(t))"boolean"!=typeof t[e]?r.setAttribute(e,t[e]):t[e]&&r.setAttribute(e,"");for(const e of n)j(r,e);return r}function y(e,t){return t.length?m("div",{class:""},m("span",null,function(e){if(e>999){return((e+1e-6)/1e3).toFixed(+((e-950)%1e3>99))+"k"}return e.toString()}(e.length)," results"),m("ul",{class:"tx-icon-search__list"},e.slice(0,10).map(e=>m("li",{class:"tx-icon-search__item"},m("span",{class:"twemoji"},m("img",{src:"https://raw.githubusercontent.com/squidfunk/mkdocs-material/master/material/.icons/"+e,style:"width: 18px; height: 18px"}))," ",m("button",{class:"md-clipboard--inline","data-clipboard-text":":"+e.replace(/\.svg$/,"").replace(/\//g,"-")+":"},m("code",null,function(e,t){return`:${Object(r.wrap)(e.replace(/\.svg$/,"").replace(/\//g,"-"),t,{wrap:{tagOpen:"<b>",tagClose:"</b>"}})}:`}(e,t))))))):m("div",{class:""})}const w=l("#__config"),x=JSON.parse(w.textContent),_=Object(o.a)(fetch(x.base+"/overrides/assets/javascripts/icons.json").then(e=>e.json())),k=u("#icon-search");k&&_.pipe(Object(i.a)(console.log),Object(a.a)(e=>Object(c.a)(k,"keyup").pipe(Object(s.a)(()=>Object(r.filter)(e,k.value))))).subscribe(e=>{const t=l(".tx-icon-result");t.innerHTML="",t.appendChild(y(e,k.value))}),Object(c.a)(document.body,"click").subscribe(e=>{if(e.target instanceof HTMLElement){e.target.closest("a[href^=http]")instanceof HTMLLinkElement&&ga("send","event","outbound","click",w.href)}})}}));
//# sourceMappingURL=bundle.b79ba6f2.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@
<meta name="twitter:title" content="{{ title }}">
<meta name="twitter:description" content="{{ config.site_description }}">
<meta name="twitter:image" content="{{ image }}">
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.dadc6109.min.css' | url }}">
<link rel="stylesheet" href="{{ 'overrides/assets/stylesheets/main.8e98f424.min.css' | url }}">
{% endblock %}
{% block announce %}
<a href="https://twitter.com/squidfunk">
@ -53,14 +53,6 @@
{% endblock %}
{% block scripts %}
{{ super() }}
{% block config %}
{%- set configuration = {
"base": base_url,
"features": features
} -%}
<script id="__config" type="application/json">
{{- configuration | tojson -}}
</script>
{% endblock %}
<script src="{{ 'overrides/assets/javascripts/bundle.75e3dcac.min.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/vendor.1aa446d9.min.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/bundle.b79ba6f2.min.js' | url }}"></script>
{% endblock %}

View File

@ -21,23 +21,34 @@
*/
import {
NEVER,
Observable,
Subject,
animationFrameScheduler,
combineLatest,
defer,
of
} from "rxjs"
import {
bufferCount,
combineLatestWith,
distinctUntilChanged,
distinctUntilKeyChanged,
filter,
map,
observeOn,
shareReplay
shareReplay,
startWith,
switchMap
} from "rxjs/operators"
import { feature } from "~/_"
import { resetHeaderState, setHeaderState } from "~/actions"
import { Viewport, watchElementSize } from "~/browser"
import {
Viewport,
watchElementSize,
watchToggle
} from "~/browser"
import { Component } from "../../_"
import { Main } from "../../main"
@ -50,14 +61,22 @@ import { Main } from "../../main"
* Header
*/
export interface Header {
sticky: boolean /* Header stickyness */
height: number /* Header visible height */
sticky: boolean /* Header stickyness */
hidden: boolean /* User scrolled past threshold */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Watch options
*/
interface WatchOptions {
viewport$: Observable<Viewport> /* Viewport observable */
}
/**
* Mount options
*/
@ -67,6 +86,52 @@ interface MountOptions {
main$: Observable<Main> /* Main area observable */
}
/* ----------------------------------------------------------------------------
* Helper functions
* ------------------------------------------------------------------------- */
/**
* Compute whether the header is hidden
*
* If the user scrolls past a certain threshold, the header can be hidden when
* scrolling down, and shown when scrolling up.
*
* @param options - Options
*
* @returns Toggle observable
*/
function isHidden({ viewport$ }: WatchOptions): Observable<boolean> {
if (!feature("header.autohide"))
return of(false)
/* Compute direction and turning point */
const direction$ = viewport$
.pipe(
map(({ offset: { y } }) => y),
bufferCount(2, 1),
map(([a, b]) => [a < b, b] as const),
distinctUntilKeyChanged(0)
)
/* Compute whether header should be hidden */
const hidden$ = combineLatest([viewport$, direction$])
.pipe(
filter(([{ offset }, [, y]]) => Math.abs(y - offset.y) > 100),
map(([, [direction]]) => direction),
distinctUntilChanged(),
)
/* Compute threshold for autohiding */
const search$ = watchToggle("search")
return combineLatest([viewport$, search$])
.pipe(
map(([{ offset }, search]) => offset.y > 400 && !search),
distinctUntilChanged(),
switchMap(active => active ? hidden$ : NEVER),
startWith(false)
)
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -75,11 +140,12 @@ interface MountOptions {
* Watch header
*
* @param el - Header element
* @param options - Options
*
* @returns Header observable
*/
export function watchHeader(
el: HTMLElement
el: HTMLElement, options: WatchOptions
): Observable<Header> {
return defer(() => {
const styles = getComputedStyle(el)
@ -89,14 +155,16 @@ export function watchHeader(
)
})
.pipe(
combineLatestWith(watchElementSize(el)),
map(([sticky, { height }]) => ({
combineLatestWith(watchElementSize(el), isHidden(options)),
map(([sticky, { height }, hidden]) => ({
height: sticky ? height : 0,
sticky,
height: sticky ? height : 0
hidden
})),
distinctUntilChanged((a, b) => (
a.sticky === b.sticky &&
a.height === b.height
a.height === b.height &&
a.hidden === b.hidden
)),
shareReplay(1)
)
@ -122,11 +190,12 @@ export function mountHeader(
internal$
.pipe(
distinctUntilKeyChanged("active"),
combineLatestWith(header$),
observeOn(animationFrameScheduler)
)
.subscribe(({ active }) => {
.subscribe(([{ active }, { hidden }]) => {
if (active)
setHeaderState(el, "shadow")
setHeaderState(el, hidden ? "hidden" : "shadow")
else
resetHeaderState(el)
})

View File

@ -88,7 +88,8 @@ if (feature("navigation.instant"))
/* Set up header observable */
const header$ = watchHeader(
getElementOrThrow("[data-md-component=header]")
getElementOrThrow("[data-md-component=header]"),
{ viewport$ }
)
/* Set up main area observable */
@ -153,6 +154,8 @@ const component$ = document$
mergeWith(control$)
)
component$.subscribe()
/* Export to window */
export {
document$,

View File

@ -21,7 +21,6 @@
*/
/* eslint-disable */
import { filter } from "fuzzaldrin-plus"
import { from, fromEvent } from "rxjs"
import { map, switchMap } from "rxjs/operators"

View File

@ -97,6 +97,7 @@
{{ super() }}
<!-- Extra JavaScript -->
<script src="{{ 'overrides/assets/javascripts/vendor.js' | url }}"></script>
<script src="{{ 'overrides/assets/javascripts/bundle.js' | url }}"></script>
{% endblock %}

View File

@ -203,11 +203,7 @@ export default (_env: never, args: Configuration): Configuration[] => {
entry: {
"assets/javascripts/bundle": "src/assets/javascripts",
"assets/stylesheets/main": "src/assets/stylesheets/main.scss",
"assets/stylesheets/palette": "src/assets/stylesheets/palette.scss",
"overrides/assets/javascripts/bundle":
"src/overrides/assets/javascripts",
"overrides/assets/stylesheets/main":
"src/overrides/assets/stylesheets/main.scss"
"assets/stylesheets/palette": "src/assets/stylesheets/palette.scss"
},
output: {
path: path.resolve(__dirname, "material"),
@ -340,7 +336,10 @@ export default (_env: never, args: Configuration): Configuration[] => {
]) {
const template = toPairs<string>(manifest)
.reduce((content, [from, to]) => (
content.replace(new RegExp(`'${from}'`, "g"), `'${to}'`)
content.replace(new RegExp(
`('|")${from}\\1`, "g"),
`$1${to}$1`
)
), fs.readFileSync(file, "utf8"))
/* Save template with replaced assets */
@ -395,6 +394,44 @@ export default (_env: never, args: Configuration): Configuration[] => {
hashDigestLength: 8,
libraryTarget: "var"
}
},
/* Overrides */
{
...base,
entry: {
"overrides/assets/javascripts/bundle": "src/overrides/assets/javascripts",
"overrides/assets/stylesheets/main": "src/overrides/assets/stylesheets/main.scss"
},
output: {
path: path.resolve(__dirname, "material"),
filename: `[name]${hash}.js`,
hashDigestLength: 8,
libraryTarget: "window"
},
/* Plugins */
plugins: [
...base.plugins || [],
/* Stylesheets */
new MiniCssExtractPlugin({
filename: `[name]${hash}.css`
}),
],
/* Optimizations */
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "overrides/assets/javascripts/vendor",
chunks: "all"
}
}
}
}
}
]
}