feat: add page load progress bar

progressbar
Divyansh Singh 2 years ago
parent 57116607b8
commit 589387f051

@ -109,9 +109,10 @@ import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp({ app }) {
enhanceApp(ctx) {
DefaultTheme.enhanceApp(ctx)
// register global components
app.component('MyGlobalComponent', /* ... */)
ctx.app.component('MyGlobalComponent', /* ... */)
}
}
```

@ -113,8 +113,9 @@ import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
enhanceApp({ app }) {
app.component('VueClickAwayExample', VueClickAwayExample)
enhanceApp(ctx) {
DefaultTheme.enhanceApp(ctx)
ctx.app.component('VueClickAwayExample', VueClickAwayExample)
}
}
```

@ -70,8 +70,8 @@ const { hasSidebar } = useSidebar()
}
.VPNavBar.has-sidebar .content {
margin-right: -32px;
padding-right: 32px;
margin-right: -100vw;
padding-right: 100vw;
-webkit-backdrop-filter: saturate(50%) blur(8px);
backdrop-filter: saturate(50%) blur(8px);
background: rgba(255, 255, 255, 0.7);

@ -6,8 +6,10 @@ import './styles/components/custom-block.css'
import './styles/components/vp-code.css'
import './styles/components/vp-doc.css'
import './styles/components/vp-sponsor.css'
import './nprogress/index.css'
import { Theme } from 'vitepress'
import { inBrowser, Theme } from 'vitepress'
import nprogress from './nprogress/index.js'
import Layout from './Layout.vue'
import NotFound from './NotFound.vue'
@ -22,7 +24,28 @@ export { default as VPTeamMembers } from './components/VPTeamMembers.vue'
const theme: Theme = {
Layout,
NotFound
NotFound,
enhanceApp: ({ router }) => {
if (inBrowser) {
let timeoutId: NodeJS.Timeout
let called = false
router.onBeforeRouteChange = () => {
timeoutId = setTimeout(() => {
nprogress.start()
called = true
}, 500)
}
router.onAfterRouteChanged = () => {
if (timeoutId) {
clearTimeout(timeoutId)
}
if (called) {
nprogress.done(true)
called = false
}
}
}
}
}
export default theme

@ -0,0 +1,31 @@
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: var(--vp-c-brand);
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px var(--vp-c-brand), 0 0 5px var(--vp-c-brand);
opacity: 1;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}

@ -0,0 +1,45 @@
// Type definitions for NProgress 0.2
// Project: https://github.com/rstacruz/nprogress
// Definitions by: Judah Gabriel Himango <https://github.com/JudahGabriel>, Ovyerus <https://github.com/Ovyerus>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.1
declare namespace nProgress {
interface NProgressOptions {
minimum: number
template: string
easing: string
speed: number
trickle: boolean
trickleSpeed: number
showSpinner: boolean
parent: string
positionUsing: string
barSelector: string
spinnerSelector: string
}
interface NProgress {
version: string
settings: NProgressOptions
status: number | null
configure(options: Partial<NProgressOptions>): NProgress
set(number: number): NProgress
isStarted(): boolean
start(): NProgress
done(force?: boolean): NProgress
inc(amount?: number): NProgress
trickle(): NProgress
/* Internal */
render(fromStart?: boolean): HTMLDivElement
remove(): void
isRendered(): boolean
getPositioningCSS(): 'translate3d' | 'translate' | 'margin'
}
}
declare const nProgress: nProgress.NProgress
export default nProgress

