Restructured components and observables

This commit is contained in:
squidfunk 2020-03-03 18:19:33 +01:00
parent 301582cc76
commit f79075223f
84 changed files with 1502 additions and 47091 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 it is too large Load Diff

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 it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,10 @@
{
"assets/javascripts/bundle.js": "assets/javascripts/bundle.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.js.map",
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.js",
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.js.map",
"assets/stylesheets/main.css": "assets/stylesheets/main.css",
"assets/stylesheets/palette.css": "assets/stylesheets/palette.css"
"assets/javascripts/bundle.js": "assets/javascripts/bundle.8ceb0d66.min.js",
"assets/javascripts/bundle.js.map": "assets/javascripts/bundle.8ceb0d66.min.js.map",
"assets/javascripts/vendor.js": "assets/javascripts/vendor.000c9aa0.min.js",
"assets/javascripts/vendor.js.map": "assets/javascripts/vendor.000c9aa0.min.js.map",
"assets/javascripts/worker/search.js": "assets/javascripts/worker/search.926ffd9e.min.js",
"assets/javascripts/worker/search.js.map": "assets/javascripts/worker/search.926ffd9e.min.js.map",
"assets/stylesheets/main.css": "assets/stylesheets/main.c05f2dae.min.css",
"assets/stylesheets/palette.css": "assets/stylesheets/palette.f5f04e6f.min.css"
}

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

@ -43,9 +43,9 @@
{% endif %}
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.c05f2dae.min.css' | url }}">
{% if palette.primary or palette.accent %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.css' | url }}">
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.f5f04e6f.min.css' | url }}">
{% endif %}
{% if palette.primary %}
{% import "partials/palette.html" as map %}
@ -195,7 +195,8 @@
{% endblock %}
</div>
{% block scripts %}
<script src="{{ 'assets/javascripts/bundle.js' | url }}"></script>
<script src="{{ 'assets/javascripts/vendor.000c9aa0.min.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.8ceb0d66.min.js' | url }}"></script>
{%- set translations = {} -%}
{%- for key in [
"clipboard.copy",
@ -218,7 +219,7 @@
app = initialize({
base: "{{ base_url }}",
worker: {
search: "{{ 'assets/javascripts/worker/search.js' | url }}"
search: "{{ 'assets/javascripts/worker/search.926ffd9e.min.js' | url }}"
},
feature: {
instant: {{ "true" if feature and feature.instant else "false" }}

576
package-lock.json generated
View File

@ -624,12 +624,6 @@
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
"dev": true
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@ -669,15 +663,6 @@
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
"dev": true
},
"ansi-align": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
"integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
"dev": true,
"requires": {
"string-width": "^2.0.0"
}
},
"ansi-colors": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
@ -999,29 +984,6 @@
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
"dev": true
},
"boxen": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
"integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
"dev": true,
"requires": {
"ansi-align": "^2.0.0",
"camelcase": "^4.0.0",
"chalk": "^2.0.1",
"cli-boxes": "^1.0.0",
"string-width": "^2.0.0",
"term-size": "^1.2.0",
"widest-line": "^2.0.0"
},
"dependencies": {
"camelcase": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
"dev": true
}
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -1323,12 +1285,6 @@
"integrity": "sha512-yYQ2QfotceRiH4U+h1Us86WJXtVHDmy3nEKIdYPsZCYnOV5/tMgGbmoIlrMzmh2VXlproqYtVaKeGDBkMZifFA==",
"dev": true
},
"capture-stack-trace": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
"integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==",
"dev": true
},
"ccount": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.4.tgz",
@ -1493,12 +1449,6 @@
"tslib": "^1.9.0"
}
},
"ci-info": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
"dev": true
},
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
@ -1547,12 +1497,6 @@
"source-map": "~0.6.0"
}
},
"cli-boxes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
"integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
"dev": true
},
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@ -1688,20 +1632,6 @@
"typedarray": "^0.0.6"
}
},
"configstore": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
"integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
"dev": true,
"requires": {
"dot-prop": "^4.1.0",
"graceful-fs": "^4.1.2",
"make-dir": "^1.0.0",
"unique-string": "^1.0.0",
"write-file-atomic": "^2.0.0",
"xdg-basedir": "^3.0.0"
}
},
"console-browserify": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
@ -1860,15 +1790,6 @@
"elliptic": "^6.0.0"
}
},
"create-error-class": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
"dev": true,
"requires": {
"capture-stack-trace": "^1.0.0"
}
},
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
@ -1928,12 +1849,6 @@
"randomfill": "^1.0.3"
}
},
"crypto-random-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
"dev": true
},
"css-loader": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz",
@ -2056,12 +1971,6 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
"deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true
},
"deep-get-set": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/deep-get-set/-/deep-get-set-1.1.0.tgz",
@ -2247,12 +2156,6 @@
"is-obj": "^1.0.0"
}
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
"duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@ -3565,15 +3468,6 @@
"integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
"dev": true
},
"global-dirs": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
"integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
"dev": true,
"requires": {
"ini": "^1.3.4"
}
},
"global-modules": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
@ -3663,33 +3557,6 @@
"delegate": "^3.1.2"
}
},
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"dev": true,
"requires": {
"create-error-class": "^3.0.0",
"duplexer3": "^0.1.4",
"get-stream": "^3.0.0",
"is-redirect": "^1.0.0",
"is-retry-allowed": "^1.0.0",
"is-stream": "^1.0.0",
"lowercase-keys": "^1.0.0",
"safe-buffer": "^5.0.1",
"timed-out": "^4.0.0",
"unzip-response": "^2.0.1",
"url-parse-lax": "^1.0.0"
},
"dependencies": {
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
}
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@ -3917,12 +3784,6 @@
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
"ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
"dev": true
},
"immutable": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
@ -3974,12 +3835,6 @@
"resolve-from": "^3.0.0"
}
},
"import-lazy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
"integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
"dev": true
},
"import-local": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@ -4094,15 +3949,6 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"dev": true
},
"is-ci": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
"integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
"dev": true,
"requires": {
"ci-info": "^1.5.0"
}
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@ -4176,28 +4022,12 @@
"integrity": "sha512-zxQ9//Q3D/34poZf8fiy3m3XVpbQc7ren15iKqrTtLPwkPD/t3Scy9Imp63FujULGxuK0ZlCwoo5xNpktFgbOA==",
"dev": true
},
"is-installed-globally": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
"integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
"dev": true,
"requires": {
"global-dirs": "^0.1.0",
"is-path-inside": "^1.0.0"
}
},
"is-interactive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
"dev": true
},
"is-npm": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
"integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
"dev": true
},
"is-number": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
@ -4213,15 +4043,6 @@
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-path-inside": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
"integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
"dev": true,
"requires": {
"path-is-inside": "^1.0.1"
}
},
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
@ -4245,24 +4066,12 @@
}
}
},
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
"dev": true
},
"is-regexp": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz",
"integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==",
"dev": true
},
"is-retry-allowed": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
"integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==",
"dev": true
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
@ -4393,15 +4202,6 @@
"node-fetch": "^2.6.0"
}
},
"latest-version": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
"integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
"dev": true,
"requires": {
"package-json": "^4.0.0"
}
},
"lcid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
@ -4517,22 +4317,6 @@
"integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
"dev": true
},
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true
},
"lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dev": true,
"requires": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"lunr": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz",
@ -4543,15 +4327,6 @@
"resolved": "https://registry.npmjs.org/lunr-languages/-/lunr-languages-1.4.0.tgz",
"integrity": "sha512-YWfZDExJN/MJEVE/DbM4AuVRLsqeHi+q3wmECMsWjGIOkd5mr9DUNos7fv8f5do9VLRMYXIzFjn+N4+KPI9pQA=="
},
"make-dir": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
"integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
"dev": true,
"requires": {
"pify": "^3.0.0"
}
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
@ -4994,88 +4769,6 @@
}
}
},
"nodemon": {
"version": "1.19.4",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz",
"integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==",
"dev": true,
"requires": {
"chokidar": "^2.1.8",
"debug": "^3.2.6",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.0.4",
"pstree.remy": "^1.1.7",
"semver": "^5.7.1",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.2",
"update-notifier": "^2.5.0"
},
"dependencies": {
"anymatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
"dev": true,
"requires": {
"micromatch": "^3.1.4",
"normalize-path": "^2.1.1"
},
"dependencies": {
"normalize-path": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
"dev": true,
"requires": {
"remove-trailing-separator": "^1.0.1"
}
}
}
},
"chokidar": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
"dev": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@ -5328,18 +5021,6 @@
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"package-json": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
"integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
"dev": true,
"requires": {
"got": "^6.7.1",
"registry-auth-token": "^3.0.1",
"registry-url": "^3.0.3",
"semver": "^5.1.0"
}
},
"pako": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz",
@ -5440,12 +5121,6 @@
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@ -5978,12 +5653,6 @@
"integrity": "sha512-mKW7Cdn68XMhdes0FjyIbA8+IVPsj3aIuAEQlZVkj9E2VhujWcXZEfwirBoXK6qZYfj1djaTBDCFKjAu1sK93w==",
"dev": true
},
"prepend-http": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -6008,18 +5677,6 @@
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
"dev": true
},
"pstree.remy": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
"integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==",
"dev": true
},
"public-encrypt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
@ -6126,26 +5783,6 @@
"safe-buffer": "^5.1.0"
}
},
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"requires": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
}
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@ -6188,25 +5825,6 @@
"safe-regex": "^1.1.0"
}
},
"registry-auth-token": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
"integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
"dev": true,
"requires": {
"rc": "^1.1.6",
"safe-buffer": "^5.0.1"
}
},
"registry-url": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
"integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
"dev": true,
"requires": {
"rc": "^1.0.1"
}
},
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
@ -6494,15 +6112,6 @@
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
},
"semver-diff": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
"integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
"dev": true,
"requires": {
"semver": "^5.0.3"
}
},
"serialize-javascript": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
@ -6904,33 +6513,6 @@
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@ -6967,12 +6549,6 @@
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"style-search": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@ -7485,49 +7061,6 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"term-size": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
"integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
"dev": true,
"requires": {
"execa": "^0.7.0"
},
"dependencies": {
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dev": true,
"requires": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"execa": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
"integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
"dev": true,
"requires": {
"cross-spawn": "^5.0.1",
"get-stream": "^3.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
}
}
},
"terser": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz",
@ -7574,12 +7107,6 @@
"xtend": "~4.0.1"
}
},
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
"dev": true
},
"timers-browserify": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
@ -7648,26 +7175,6 @@
}
}
},
"touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
"requires": {
"nopt": "~1.0.10"
},
"dependencies": {
"nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
"dev": true,
"requires": {
"abbrev": "1"
}
}
}
},
"trim": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz",
@ -7905,15 +7412,6 @@
}
}
},
"undefsafe": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz",
"integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=",
"dev": true,
"requires": {
"debug": "^2.2.0"
}
},
"unherit": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.2.tgz",
@ -7976,15 +7474,6 @@
"imurmurhash": "^0.1.4"
}
},
"unique-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
"integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
"dev": true,
"requires": {
"crypto-random-string": "^1.0.0"
}
},
"unist-util-find-all-after": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz",
@ -8082,36 +7571,12 @@
}
}
},
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
"dev": true
},
"upath": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
"dev": true
},
"update-notifier": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
"integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
"dev": true,
"requires": {
"boxen": "^1.2.1",
"chalk": "^2.0.1",
"configstore": "^3.0.0",
"import-lazy": "^2.1.0",
"is-ci": "^1.0.10",
"is-installed-globally": "^0.1.0",
"is-npm": "^1.0.0",
"latest-version": "^3.0.0",
"semver-diff": "^2.0.0",
"xdg-basedir": "^3.0.0"
}
},
"upper-case": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
@ -8151,15 +7616,6 @@
}
}
},
"url-parse-lax": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
"integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
"dev": true,
"requires": {
"prepend-http": "^1.0.1"
}
},
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@ -8578,15 +8034,6 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
},
"widest-line": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
"integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
"dev": true,
"requires": {
"string-width": "^2.1.1"
}
},
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@ -8611,17 +8058,6 @@
"mkdirp": "^0.5.1"
}
},
"write-file-atomic": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
"integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.2"
}
},
"x-is-string": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz",
@ -8634,12 +8070,6 @@
"integrity": "sha1-x/pyyqD0QNt4/VZzQyA4rJhEULk=",
"dev": true
},
"xdg-basedir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
"integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
"dev": true
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@ -8652,12 +8082,6 @@
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
"dev": true
},
"yn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",

