Switch themes to minimo

This commit is contained in:
Dave Gallant
2021-09-06 11:55:05 -04:00
parent 75f9797c91
commit 4ff7aec7c1
302 changed files with 17841 additions and 607 deletions

View File

@@ -0,0 +1,33 @@
const commentList = document.querySelector('.comment-list')
const respondBlock = document.querySelector('#respond')
const commentForm = respondBlock.querySelector('form')
const cancelReplyLink = respondBlock.querySelector('#cancel-comment-reply-link')
const parentIdInput = respondBlock.querySelector('[name="fields[parent_id]"]')
const moveRespondBlock = commentId => {
if (!commentId) return
const comment = commentList.querySelector(`#comment-${commentId} article`)
parentIdInput.value = commentId
comment.parentNode.insertBefore(respondBlock, comment.nextSibling)
cancelReplyLink.style.display = ''
commentForm.querySelector('textarea').focus()
return false
}
export const initComments = () => {
cancelReplyLink.style.display = 'none'
cancelReplyLink.addEventListener('click', e => {
e.preventDefault()
parentIdInput.value = ''
commentList.parentNode.appendChild(respondBlock)
cancelReplyLink.style.display = 'none'
})
window.moveRespondBlock = moveRespondBlock
}

View File

@@ -0,0 +1,36 @@
const body = document.body
const detailsTagSupported = () => {
let el = document.createElement('details')
if (!('open' in el)) return false
el.innerHTML = '<summary>a</summary>b'
body.appendChild(el)
let diff = el.offsetHeight
el.open = true
let result = diff != el.offsetHeight
body.removeChild(el)
return result
}
export const detailsPolyfill = detailsElements => {
if (!detailsTagSupported()) {
body.classList.add('no-details')
detailsElements.forEach(detailsElement => {
let summaryElement = detailsElement.querySelector('summary')
summaryElement.addEventListener('click', () => {
if (detailsElement.getAttribute('open')) {
detailsElement.open = false
detailsElement.removeAttribute('open')
} else {
detailsElement.open = true
detailsElement.setAttribute('open', 'open')
}
})
})
}
}

View File

@@ -0,0 +1,17 @@
export const shuffle = array => {
let shuffled = [...array],
currIndex = array.length,
tempValue,
randIndex
while (currIndex) {
randIndex = Math.floor(Math.random() * currIndex)
currIndex--
tempValue = shuffled[currIndex]
shuffled[currIndex] = shuffled[randIndex]
shuffled[randIndex] = tempValue
}
return shuffled
}

View File

@@ -0,0 +1,50 @@
import './webpack-public-path'
import '../stylesheets/style'
import docReady from 'es6-docready'
import { shuffle } from './helpers'
docReady(() => {
const body = document.body
const taxonomyClouds = body.querySelectorAll(
'.taxonomy-cloud:not(.no-shuffle)'
)
if (taxonomyClouds.length) {
taxonomyClouds.forEach(taxonomyCloud => {
let terms = taxonomyCloud.querySelectorAll('li')
shuffle(terms).forEach(term => term.parentElement.appendChild(term))
})
}
const detailsElements = body.querySelectorAll('details')
if (detailsElements.length) {
import(/* webpackChunkName: "details-polyfill" */ './details-polyfill').then(
({ detailsPolyfill }) => detailsPolyfill(detailsElements)
)
}
let hasEmoji = body.classList.contains('has-emoji')
if (hasEmoji) {
let entry = body.querySelector('.entry')
import(/* webpackChunkName: "twemoji" */ 'twemoji').then(twemoji =>
twemoji.parse(entry)
)
}
let hasSidebar = body.classList.contains('has-sidebar')
if (hasSidebar) {
import(/* webpackChunkName: "sidebar" */ './sidebar').then(
({ initSidebar }) => initSidebar()
)
}
let hasComments = body.querySelector('#comment-form')
if (hasComments) {
import(/* webpackChunkName: "comments" */ './comments').then(
({ initComments }) => initComments()
)
}
})

View File