@ -0,0 +1,505 @@
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
* @license MIT */
var NProgress = {}
NProgress.version = '0.2.0'
var Settings = (NProgress.settings = {
minimum: 0.08,
easing: 'linear',
positionUsing: '',
speed: 200,
trickle: true,
trickleSpeed: 200,
showSpinner: true,
barSelector: '[role="bar"]',
spinnerSelector: '[role="spinner"]',
parent: 'body',
template:
'<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'
})
/**
* Updates configuration.
*
* NProgress.configure({
* minimum: 0.1
* });
*/
NProgress.configure = function (options) {
var key, value
for (key in options) {
value = options[key]
if (value !== undefined && options.hasOwnProperty(key))
Settings[key] = value
}
return this
}
/**
* Last number.
*/
NProgress.status = null
/**
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
*
* NProgress.set(0.4);
* NProgress.set(1.0);
*/
NProgress.set = function (n) {
var started = NProgress.isStarted()
n = clamp(n, Settings.minimum, 1)
NProgress.status = n === 1 ? null : n
var progress = NProgress.render(!started),
bar = progress.querySelector(Settings.barSelector),
speed = Settings.speed,
ease = Settings.easing
progress.offsetWidth /* Repaint */
queue(function (next) {
// Set positionUsing if it hasn't already been set
if (Settings.positionUsing === '')
Settings.positionUsing = NProgress.getPositioningCSS()
// Add transition
css(bar, barPositionCSS(n, speed, ease))
if (n === 1) {
// Fade out
css(progress, {
transition: 'none',
opacity: 1
})
progress.offsetWidth /* Repaint */
setTimeout(function () {
css(progress, {
transition: 'all ' + speed + 'ms linear',
opacity: 0
})
setTimeout(function () {
NProgress.remove()
next()
}, speed)
}, speed)
} else {
setTimeout(next, speed)
}
})
return this
}
NProgress.isStarted = function () {
return typeof NProgress.status === 'number'
}
/**
* Shows the progress bar.
* This is the same as setting the status to 0%, except that it doesn't go backwards.
*
* NProgress.start();
*
*/
NProgress.start = function () {
if (!NProgress.status) NProgress.set(0)
var work = function () {
setTimeout(function () {
if (!NProgress.status) return
NProgress.trickle()
work()
}, Settings.trickleSpeed)
}
if (Settings.trickle) work()
return this
}
/**
* Hides the progress bar.
* This is the *sort of* the same as setting the status to 100%, with the
* difference being `done()` makes some placebo effect of some realistic motion.
*
* NProgress.done();
*
* If `true` is passed, it will show the progress bar even if its hidden.
*
* NProgress.done(true);
*/
NProgress.done = function (force) {
if (!force && !NProgress.status) return this
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1)
}
/**
* Increments by a random amount.
*/
NProgress.inc = function (amount) {
var n = NProgress.status
if (!n) {
return NProgress.start()
} else if (n > 1) {
return
} else {
if (typeof amount !== 'number') {
if (n >= 0 && n < 0.2) {
amount = 0.1
} else if (n >= 0.2 && n < 0.5) {
amount = 0.04
} else if (n >= 0.5 && n < 0.8) {
amount = 0.02
} else if (n >= 0.8 && n < 0.99) {
amount = 0.005
} else {
amount = 0
}
}
n = clamp(n + amount, 0, 0.994)
return NProgress.set(n)
}
}
NProgress.trickle = function () {
return NProgress.inc()
}
/**
* Waits for all supplied jQuery promises and
* increases the progress as the promises resolve.
*
* @param $promise jQUery Promise
*/
;(function () {
var initial = 0,
current = 0
NProgress.promise = function ($promise) {
if (!$promise || $promise.state() === 'resolved') {
return this
}
if (current === 0) {
NProgress.start()
}
initial++
current++
$promise.always(function () {
current--
if (current === 0) {
initial = 0
NProgress.done()
} else {
NProgress.set((initial - current) / initial)
}
})
return this
}
})()
/**
* (Internal) renders the progress bar markup based on the `template`
* setting.
*/
NProgress.render = function (fromStart) {
if (NProgress.isRendered()) return document.getElementById('nprogress')
addClass(document.documentElement, 'nprogress-busy')
var progress = document.createElement('div')
progress.id = 'nprogress'
progress.innerHTML = Settings.template
var bar = progress.querySelector(Settings.barSelector),
perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
parent = isDOM(Settings.parent)
? Settings.parent
: document.querySelector(Settings.parent),
spinner
css(bar, {
transition: 'all 0 linear',
transform: 'translate3d(' + perc + '%,0,0)'
})
if (!Settings.showSpinner) {
spinner = progress.querySelector(Settings.spinnerSelector)
spinner && removeElement(spinner)
}
if (parent != document.body) {
addClass(parent, 'nprogress-custom-parent')
}
parent.appendChild(progress)
return progress
}
/**
* Removes the element. Opposite of render().
*/
NProgress.remove = function () {
removeClass(document.documentElement, 'nprogress-busy')
var parent = isDOM(Settings.parent)
? Settings.parent
: document.querySelector(Settings.parent)
removeClass(parent, 'nprogress-custom-parent')
var progress = document.getElementById('nprogress')
progress && removeElement(progress)
}
/**
* Checks if the progress bar is rendered.
*/
NProgress.isRendered = function () {
return !!document.getElementById('nprogress')
}
/**
* Determine which positioning CSS rule to use.
*/
NProgress.getPositioningCSS = function () {
// Sniff on document.body.style
var bodyStyle = document.body.style
// Sniff prefixes
var vendorPrefix =
'WebkitTransform' in bodyStyle
? 'Webkit'
: 'MozTransform' in bodyStyle
? 'Moz'
: 'msTransform' in bodyStyle
? 'ms'
: 'OTransform' in bodyStyle
? 'O'
: ''
if (vendorPrefix + 'Perspective' in bodyStyle) {
// Modern browsers with 3D support, e.g. Webkit, IE10
return 'translate3d'
} else if (vendorPrefix + 'Transform' in bodyStyle) {
// Browsers without 3D support, e.g. IE9
return 'translate'
} else {
// Browsers without translate() support, e.g. IE7-8
return 'margin'
}
}
/**
* Helpers
*/
function isDOM(obj) {
if (typeof HTMLElement === 'object') {
return obj instanceof HTMLElement
}
return (
obj &&
typeof obj === 'object' &&
obj.nodeType === 1 &&
typeof obj.nodeName === 'string'
)
}
function clamp(n, min, max) {
if (n < min) return min
if (n > max) return max
return n
}
/**
* (Internal) converts a percentage (`0..1`) to a bar translateX
* percentage (`-100%..0%`).
*/
function toBarPerc(n) {
return (-1 + n) * 100
}
/**
* (Internal) returns the correct CSS for changing the bar's
* position given an n percentage, and speed and ease from Settings
*/
function barPositionCSS(n, speed, ease) {
var barCSS
if (Settings.positionUsing === 'translate3d') {
barCSS = { transform: 'translate3d(' + toBarPerc(n) + '%,0,0)' }
} else if (Settings.positionUsing === 'translate') {
barCSS = { transform: 'translate(' + toBarPerc(n) + '%,0)' }
} else {
barCSS = { 'margin-left': toBarPerc(n) + '%' }
}
barCSS.transition = 'all ' + speed + 'ms ' + ease
return barCSS
}
/**
* (Internal) Queues a function to be executed.
*/
var queue = (function () {
var pending = []
function next() {
var fn = pending.shift()
if (fn) {
fn(next)
}
}
return function (fn) {
pending.push(fn)
if (pending.length == 1) next()
}
})()
/**
* (Internal) Applies css properties to an element, similar to the jQuery
* css method.
*
* While this helper does assist with vendor prefixed property names, it
* does not perform any manipulation of values prior to setting styles.
*/
var css = (function () {
var cssPrefixes = ['Webkit', 'O', 'Moz', 'ms'],
cssProps = {}
function camelCase(string) {
return string
.replace(/^-ms-/, 'ms-')
.replace(/-([\da-z])/gi, function (match, letter) {
return letter.toUpperCase()
})
}
function getVendorProp(name) {
var style = document.body.style
if (name in style) return name
var i = cssPrefixes.length,
capName = name.charAt(0).toUpperCase() + name.slice(1),
vendorName
while (i--) {
vendorName = cssPrefixes[i] + capName
if (vendorName in style) return vendorName
}
return name
}
function getStyleProp(name) {
name = camelCase(name)
return cssProps[name] || (cssProps[name] = getVendorProp(name))
}
function applyCss(element, prop, value) {
prop = getStyleProp(prop)
element.style[prop] = value
}
return function (element, properties) {
var args = arguments,
prop,
value
if (args.length == 2) {
for (prop in properties) {
value = properties[prop]
if (value !== undefined && properties.hasOwnProperty(prop))
applyCss(element, prop, value)
}
} else {
applyCss(element, args[1], args[2])
}
}
})()
/**
* (Internal) Determines if an element or space separated list of class names contains a class name.
*/
function hasClass(element, name) {
var list = typeof element == 'string' ? element : classList(element)
return list.indexOf(' ' + name + ' ') >= 0
}
/**
* (Internal) Adds a class to an element.
*/
function addClass(element, name) {
var oldList = classList(element),
newList = oldList + name
if (hasClass(oldList, name)) return
// Trim the opening space.
element.className = newList.substring(1)
}
/**
* (Internal) Removes a class from an element.
*/
function removeClass(element, name) {
var oldList = classList(element),
newList
if (!hasClass(element, name)) return
// Replace the class name.
newList = oldList.replace(' ' + name + ' ', ' ')
// Trim the opening and closing spaces.
element.className = newList.substring(1, newList.length - 1)
}
/**
* (Internal) Gets a space separated list of the class names on the element.
* The list is wrapped with a single space on each end to facilitate finding
* matches within the list.
*/
function classList(element) {
return (' ' + ((element && element.className) || '') + ' ').replace(
/\s+/gi,
' '
)
}
/**
* (Internal) Removes an element from the DOM.
*/
function removeElement(element) {
element && element.parentNode && element.parentNode.removeChild(element)
}
export default NProgress

@ -1,12 +0,0 @@
#nprogress .bar {
background: var(--vp-c-brand);
}
#nprogress .spinner-icon {
border-top-color: var(--vp-c-brand);
border-left-color: var(--vp-c-brand);
}
#nprogress .peg {
box-shadow: 0 0 10px var(--vp-c-brand), 0 0 5px var(--vp-c-brand);
}

2
theme.d.ts vendored

@ -1,5 +1,6 @@
// so that users can do `import DefaultTheme from 'vitepress/theme'`
import type { DefineComponent } from 'vue'
import { EnhanceAppContext } from './dist/client/index.js'
// TODO: add props for these
export const VPHomeHero: DefineComponent
@ -14,6 +15,7 @@ export const VPTeamMembers: DefineComponent
declare const theme: {
Layout: DefineComponent
NotFound: DefineComponent
enhanceApp: (ctx: EnhanceAppContext) => void
}
export default theme

Loading…
Cancel
Save