View File

@ -24,13 +24,12 @@
"url": "https://github.com/squidfunk/mkdocs-material.git"
},
"scripts": {
"build": "npm run dirty && npx webpack --mode production",
"clean": "npx rimraf 'material'",
"dirty": "npx rimraf 'material/!(.icons)'",
"build": "npm run clean && npx webpack --mode production",
"clean": "npx rimraf material",
"lint": "npm run lint:ts && npm run lint:scss",
"lint:scss": "npx stylelint `find src/assets -name *.scss`",
"lint:ts": "npx tslint -p tsconfig.json 'src/**/*.ts'",
"start": "npm run dirty && npx webpack --mode development --watch"
"start": "npx webpack --mode development --watch"
},
"dependencies": {
"clipboard": "^2.0.0",
@ -69,7 +68,6 @@
"material-design-icons-svg": "^3.0.0",
"material-shadows": "^3.0.1",
"modularscale-sass": "^3.0.10",
"nodemon": "^1.19.4",
"postcss-loader": "^3.0.0",
"postcss-replace": "^1.1.0",
"preact": "^10.1.1",

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
export * from "./result"

View File

@ -20,10 +20,48 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { switchMap } from "rxjs/operators"
import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import {
distinctUntilChanged,
filter,
map,
shareReplay,
startWith,
switchMap,
withLatestFrom
} from "rxjs/operators"
import { Header, Viewport, watchHeader } from "observables"
import {
Viewport,
getElement,
watchViewportAt
} from "observables"
import { useComponent } from "../../_"
import { paintHeaderType } from "../paint"
import { watchHeader } from "../watch"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Header type
*/
export type HeaderType =
| "site" /* Header shows site title */
| "page" /* Header shows page title */
/* ------------------------------------------------------------------------- */
/**
* Header
*/
export interface Header {
type: HeaderType /* Header type */
sticky: boolean /* Header stickyness */
height: number /* Header visible height */
}
/* ----------------------------------------------------------------------------
* Helper types
@ -51,6 +89,33 @@ export function mountHeader(
{ viewport$ }: MountOptions
): OperatorFunction<HTMLElement, Header> {
return pipe(
switchMap(el => watchHeader(el, { viewport$ }))
switchMap(el => {
const header$ = watchHeader(el, { viewport$ })
/* Compute whether the header should switch to page header */
const type$ = useComponent("main")
.pipe(
map(main => getElement("h1, h2, h3, h4, h5, h6", main)!),
filter(hx => typeof hx !== "undefined"),
withLatestFrom(useComponent("header-title")),
switchMap(([hx, title]) => watchViewportAt(hx, { header$, viewport$ })
.pipe(
map(({ offset: { y } }) => {
return y >= hx.offsetHeight ? "page" : "site"
}),
distinctUntilChanged(),
paintHeaderType(title)
)
),
startWith<HeaderType>("site")
)
/* Combine into single observable */
return combineLatest([header$, type$])
.pipe(
map(([header, type]): Header => ({ type, ...header })),
shareReplay(1)
)
})
)
}