@@ -0,0 +1,47 @@
import algoliasearch from 'algoliasearch/lite'
import {
appendResults,
getUrlSearchParam,
setSearchingIndicator
} from './helpers'
const { appId, indexName, searchApiKey } = window.algolia
const client = algoliasearch(appId, searchApiKey)
const index = client.initIndex(
`${indexName}${window.location.pathname.replace('/search/', '')}`
)
const doSearch = (term, resultsBlock) => {
setSearchingIndicator(resultsBlock)
if (!term) {
appendResults([], resultsBlock)
} else {
index.search(
term,
{ attributesToRetrieve: ['title', 'href'], hitsPerPage: 10 },
(err, content) => {
if (err) console.error(err)
else appendResults(content.hits, resultsBlock)
}
)
}
}
const searchForm = document.getElementById('search-form')
const searchInputBox = document.getElementById('search-term')
const resultsBlock = document.getElementById('search-results')
let term = getUrlSearchParam('q')
searchInputBox.value = term
searchInputBox.focus()
doSearch(term, resultsBlock)
searchForm.addEventListener('submit', e => {
e.preventDefault()
doSearch(searchInputBox.value, resultsBlock)
})

View File

@@ -0,0 +1,54 @@
import Fuse from 'fuse.js'
import {
appendResults,
getJSON,
getUrlSearchParam,
setSearchingIndicator
} from './helpers'
const doSearch = (term, fuse, resultsBlock) => {
setSearchingIndicator(resultsBlock)
let results = term
? fuse
.search(term)
.map(result => ({ href: result.href, title: result.title }))
: []
appendResults(results, resultsBlock)
}
const options = {
shouldSort: true,
threshold: 0.5,
location: 0,
distance: 500,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [{ name: 'title', weight: 0.7 }, { name: 'content', weight: 0.3 }]
}
const searchInputBox = document.getElementById('search-term')
const resultsBlock = document.getElementById('search-results')
let term = getUrlSearchParam('q')
searchInputBox.value = term
searchInputBox.focus()
setSearchingIndicator(resultsBlock)
getJSON(`${window.location.pathname}index.json`, (err, list) => {
if (err) {
console.error(err)
return
}
let fuse = new Fuse(list, options)
doSearch(term, fuse, resultsBlock)
searchInputBox.addEventListener('input', e => {
doSearch(e.target.value, fuse, resultsBlock)
})
})

View File

@@ -0,0 +1,52 @@
export const appendResults = (results, resultsBlock) => {
if (results.length === 0) {
resultsBlock.innerHTML = `<li class='results-empty'>
<a href='#search-term'>${resultsBlock.dataset.resultsEmpty}</a>
</li>`
} else {
resultsBlock.innerHTML = results.reduce((prevItem, { href, title }) => {
return `${prevItem}<li><a href='${href}'>${title}</a></li>`
}, '')
}
}
export const setSearchingIndicator = resultsBlock => {
resultsBlock.innerHTML = `<li class='searching'>
<a href='#search-results'>${resultsBlock.dataset.searching}&hellip;</a>
</li>`
}
export const getUrlSearchParam = name => {
if ('URLSearchParams' in window) {
let urlParams = new URLSearchParams(window.location.search)
return urlParams.get(name)
} else {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
let regex = new RegExp('[\\?&]' + name + '=([^&#]*)')
let results = regex.exec(location.search)
return results === null
? ''
: decodeURIComponent(results[1].replace(/\+/g, ' '))
}
}
export const getJSON = (url, callback) => {
let request = new XMLHttpRequest()
request.open('GET', url, true)
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
let data = JSON.parse(request.responseText)
callback(null, data)
} else {
callback(new Error(request.statusText))
}
}
request.onerror = () => {
callback(new Error(`Failed to get JSON! ${request.statusText}`))
}
request.send()
}

View File

@@ -0,0 +1,34 @@
import lunr from 'lunr'
import {
appendResults,
getUrlSearchParam,
setSearchingIndicator
} from './helpers'
const doSearch = (term, idx, pageTitles, resultsBlock) => {
setSearchingIndicator(resultsBlock)
let results = term
? idx
.search(term)
.map(result => ({ href: result.ref, title: pageTitles[result.ref] }))
: []
appendResults(results, resultsBlock)
}
const idx = lunr.Index.load(window.lunr_idx)
const pageTitles = window.page_titles
const searchInputBox = document.getElementById('search-term')
const resultsBlock = document.getElementById('search-results')
let term = getUrlSearchParam('q')
searchInputBox.value = term
searchInputBox.focus()
doSearch(term, idx, pageTitles, resultsBlock)
searchInputBox.addEventListener('input', e => {
doSearch(e.target.value, idx, pageTitles, resultsBlock)
})