View File

@ -25,23 +25,23 @@
* ------------------------------------------------------------------------- */
/**
* Set header title
* Set header title active
*
* @param el - Header title element
* @param value - Whether the title is shown
*/
export function setHeaderTitle(
export function setHeaderTitleActive(
el: HTMLElement, value: boolean
): void {
el.setAttribute("data-md-state", value ? "active" : "")
}
/**
* Reset header title
* Reset header title active
*
* @param el - Header element
* @param el - Header title element
*/
export function resetHeaderTitle(
export function resetHeaderTitleActive(
el: HTMLElement
): void {
el.removeAttribute("data-md-state")

View File

@ -21,4 +21,6 @@
*/
export * from "./_"
export * from "./title"
export * from "./apply"
export * from "./paint"
export * from "./watch"

View File

@ -20,47 +20,44 @@
* IN THE SOFTWARE.
*/
import { OperatorFunction, animationFrameScheduler, pipe } from "rxjs"
import {
distinctUntilChanged,
finalize,
map,
observeOn,
tap
} from "rxjs/operators"
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { resetHidden, setHidden } from "actions"
import { Viewport } from "../../agent"
import { HeaderType } from "../_"
import {
resetHeaderTitleActive,
setHeaderTitleActive
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint hideable
* Paint header title type
*
* @param el - Hideable element
* @param offset - Additional offset
* @param el - Header title element
*
* @return Operator function
*/
export function paintHideable(
el: HTMLElement, offset: number = 0
): OperatorFunction<Viewport, boolean> {
export function paintHeaderType(
el: HTMLElement
): MonoTypeOperatorFunction<HeaderType> {
return pipe(
map(({ offset: { y } }) => y >= offset),
distinctUntilChanged(),
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(value => {
setHidden(el, value)
tap(type => {
setHeaderTitleActive(el, type === "page")
}),
/* Reset on complete or error */
finalize(() => {
resetHidden(el)
resetHeaderTitleActive(el)
})
)
}

View File

@ -23,19 +23,9 @@
import { Observable, of } from "rxjs"
import { distinctUntilKeyChanged, switchMap } from "rxjs/operators"
import { Viewport } from "../../agent"
import { Viewport } from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Header
*/
export interface Header {
sticky: boolean /* Header stickyness */
height: number /* Header visible height */
}
import { Header } from "../_"
/* ----------------------------------------------------------------------------
* Helper types
@ -65,7 +55,7 @@ interface WatchOptions {
*/
export function watchHeader(
el: HTMLElement, { viewport$ }: WatchOptions
): Observable<Header> {
): Observable<Omit<Header, "type">> {
return viewport$
.pipe(
distinctUntilKeyChanged("size"),

View File

@ -21,17 +21,23 @@
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { filter, map, switchMap } from "rxjs/operators"
import { distinctUntilChanged, map, switchMap } from "rxjs/operators"
import {
Header,
Viewport,
getElement,
paintHeaderTitle,
watchViewportAt
} from "observables"
import { Viewport, watchViewportAt } from "observables"
import { useComponent } from "../../_"
import { Header } from "../../header"
import { paintHero } from "../paint"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Hero
*/
export interface Hero {
hidden: boolean /* Whether the hero is hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
@ -50,26 +56,21 @@ interface MountOptions {
* ------------------------------------------------------------------------- */
/**
* Mount header title from source observable
* Mount hero from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountHeaderTitle(
export function mountHero(
{ header$, viewport$ }: MountOptions
): OperatorFunction<HTMLElement, boolean> {
): OperatorFunction<HTMLElement, Hero> {
return pipe(
switchMap(el => useComponent("main")
switchMap(el => watchViewportAt(el, { header$, viewport$ })
.pipe(
map(main => getElement("h1, h2, h3, h4, h5, h6", main)!),
filter(hx => typeof hx !== "undefined"),
switchMap(hx => watchViewportAt(hx, { header$, viewport$ })
.pipe(
map(({ offset: { y } }) => y >= hx.offsetHeight),
paintHeaderTitle(el)
)
)
map(({ offset: { y } }) => ({ hidden: y >= 20 })),
distinctUntilChanged(),
paintHero(el)
)
)
)

View File

@ -25,23 +25,23 @@
* ------------------------------------------------------------------------- */
/**
* Set hidden
* Set hero hidden
*
* @param el - Hideable element
* @param el - Hero element
* @param value - Whether the element is hidden
*/
export function setHidden(
export function setHeroHidden(
el: HTMLElement, value: boolean
): void {
el.setAttribute("data-md-state", value ? "hidden" : "")
}
/**
* Reset hidden
* Reset hero hidden
*
* @param el - Hideable element
* @param el - Hero element
*/
export function resetHidden(
export function resetHeroHidden(
el: HTMLElement
): void {
el.removeAttribute("data-md-state")

View File

@ -20,59 +20,6 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import {
Header,
Viewport,
paintHideable,
watchViewportAt
} from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Hero
*/
export interface Hero {
hidden: boolean /* Whether the hero is hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
viewport$: Observable<Viewport> /* Viewport observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount hero from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountHero(
{ header$, viewport$ }: MountOptions
): OperatorFunction<HTMLElement, Hero> {
return pipe(
switchMap(el => watchViewportAt(el, { header$, viewport$ })
.pipe(
paintHideable(el, 20),
map(hidden => ({ hidden }))
)
)
)
}
export * from "./_"
export * from "./apply"
export * from "./paint"

View File

@ -27,33 +27,37 @@ import {
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { resetHeaderTitle, setHeaderTitle } from "actions"
import { Hero } from "../_"
import {
resetHeroHidden,
setHeroHidden
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint header title
* Paint hero
*
* @param el - Header element
* @param el - Hero element
*
* @return Operator function
*/
export function paintHeaderTitle(
export function paintHero(
el: HTMLElement
): MonoTypeOperatorFunction<boolean> {
): MonoTypeOperatorFunction<Hero> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(active => {
setHeaderTitle(el, active)
tap(({ hidden }) => {
setHeroHidden(el, hidden)
}),
/* Reset on complete or error */
finalize(() => {
resetHeaderTitle(el)
resetHeroHidden(el)
})
)
}

View File

@ -26,5 +26,6 @@ export * from "./hero"
export * from "./main"
export * from "./navigation"
export * from "./search"
export * from "./shared"
export * from "./tabs"
export * from "./toc"

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, Subject, pipe } from "rxjs"
import { distinctUntilKeyChanged, switchMap, tap } from "rxjs/operators"
import { Viewport } from "observables"
import { useComponent } from "../../_"
import { Header } from "../../header"
import { paintHeaderShadow } from "../paint"
import { watchMain } from "../watch"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Main area
*/
export interface Main {
offset: number /* Main area top offset */
height: number /* Main area visible height */
active: boolean /* Scrolled past top offset */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
viewport$: Observable<Viewport> /* Viewport observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount main area from source observable
*
* The header must be connected to the main area observable outside of the
* operator function, as the header will persist in-between document switches
* while the main area is replaced. However, the header observable must be
* passed to this function, so we connect both via a long-living subject.
*
* @param options - Options
*
* @return Operator function
*/
export function mountMain(
{ header$, viewport$ }: MountOptions
): OperatorFunction<HTMLElement, Main> {
const main$ = new Subject<Main>()
/* Connect to main area observable via long-living subject */
useComponent("header")
.pipe(
switchMap(header => main$
.pipe(
distinctUntilKeyChanged("active"),
paintHeaderShadow(header)
)
)
)
.subscribe()
/* Return operator */
return pipe(
switchMap(el => watchMain(el, { header$, viewport$ })),
tap(main => main$.next(main))
)
}

View File

@ -20,67 +20,7 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, Subject, pipe } from "rxjs"
import { distinctUntilKeyChanged, switchMap, tap } from "rxjs/operators"
import {
Header,
Main,
Viewport,
paintHeaderShadow,
watchMain
} from "observables"
import { useComponent } from "../_"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
viewport$: Observable<Viewport> /* Viewport observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount main area from source observable
*
* The header must be connected to the main area observable outside of the
* operator function, as the header will persist in-between document switches
* while the main area is replaced. However, the header observable must be
* passed to this function, so we connect both via a long-living subject.
*
* @param options - Options
*
* @return Operator function
*/
export function mountMain(
{ header$, viewport$ }: MountOptions
): OperatorFunction<HTMLElement, Main> {
const main$ = new Subject<Main>()
/* Connect to main area observable via long-living subject */
useComponent("header")
.pipe(
switchMap(header => main$
.pipe(
distinctUntilKeyChanged("active"),
paintHeaderShadow(header)
)
)
)
.subscribe()
/* Return operator */
return pipe(
switchMap(el => watchMain(el, { header$, viewport$ })),
tap(main => main$.next(main))
)
}
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"

View File

@ -27,9 +27,11 @@ import {
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { resetHeaderShadow, setHeaderShadow } from "actions"
import { Main } from "../../main"
import { Main } from "../_"
import {
resetHeaderShadow,
setHeaderShadow
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions

View File

@ -23,21 +23,10 @@
import { Observable, combineLatest } from "rxjs"
import { distinctUntilChanged, map, pluck } from "rxjs/operators"
import { Viewport } from "../../agent"
import { Viewport } from "observables"
import { Header } from "../../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Main area
*/
export interface Main {
offset: number /* Main area top offset */
height: number /* Main area visible height */
active: boolean /* Scrolled past top offset */
}
import { Main } from "../_"
/* ----------------------------------------------------------------------------
* Helper types

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import { Viewport, getElements } from "observables"
import { Header } from "../../header"
import { Main } from "../../main"
import {
Sidebar,
paintSidebar,
watchSidebar
} from "../../shared"
import {
NavigationLayer,
paintNavigationLayer,
watchNavigationLayer
} from "../layer"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Navigation for [screen -]
*/
interface NavigationBelowScreen {
layer: NavigationLayer /* Active layer */
}
/**
* Navigation for [screen +]
*/
interface NavigationAboveScreen {
sidebar: Sidebar /* Sidebar */
}
/* ------------------------------------------------------------------------- */
/**
* Navigation
*/
export type Navigation =
| NavigationBelowScreen
| NavigationAboveScreen
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
viewport$: Observable<Viewport> /* Viewport observable */
screen$: Observable<boolean> /* Screen media observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount navigation from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountNavigation(
{ header$, main$, viewport$, screen$ }: MountOptions
): OperatorFunction<HTMLElement, Navigation> {
return pipe(
switchMap(el => screen$
.pipe(
switchMap(screen => {
/* [screen +]: Mount navigation in sidebar */
if (screen) {
return watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ }),
map(sidebar => ({ sidebar }))
)
/* [screen -]: Mount navigation in drawer */
} else {
const els = getElements("nav", el)
return watchNavigationLayer(els)
.pipe(
paintNavigationLayer(els),
map(layer => ({ layer }))
)
}
})
)
)
)
}

View File

@ -20,101 +20,5 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import {
Header,
Main,
NavigationLayer,
Sidebar,
Viewport,
getElements,
paintNavigationLayer,
paintSidebar,
watchNavigationLayer,
watchSidebar
} from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Navigation for [screen -]
*/
export interface NavigationBelowScreen {
layer: NavigationLayer /* Active layer */
}
/**
* Navigation for [screen +]
*/
export interface NavigationAboveScreen {
sidebar: Sidebar /* Sidebar */
}
/* ------------------------------------------------------------------------- */
/**
* Navigation
*/
export type Navigation =
| NavigationBelowScreen
| NavigationAboveScreen
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
viewport$: Observable<Viewport> /* Viewport observable */
screen$: Observable<boolean> /* Screen media observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount navigation from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountNavigation(
{ header$, main$, viewport$, screen$ }: MountOptions
): OperatorFunction<HTMLElement, Navigation> {
return pipe(
switchMap(el => screen$
.pipe(
switchMap(screen => {
/* [screen +]: Mount navigation in sidebar */
if (screen) {
return watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ }),
map(sidebar => ({ sidebar }))
)
/* [screen -]: Mount navigation in drawer */
} else {
const els = getElements("nav", el)
return watchNavigationLayer(els)
.pipe(
paintNavigationLayer(els),
map(layer => ({ layer }))
)
}
})
)
)
)
}
export * from "./_"
export * from "./layer"

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Navigation layer
*/
export interface NavigationLayer {
prev?: HTMLElement /* Layer (previous) */
next: HTMLElement /* Layer (next) */
}

View File

@ -58,33 +58,3 @@ export function resetOverflowScrolling(
): void {
el.style.webkitOverflowScrolling = ""
}
/* ------------------------------------------------------------------------- */
/**
* Set scroll lock
*
* @param el - Scrollable element
* @param value - Vertical offset
*/
export function setScrollLock(
el: HTMLElement, value: number
): void {
el.setAttribute("data-md-state", "lock")
el.style.top = `-${value}px`
}
/**
* Reset scroll lock
*
* @param el - Scrollable element
*/
export function resetScrollLock(
el: HTMLElement
): void {
const value = -1 * parseInt(el.style.top, 10)
el.removeAttribute("data-md-state")
el.style.top = ""
if (value)
window.scrollTo(0, value)
}

View File

@ -21,5 +21,6 @@
*/
export * from "./_"
export * from "./hideable"
export * from "./sidebar"
export * from "./apply"
export * from "./paint"
export * from "./watch"

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import {
delay,
finalize,
observeOn,
tap
} from "rxjs/operators"
import { getElementOrThrow } from "observables"
import { NavigationLayer } from "../_"
import {
resetOverflowScrolling,
setOverflowScrolling
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint navigation layer
*
* @param els - Navigation elements
*
* @return Operator function
*/
export function paintNavigationLayer(
els: HTMLElement[]
): MonoTypeOperatorFunction<NavigationLayer> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev }) => {
if (prev)
resetOverflowScrolling(prev)
}),
/* Wait until transition has finished */
delay(250),
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ next }) => {
setOverflowScrolling(next)
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els)
resetOverflowScrolling(
getElementOrThrow(".md-nav__list", el)
)
})
)
}

View File

@ -21,44 +21,15 @@
*/
import { findLast } from "ramda"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
fromEvent,
merge,
pipe
} from "rxjs"
import {
delay,
finalize,
map,
observeOn,
scan,
tap
} from "rxjs/operators"
import {
resetOverflowScrolling,
setOverflowScrolling
} from "actions"
import { Observable, fromEvent, merge } from "rxjs"
import { map, scan } from "rxjs/operators"
import {
getElement,
getElementOrThrow
} from "../../agent"
} from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Navigation layer
*/
export interface NavigationLayer {
prev?: HTMLElement /* Layer (previous) */
next: HTMLElement /* Layer (next) */
}
import { NavigationLayer } from "../_"
/* ----------------------------------------------------------------------------
* Functions
@ -105,43 +76,3 @@ export function watchNavigationLayer(
scan(({ next: prev }, { next }) => ({ prev, next }))
)
}
/* ------------------------------------------------------------------------- */
/**
* Paint navigation layer
*
* @param els - Navigation elements
*
* @return Operator function
*/
export function paintNavigationLayer(
els: HTMLElement[]
): MonoTypeOperatorFunction<NavigationLayer> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev }) => {
if (prev)
resetOverflowScrolling(prev)
}),
/* Wait until transition has finished */
delay(250),
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ next }) => {
setOverflowScrolling(next)
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els)
resetOverflowScrolling(
getElementOrThrow(".md-nav__list", el)
)
})
)
}

View File