View File

@@ -0,0 +1,127 @@
const body = document.body
const sidebar = body.querySelector('#sidebar')
const expandButton = body.querySelector('#sidebar-toggler')
const overlay = body.querySelector('.sidebar-overlay')
const sidebarMenu = body.querySelector('#sidebar-menu')
const collapseButton = expandButton.cloneNode(true)
collapseButton.setAttribute('id', '#sidebar-collapse')
const setAriaExpanded = (items, value) => {
items.forEach(item => item.setAttribute('aria-expanded', value))
}
const hideSidebar = () => {
sidebar.classList.remove('toggled')
setAriaExpanded([sidebar, expandButton, collapseButton], false)
}
const showSidebar = () => {
sidebar.classList.add('toggled')
setAriaExpanded([sidebar, expandButton, collapseButton], true)
sidebar.focus()
}
let windowWidth,
windowHeight,
bodyHeight,
sidebarHeight,
windowPos,
lastWindowPos = 0,
top = false,
bottom = false,
topOffset = 0,
sidebarOffsetTop,
resizeTimer
const resizeHandler = () => {
windowWidth = window.innerWidth
windowHeight = window.innerHeight
}
const scrollHandler = () => {
windowPos = window.scrollY
bodyHeight = body.offsetHeight
sidebarHeight = sidebar.offsetHeight
sidebarOffsetTop = Math.round(windowPos + sidebar.getBoundingClientRect().top)
if (sidebarHeight > windowHeight) {
if (windowPos > lastWindowPos) {
if (top) {
top = false
topOffset = sidebarOffsetTop > 0 ? sidebarOffsetTop : 0
sidebar.setAttribute('style', `top: ${topOffset}px;`)
} else if (
!bottom &&
windowPos + windowHeight > sidebarHeight + sidebarOffsetTop &&
sidebarHeight < bodyHeight
) {
bottom = true
sidebar.setAttribute('style', 'position: fixed; bottom: 0;')
}
} else if (windowPos < lastWindowPos) {
if (bottom) {
bottom = false
topOffset = sidebarOffsetTop > 0 ? sidebarOffsetTop : 0
sidebar.setAttribute('style', `top: ${topOffset}px;`)
} else if (!top && windowPos < sidebarOffsetTop) {
top = true
sidebar.setAttribute('style', 'position: fixed;')
}
} else {
top = bottom = false
topOffset = sidebarOffsetTop ? sidebarOffsetTop : 0
sidebar.setAttribute('style', `top: ${topOffset}px;`)
}
} else if (!top) {
top = true
sidebar.setAttribute('style', 'position: fixed;')
}
lastWindowPos = windowPos
}
const resizeAndScrollHandler = () => {
resizeHandler()
scrollHandler()
}
const initSidebarMenu = () => {
let itemsWithSubmenu = sidebarMenu.querySelectorAll('.item.has-children')
itemsWithSubmenu.forEach(item => {
let toggler = item.querySelector('button')
let submenu = item.querySelector('.sub-menu')
setAriaExpanded([submenu, toggler], false)
toggler.addEventListener('click', () => {
let toggled = item.classList.contains('toggled')
item.classList[toggled ? 'remove' : 'add']('toggled')
setAriaExpanded([submenu, toggler], !toggled)
})
})
}
export const initSidebar = () => {
sidebar.setAttribute('tabindex', '-1')
sidebar.insertBefore(collapseButton, sidebar.children[1])
setAriaExpanded([sidebar, expandButton, collapseButton], false)
expandButton.addEventListener('click', showSidebar)
collapseButton.addEventListener('click', hideSidebar)
overlay.addEventListener('click', hideSidebar)
window.addEventListener('scroll', scrollHandler)
window.addEventListener('resize', () => {
clearTimeout(resizeTimer)
resizeTimer = setTimeout(resizeAndScrollHandler, 500)
})
resizeAndScrollHandler()
if (sidebarMenu) initSidebarMenu()
}

View File

@@ -0,0 +1 @@
__webpack_public_path__ = window.__assets_js_src