@ -24,7 +24,8 @@ import { Observable, OperatorFunction, combineLatest, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import { SearchResult } from "integrations/search"
import { SearchQuery } from "observables"
import { SearchQuery } from "../query"
/* ----------------------------------------------------------------------------
* Types

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { OperatorFunction, pipe } from "rxjs"
import {
distinctUntilKeyChanged,
map,
switchMap,
withLatestFrom
} from "rxjs/operators"
import {
WorkerHandler,
setToggle,
useToggle
} from "observables"
import {
SearchMessage,
SearchMessageType,
SearchQueryMessage
} from "workers"
import { watchSearchQuery } from "../watch"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Search query
*/
export interface SearchQuery {
value: string /* Query value */
focus: boolean /* Query focus */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
transform?(value: string): string /* Transformation function */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search query from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Operator function
*/
export function mountSearchQuery(
{ tx$ }: WorkerHandler<SearchMessage>, options: MountOptions = {}
): OperatorFunction<HTMLInputElement, SearchQuery> {
const toggle$ = useToggle("search")
return pipe(
switchMap(el => {
const query$ = watchSearchQuery(el, options)
/* Subscribe worker to search query */
query$
.pipe(
distinctUntilKeyChanged("value"),
map(({ value }): SearchQueryMessage => ({
type: SearchMessageType.QUERY,
data: value
}))
)
.subscribe(tx$.next.bind(tx$))
/* Toggle search on focus */
query$
.pipe(
distinctUntilKeyChanged("focus"),
withLatestFrom(toggle$)
)
.subscribe(([{ focus }, toggle]) => {
if (focus)
setToggle(toggle, focus)
})
/* Return search query */
return query$
})
)
}

View File

@ -20,82 +20,5 @@
* IN THE SOFTWARE.
*/
import { OperatorFunction, pipe } from "rxjs"
import {
distinctUntilKeyChanged,
map,
switchMap,
withLatestFrom
} from "rxjs/operators"
import {
SearchQuery,
WorkerHandler,
setToggle,
useToggle,
watchSearchQuery
} from "observables"
import {
SearchMessage,
SearchMessageType,
SearchQueryMessage
} from "workers"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
transform?(value: string): string /* Transformation function */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search query from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Operator function
*/
export function mountSearchQuery(
{ tx$ }: WorkerHandler<SearchMessage>, options: MountOptions = {}
): OperatorFunction<HTMLInputElement, SearchQuery> {
const toggle$ = useToggle("search")
return pipe(
switchMap(el => {
const query$ = watchSearchQuery(el, options)
/* Subscribe worker to search query */
query$
.pipe(
distinctUntilKeyChanged("value"),
map<SearchQuery, SearchQueryMessage>(({ value }) => ({
type: SearchMessageType.QUERY,
data: value
}))
)
.subscribe(tx$.next.bind(tx$))
/* Toggle search on focus */
query$
.pipe(
distinctUntilKeyChanged("focus"),
withLatestFrom(toggle$)
)
.subscribe(([{ focus }, toggle]) => {
if (focus)
setToggle(toggle, focus)
})
/* Return search query */
return query$
})
)
}
export * from "./_"
export * from "./watch"

View File

@ -28,19 +28,9 @@ import {
startWith
} from "rxjs/operators"
import { watchElementFocus } from "../../agent"
import { watchElementFocus } from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Search query
*/
export interface SearchQuery {
value: string /* Query value */
focus: boolean /* Query focus */
}
import { SearchQuery } from "../_"
/* ----------------------------------------------------------------------------
* Helper types

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { OperatorFunction, pipe } from "rxjs"
import {
mapTo,
startWith,
switchMap,
switchMapTo,
tap
} from "rxjs/operators"
import { setElementFocus } from "observables"
import { useComponent } from "../../../_"
import { watchSearchReset } from "../watch"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search reset from source observable
*
* @return Operator function
*/
export function mountSearchReset(): OperatorFunction<HTMLElement, void> {
return pipe(
switchMap(el => watchSearchReset(el)
.pipe(
switchMapTo(useComponent("search-query")),
tap(setElementFocus),
mapTo(undefined)
)
),
startWith(undefined)
)
}

View File

@ -20,37 +20,5 @@
* IN THE SOFTWARE.
*/
import { OperatorFunction, pipe } from "rxjs"
import {
mapTo,
startWith,
switchMap,
switchMapTo,
tap
} from "rxjs/operators"
import { setElementFocus, watchSearchReset } from "observables"
import { useComponent } from "../../_"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search reset from source observable
*
* @return Operator function
*/
export function mountSearchReset(): OperatorFunction<HTMLElement, void> {
return pipe(
switchMap(el => watchSearchReset(el)
.pipe(
switchMapTo(useComponent("search-query")),
tap(setElementFocus),
mapTo(undefined)
)
),
startWith(undefined)
)
}
export * from "./_"
export * from "./watch"

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { identity } from "ramda"
import { Observable, OperatorFunction, pipe } from "rxjs"
import {
distinctUntilChanged,
filter,
map,
pluck,
switchMap
} from "rxjs/operators"
import { SearchResult } from "integrations/search"
import {
WorkerHandler,
watchElementOffset
} from "observables"
import {
SearchMessage,
isSearchResultMessage
} from "workers"
import { SearchQuery } from "../../query"
import { paintSearchResult } from "../paint"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
query$: Observable<SearchQuery> /* Search query observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search result from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Operator function
*/
export function mountSearchResult(
{ rx$ }: WorkerHandler<SearchMessage>, { query$ }: MountOptions
): OperatorFunction<HTMLElement, SearchResult[]> {
return pipe(
switchMap(el => {
const container = el.parentElement!
/* Compute whether there are more search results to fetch */
const fetch$ = watchElementOffset(container)
.pipe(
map(({ y }) => {
return y >= container.scrollHeight - container.offsetHeight - 16
}),
distinctUntilChanged(),
filter(identity)
)
/* Paint search results */
return rx$
.pipe(
filter(isSearchResultMessage),
pluck("data"),
paintSearchResult(el, { query$, fetch$ })
)
})
)
}

View File

@ -20,75 +20,6 @@
* IN THE SOFTWARE.
*/
import { identity } from "ramda"
import { Observable, OperatorFunction, pipe } from "rxjs"
import {
distinctUntilChanged,
filter,
map,
pluck,
switchMap
} from "rxjs/operators"
import { SearchResult } from "integrations/search"
import {
SearchQuery,
WorkerHandler,
paintSearchResult,
watchElementOffset
} from "observables"
import {
SearchMessage,
isSearchResultMessage
} from "workers"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
query$: Observable<SearchQuery> /* Search query observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount search result from source observable
*
* @param handler - Worker handler
* @param options - Options
*
* @return Operator function
*/
export function mountSearchResult(
{ rx$ }: WorkerHandler<SearchMessage>, { query$ }: MountOptions
): OperatorFunction<HTMLElement, SearchResult[]> {
return pipe(
switchMap(el => {
const container = el.parentElement!
/* Compute whether there are more search results to fetch */
const fetch$ = watchElementOffset(container)
.pipe(
map(({ y }) => {
return y >= container.scrollHeight - container.offsetHeight - 16
}),
distinctUntilChanged(),
filter(identity)
)
/* Paint search results */
return rx$
.pipe(
filter(isSearchResultMessage),
pluck("data"),
paintSearchResult(el, { query$, fetch$ })
)
})
)
}
export * from "./_"
export * from "./apply"
export * from "./paint"

View File

@ -36,17 +36,17 @@ import {
withLatestFrom
} from "rxjs/operators"
import { SearchResult } from "integrations/search"
import { getElementOrThrow } from "observables"
import { renderSearchResult } from "templates"
import { SearchQuery } from "../../query"
import {
addToSearchResultList,
resetSearchResultList,
resetSearchResultMeta,
setSearchResultMeta
} from "actions"
import { SearchResult } from "integrations/search"
import { renderSearchResult } from "templates"
import { getElementOrThrow } from "../../agent"
import { SearchQuery } from "../query"
} from "../apply"
/* ----------------------------------------------------------------------------
* Helper types

View File

@ -20,4 +20,4 @@
* IN THE SOFTWARE.
*/
export * from "./layer"
export * from "./sidebar"

View File

@ -20,5 +20,14 @@
* IN THE SOFTWARE.
*/
export * from "./shadow"
export * from "./title"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Sidebar
*/
export interface Sidebar {
height: number /* Sidebar height */
lock: boolean /* Sidebar lock */
}

View File

@ -21,5 +21,6 @@
*/
export * from "./_"
export * from "./shadow"
export * from "./title"
export * from "./apply"
export * from "./paint"
export * from "./watch"

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
pipe
} from "rxjs"
import {
finalize,
map,
observeOn,
tap,
withLatestFrom
} from "rxjs/operators"
import { Header } from "../../../header"
import { Sidebar } from "../_"
import {
resetSidebarHeight,
resetSidebarLock,
resetSidebarOffset,
setSidebarHeight,
setSidebarLock,
setSidebarOffset
} from "../apply"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Paint options
*/
interface PaintOptions {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint sidebar
*
* @param el - Sidebar element
* @param options - Options
*
* @return Operator function
*/
export function paintSidebar(
el: HTMLElement, { header$ }: PaintOptions
): MonoTypeOperatorFunction<Sidebar> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
withLatestFrom(header$),
tap(([{ height, lock }, { height: offset }]) => {
setSidebarHeight(el, height)
setSidebarLock(el, lock)
/* Set offset in locked state depending on header height */
if (lock)
setSidebarOffset(el, offset)
else
resetSidebarOffset(el)
}),
/* Re-map to sidebar */
map(([sidebar]) => sidebar),
/* Reset on complete or error */
finalize(() => {
resetSidebarOffset(el)
resetSidebarHeight(el)
resetSidebarLock(el)
})
)
}

View File

@ -20,47 +20,18 @@
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
combineLatest,
pipe
} from "rxjs"
import { Observable, combineLatest } from "rxjs"
import {
distinctUntilChanged,
distinctUntilKeyChanged,
finalize,
map,
observeOn,
tap,
withLatestFrom
} from "rxjs/operators"
import {
resetSidebarHeight,
resetSidebarLock,
resetSidebarOffset,
setSidebarHeight,
setSidebarLock,
setSidebarOffset
} from "actions"
import { Viewport } from "observables"
import { Viewport } from "../../agent"
import { Header } from "../../header"
import { Main } from "../_"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Sidebar
*/
export interface Sidebar {
height: number /* Sidebar height */
lock: boolean /* Sidebar lock */
}
import { Main } from "../../../main"
import { Sidebar } from "../_"
/* ----------------------------------------------------------------------------
* Helper types
@ -74,13 +45,6 @@ interface WatchOptions {
viewport$: Observable<Viewport> /* Viewport observable */
}
/**
* Paint options
*/
interface PaintOptions {
header$: Observable<Header> /* Header observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -141,44 +105,3 @@ export function watchSidebar(
map(([height, lock]) => ({ height, lock }))
)
}
/* ------------------------------------------------------------------------- */
/**
* Paint sidebar
*
* @param el - Sidebar element
* @param options - Options
*
* @return Operator function
*/
export function paintSidebar(
el: HTMLElement, { header$ }: PaintOptions
): MonoTypeOperatorFunction<Sidebar> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
withLatestFrom(header$),
tap(([{ height, lock }, { height: offset }]) => {
setSidebarHeight(el, height)
setSidebarLock(el, lock)
/* Set offset in locked state depending on header height */
if (lock)
setSidebarOffset(el, offset)
else
resetSidebarOffset(el)
}),
/* Re-map to sidebar */
map(([sidebar]) => sidebar),
/* Reset on complete or error */
finalize(() => {
resetSidebarOffset(el)
resetSidebarHeight(el)
resetSidebarLock(el)
})
)
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, of, pipe } from "rxjs"
import { distinctUntilChanged, map, switchMap } from "rxjs/operators"
import { Viewport, watchViewportAt } from "observables"
import { Header } from "../../header"
import { paintTabs } from "../paint"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Tabs
*/
export interface Tabs {
hidden: boolean /* Whether the tabs are hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
viewport$: Observable<Viewport> /* Viewport observable */
screen$: Observable<boolean> /* Media screen observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount tabs from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountTabs(
{ header$, viewport$, screen$ }: MountOptions
): OperatorFunction<HTMLElement, Tabs> {
return pipe(
switchMap(el => screen$
.pipe(
switchMap(screen => {
/* [screen +]: Mount tabs above screen breakpoint */
if (screen) {
return watchViewportAt(el, { header$, viewport$ })
.pipe(
map(({ offset: { y } }) => ({ hidden: y >= 10 })),
distinctUntilChanged(),
paintTabs(el)
)
/* [screen -]: Unmount tabs below screen breakpoint */
} else {
return of({ hidden: true })
}
})
)
)
)
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Set tabs hidden
*
* @param el - Tabs element
* @param value - Whether the element is hidden
*/
export function setTabsHidden(
el: HTMLElement, value: boolean
): void {
el.setAttribute("data-md-state", value ? "hidden" : "")
}
/**
* Reset tabs hidden
*
* @param el - Tabs element
*/
export function resetTabsHidden(
el: HTMLElement
): void {
el.removeAttribute("data-md-state")
}

View File

@ -20,73 +20,6 @@
* IN THE SOFTWARE.
*/
import { Observable, OperatorFunction, of, pipe } from "rxjs"
import { map, switchMap } from "rxjs/operators"
import {
Header,
Viewport,
paintHideable,
watchViewportAt
} from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Tabs
*/
export interface Tabs {
hidden: boolean /* Whether the tabs are hidden */
}
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
viewport$: Observable<Viewport> /* Viewport observable */
screen$: Observable<boolean> /* Media screen observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount tabs from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountTabs(
{ header$, viewport$, screen$ }: MountOptions
): OperatorFunction<HTMLElement, Tabs> {
return pipe(
switchMap(el => screen$
.pipe(
switchMap(screen => {
/* Mount tabs above screen breakpoint */
if (screen) {
return watchViewportAt(el, { header$, viewport$ })
.pipe(
paintHideable(el, 10),
map(hidden => ({ hidden }))
)
/* Mount tabs below screen breakpoint */
} else {
return of({ hidden: true })
}
})
)
)
)
}
export * from "./_"
export * from "./apply"
export * from "./paint"

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { Tabs } from "../_"
import {
resetTabsHidden,
setTabsHidden
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint tabs
*
* @param el - Tabs element
*
* @return Operator function
*/
export function paintTabs(
el: HTMLElement
): MonoTypeOperatorFunction<Tabs> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ hidden }) => {
setTabsHidden(el, hidden)
}),
/* Reset on complete or error */
finalize(() => {
resetTabsHidden(el)
})
)
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
Observable,
OperatorFunction,
combineLatest,
of,
pipe
} from "rxjs"
import { map, switchMap } from "rxjs/operators"
import { Viewport, getElements } from "observables"
import { Header } from "../../header"
import { Main } from "../../main"
import {
Sidebar,
paintSidebar,
watchSidebar
} from "../../shared"
import {
AnchorList,
paintAnchorList,
watchAnchorList
} from "../anchor"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Table of contents for [tablet -]
*/
interface TableOfContentsBelowTablet {} // tslint:disable-line
/**
* Table of contents for [tablet +]
*/
interface TableOfContentsAboveTablet {
sidebar: Sidebar /* Sidebar */
anchors: AnchorList /* Anchor list */
}
/* ------------------------------------------------------------------------- */
/**
* Table of contents
*/
export type TableOfContents =
| TableOfContentsBelowTablet
| TableOfContentsAboveTablet
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
viewport$: Observable<Viewport> /* Viewport observable */
tablet$: Observable<boolean> /* Tablet media observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount table of contents from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountTableOfContents(
{ header$, main$, viewport$, tablet$}: MountOptions
): OperatorFunction<HTMLElement, TableOfContents> {
return pipe(
switchMap(el => tablet$
.pipe(
switchMap(tablet => {
/* [tablet +]: Mount table of contents in sidebar */
if (tablet) {
const els = getElements<HTMLAnchorElement>(".md-nav__link", el)
/* Watch and paint sidebar */
const sidebar$ = watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ })
)
/* Watch and paint anchor list (scroll spy) */
const anchors$ = watchAnchorList(els, { header$, viewport$ })
.pipe(
paintAnchorList(els)
)
/* Combine into a single hot observable */
return combineLatest([sidebar$, anchors$])
.pipe(
map(([sidebar, anchors]) => ({ sidebar, anchors }))
)
/* [tablet -]: Unmount table of contents */
} else {
return of({})
}
})
)
)
)
}

View File

@ -20,7 +20,14 @@
* IN THE SOFTWARE.
*/
export * from "./anchor"
export * from "./header"
export * from "./main"
export * from "./search"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Anchor list
*/
export interface AnchorList {
prev: HTMLAnchorElement[][] /* Anchors (previous) */
next: HTMLAnchorElement[][] /* Anchors (next) */
}

View File

@ -20,6 +20,7 @@
* IN THE SOFTWARE.
*/
export * from "./hideable"
export * from "./sidebar"
export * from "./scrollable"
export * from "./_"
export * from "./apply"
export * from "./paint"
export * from "./watch"

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import {
MonoTypeOperatorFunction,
animationFrameScheduler,
pipe
} from "rxjs"
import { finalize, observeOn, tap } from "rxjs/operators"
import { AnchorList } from "../_"
import {
resetAnchorActive,
resetAnchorBlur,
setAnchorActive,
setAnchorBlur
} from "../apply"
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Paint anchor list
*
* @param els - Anchor elements
*
* @return Operator function
*/
export function paintAnchorList(
els: HTMLAnchorElement[]
): MonoTypeOperatorFunction<AnchorList> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev, next }) => {
/* Look forward */
for (const [el] of next) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
/* Look backward */
for (const [index, [el]] of prev.entries()) {
setAnchorActive(el, index === prev.length - 1)
setAnchorBlur(el, true)
}
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
})
)
}

View File

@ -21,47 +21,21 @@
*/
import { reverse } from "ramda"
import {
MonoTypeOperatorFunction,
Observable,
animationFrameScheduler,
combineLatest,
pipe
} from "rxjs"
import { Observable, combineLatest } from "rxjs"
import {
bufferCount,
distinctUntilChanged,
distinctUntilKeyChanged,
finalize,
map,
observeOn,
scan,
startWith,
switchMap,
tap
switchMap
} from "rxjs/operators"
import {
resetAnchorActive,
resetAnchorBlur,
setAnchorActive,
setAnchorBlur
} from "actions"
import { Viewport, getElement } from "observables"
import { Viewport, getElement } from "../agent"
import { Header } from "../header"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Anchor list
*/
export interface AnchorList {
prev: HTMLAnchorElement[][] /* Anchors (previous) */
next: HTMLAnchorElement[][] /* Anchors (next) */
}
import { Header } from "../../../header"
import { AnchorList } from "../_"
/* ----------------------------------------------------------------------------
* Helper types
@ -215,44 +189,3 @@ export function watchAnchorList(
})
)
}
/* ------------------------------------------------------------------------- */
/**
* Paint anchor list
*
* @param els - Anchor elements
*
* @return Operator function
*/
export function paintAnchorList(
els: HTMLAnchorElement[]
): MonoTypeOperatorFunction<AnchorList> {
return pipe(
/* Defer repaint to next animation frame */
observeOn(animationFrameScheduler),
tap(({ prev, next }) => {
/* Look forward */
for (const [el] of next) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
/* Look backward */
for (const [index, [el]] of prev.entries()) {
setAnchorActive(el, index === prev.length - 1)
setAnchorBlur(el, true)
}
}),
/* Reset on complete or error */
finalize(() => {
for (const el of els) {
resetAnchorActive(el)
resetAnchorBlur(el)
}
})
)
}

View File

@ -20,115 +20,5 @@
* IN THE SOFTWARE.
*/
import {
Observable,
OperatorFunction,
combineLatest,
of,
pipe
} from "rxjs"
import { map, switchMap } from "rxjs/operators"
import {
AnchorList,
Header,
Main,
Sidebar,
Viewport,
getElements,
paintAnchorList,
paintSidebar,
watchAnchorList,
watchSidebar
} from "observables"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */
/**
* Table of contents for [tablet -]
*/
export interface TableOfContentsBelowTablet {} // tslint:disable-line
/**
* Table of contents for [tablet +]
*/
export interface TableOfContentsAboveTablet {
sidebar: Sidebar /* Sidebar */
anchors: AnchorList /* Anchor list */
}
/* ------------------------------------------------------------------------- */
/**
* Table of contents
*/
export type TableOfContents =
| TableOfContentsBelowTablet
| TableOfContentsAboveTablet
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
*/
interface MountOptions {
header$: Observable<Header> /* Header observable */
main$: Observable<Main> /* Main area observable */
viewport$: Observable<Viewport> /* Viewport observable */
tablet$: Observable<boolean> /* Tablet media observable */
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Mount table of contents from source observable
*
* @param options - Options
*
* @return Operator function
*/
export function mountTableOfContents(
{ header$, main$, viewport$, tablet$}: MountOptions
): OperatorFunction<HTMLElement, TableOfContents> {
return pipe(
switchMap(el => tablet$
.pipe(
switchMap(tablet => {
/* [tablet +]: Mount table of contents in sidebar */
if (tablet) {
const els = getElements<HTMLAnchorElement>(".md-nav__link", el)
/* Watch and paint sidebar */
const sidebar$ = watchSidebar(el, { main$, viewport$ })
.pipe(
paintSidebar(el, { header$ })
)
/* Watch and paint anchor list (scroll spy) */
const anchors$ = watchAnchorList(els, { header$, viewport$ })
.pipe(
paintAnchorList(els)
)
/* Combine into a single hot observable */
return combineLatest([sidebar$, anchors$])
.pipe(
map(([sidebar, anchors]) => ({ sidebar, anchors }))
)
/* [tablet -]: Unmount table of contents */
} else {
return of({})
}
})
)
)
)
}
export * from "./_"
export * from "./anchor"

View File

@ -68,12 +68,10 @@ import {
useToggle,
getElement,
setViewportOffset,
ViewportOffset,
getLocation
ViewportOffset
} from "./observables"
import { setupSearchWorker } from "./workers"
import { setScrollLock, resetScrollLock } from "actions"
import {
mountHeader,
mountHero,
@ -84,7 +82,6 @@ import {
mountTabs,
useComponent,
setupComponents,
mountHeaderTitle,
mountSearchQuery,
mountSearchReset,
mountSearchResult
@ -110,6 +107,34 @@ document.documentElement.classList.add("js")
if (navigator.userAgent.match(/(iPad|iPhone|iPod)/g))
document.documentElement.classList.add("ios")
/**
* Set scroll lock
*
* @param el - Scrollable element
* @param value - Vertical offset
*/
export function setScrollLock(
el: HTMLElement, value: number
): void {
el.setAttribute("data-md-state", "lock")
el.style.top = `-${value}px`
}
/**
* Reset scroll lock
*
* @param el - Scrollable element
*/
export function resetScrollLock(
el: HTMLElement
): void {
const value = -1 * parseInt(el.style.top, 10)
el.removeAttribute("data-md-state")
el.style.top = ""
if (value)
window.scrollTo(0, value)
}
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
@ -235,12 +260,6 @@ export function initialize(config: unknown) {
shareReplay(1)
)
const title$ = useComponent("header-title")
.pipe(
mountHeaderTitle({ header$, viewport$ }),
shareReplay(1)
)
/* ----------------------------------------------------------------------- */
const keyboard$ = setupKeyboard()
@ -320,7 +339,8 @@ export function initialize(config: unknown) {
function isInternalLink(el: HTMLAnchorElement | URL) {
return el.host === location.host && (
!el.pathname || /\/[\w-]+(?:\/?|\.html)$/i.test(el.pathname)
// TODO: Improve regex
!el.pathname || el.pathname === "/" || /\/[\w-]+(?:\/?|\.html)$/i.test(el.pathname) // TODO: provide some test cases
)
}
@ -541,7 +561,7 @@ export function initialize(config: unknown) {
toc$,
tabs$,
hero$,
title$ // TODO: header title
// title$ // TODO: header title
}
const { ...rest } = state

View File

@ -26,12 +26,9 @@ import {
map,
observeOn,
switchMap,
tap,
withLatestFrom
tap
} from "rxjs/operators"
import { useComponent } from "components"
/* ----------------------------------------------------------------------------
* Types
* ------------------------------------------------------------------------- */

View File

@ -37,6 +37,7 @@ import {
getElements,
isSusceptibleToKeyboard,
setElementFocus,
setElementSelection,
setToggle,
useToggle,
watchKeyboard,
@ -177,7 +178,7 @@ export function setupKeyboard(): Observable<Keyboard> {
case "f":
case "s":
setElementFocus(query)
query.select()
setElementSelection(query)
key.claim()
break

View File

@ -23,3 +23,4 @@
export * from "./_"
export * from "./focus"
export * from "./offset"
export * from "./select"

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
/**
* Set element text selection
*
* @param el - Element
*/
export function setElementSelection(
el: HTMLElement
): void {
if (el instanceof HTMLInputElement)
el.select()
else
throw new Error("Not implemented")
}

View File

@ -95,7 +95,7 @@ export function watchViewportAt(
const offset$ = viewport$
.pipe(
distinctUntilKeyChanged("size"),
map<Viewport, ViewportOffset>(() => ({
map((): ViewportOffset => ({
x: el.offsetLeft,
y: el.offsetTop
}))

View File

@ -21,9 +21,4 @@
*/
export * from "./agent"
export * from "./anchor"
export * from "./header"
export * from "./main"
export * from "./navigation"
export * from "./search"
export * from "./toggle"

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2016-2020 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
export * from "./query"
export * from "./reset"
export * from "./result"

View File

@ -40,9 +40,9 @@ import {
* ------------------------------------------------------------------------- */
/**
* Mount options
* Patch options
*/
interface MountOptions {
interface PatchOptions {
document$: Observable<Document> /* Document observable */
hash$: Observable<string> /* Location hash observable */
}
@ -55,12 +55,12 @@ interface MountOptions {
* Patch all `details` elements
*
* This function will ensure that all `details` tags are opened prior to
* printing, so the whole content of the page is included.
* printing, so the whole content of the page is included, and on anchor jumps.
*
* @param options - Options
*/
export function patchDetails(
{ document$, hash$ }: MountOptions
{ document$, hash$ }: PatchOptions
): void {
const els$ = document$
.pipe(

View File

@ -20,32 +20,20 @@
* IN THE SOFTWARE.
*/
import { identity } from "ramda"
import { Observable, fromEvent, merge } from "rxjs"
import {
filter,
map,
skip,
switchMapTo,
tap,
withLatestFrom
} from "rxjs/operators"
import { Observable } from "rxjs"
import { map, skip, withLatestFrom } from "rxjs/operators"
import { useComponent } from "components"
import {
getElement,
getElements,
watchMedia
} from "observables"
import { getElements } from "observables"
/* ----------------------------------------------------------------------------
* Helper types
* ------------------------------------------------------------------------- */
/**
* Mount options
* Patch options
*/
interface MountOptions {
interface PatchOptions {
document$: Observable<Document> /* Document observable */
}
@ -62,7 +50,7 @@ interface MountOptions {
* @param options - Options
*/
export function patchScripts(
{ document$ }: MountOptions
{ document$ }: PatchOptions
): void {
const els$ = document$
.pipe(

View File

@ -30,9 +30,9 @@ import { getElements } from "observables"
* ------------------------------------------------------------------------- */
/**
* Mount options
* Patch options
*/
interface MountOptions {
interface PatchOptions {
document$: Observable<Document> /* Document observable */
}
@ -41,7 +41,7 @@ interface MountOptions {
* ------------------------------------------------------------------------- */
/**
* Check whetehr the given device is an Apple device
* Check whether the given device is an Apple device
*
* @return Test result
*/
@ -65,7 +65,7 @@ function isAppleDevice(): boolean {
* @param options - Options
*/
export function patchScrollfix(
{ document$ }: MountOptions
{ document$ }: PatchOptions
): void {
const els$ = document$
.pipe(

View File

@ -21,7 +21,7 @@
*/
import { NEVER, Observable } from "rxjs"
import { catchError, map, switchMap, take } from "rxjs/operators"
import { catchError, map, switchMap } from "rxjs/operators"
import { getElementOrThrow, getElements } from "observables"
import { renderSource } from "templates"
@ -44,9 +44,9 @@ export type SourceFacts = string[]
* ------------------------------------------------------------------------- */
/**
* Setup options
* Patch options
*/
interface SetupOptions {
interface PatchOptions {
document$: Observable<Document> /* Document observable */
}
@ -96,12 +96,11 @@ function fetchSourceFacts(
* @param options - Options
*/
export function patchSource(
{ document$ }: SetupOptions
{ document$ }: PatchOptions
): void {
document$
.pipe(
map(() => getElementOrThrow<HTMLAnchorElement>(".md-source[href]")),
take(1),
switchMap(({ href }) => (
cache(`${hash(href)}`, () => fetchSourceFacts(href))
)),
@ -109,8 +108,10 @@ export function patchSource(
)
.subscribe(facts => {
for (const el of getElements(".md-source__repository")) {
el.setAttribute("data-md-state", "done")
el.appendChild(renderSource(facts))
if (!el.hasAttribute("data-md-state")) {
el.setAttribute("data-md-state", "done")
el.appendChild(renderSource(facts))
}
}
})
}

View File

@ -105,8 +105,8 @@
&[data-md-state="hidden"] {
pointer-events: none;
// Hide tabs upon scrolling - disable transition to minimizes repaints whilte
// scrolling down, while scrolling up seems to be okay
// Hide tabs upon scrolling - disable transition to minimizes repaints
// while scrolling down, while scrolling up seems to be okay
.md-tabs__link {
transform: translateY(50%);
transition:
@ -125,7 +125,7 @@
display: none;
}
// We're on the 2nd+ level
// Active tab
&--active ~ .md-main {
// Adjust 1st level styles
@ -144,15 +144,10 @@
}
}
// Hide 1st level normal items
// Hide 1st level items
> .md-nav__list > .md-nav__item {
display: none;
// Hide nested items
&--nested {
display: none;
}
// Show 1st level active nested items
&--active {
display: block;

View File

@ -370,6 +370,7 @@
<!-- Theme-related JavaScript -->
{% block scripts %}
<script src="{{ 'assets/javascripts/vendor.js' | url }}"></script>
<script src="{{ 'assets/javascripts/bundle.js' | url }}"></script>
<!-- Translations -->

View File

@ -309,7 +309,20 @@ export default (_env: never, args: Configuration): Configuration[] => {
}
}
})
]
],
/* Optimizations */
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /\/node_modules\//,
name: "assets/javascripts/vendor",
chunks: "all"
}
}
}
}
},
/* Search worker */