mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 12:09:26 +00:00
oh no where did astro go
This commit is contained in:
57
docs/assets/scripts/code-examples.js
Normal file
57
docs/assets/scripts/code-examples.js
Normal file
@@ -0,0 +1,57 @@
|
||||
document.addEventListener('click', event => {
|
||||
const toggle = event.target?.closest('.code-example-toggle');
|
||||
const pen = event.target?.closest('.code-example-pen');
|
||||
|
||||
// Toggle source
|
||||
if (toggle) {
|
||||
const codeExample = toggle.closest('.code-example');
|
||||
const isOpen = !codeExample.classList.contains('open');
|
||||
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
codeExample.classList.toggle('open', isOpen);
|
||||
}
|
||||
|
||||
// Edit in CodePen
|
||||
if (pen) {
|
||||
const codeExample = pen.closest('.code-example');
|
||||
const code = codeExample.querySelector('code');
|
||||
const version = document.documentElement.dataset.version;
|
||||
const html =
|
||||
`<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/webawesome@${version}/dist/webawesome.loader.js"></script>` +
|
||||
`\n\n` +
|
||||
`${code.textContent}`;
|
||||
const css = 'body {\n font: 16px sans-serif;\n padding: 1rem;\n}\n';
|
||||
const js = '';
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.action = 'https://codepen.io/pen/define';
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
|
||||
const data = {
|
||||
title: '',
|
||||
description: '',
|
||||
tags: ['webawesome'],
|
||||
editors: '1000',
|
||||
head: '<meta name="viewport" content="width=device-width">',
|
||||
html_classes: '',
|
||||
css_external: '',
|
||||
js_external: '',
|
||||
js_module: true,
|
||||
js_pre_processor: 'none',
|
||||
html,
|
||||
css,
|
||||
js
|
||||
};
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'data';
|
||||
input.value = JSON.stringify(data);
|
||||
form.append(input);
|
||||
|
||||
document.documentElement.append(form);
|
||||
form.submit();
|
||||
form.remove();
|
||||
}
|
||||
});
|
||||
15
docs/assets/scripts/copy-code.js
Normal file
15
docs/assets/scripts/copy-code.js
Normal file
@@ -0,0 +1,15 @@
|
||||
function setCopyValue() {
|
||||
document.querySelectorAll('.copy-button').forEach(copyButton => {
|
||||
const pre = copyButton.closest('pre');
|
||||
const code = pre?.querySelector('code');
|
||||
|
||||
if (code) {
|
||||
copyButton.value = code.textContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set data for all copy buttons when the page loads
|
||||
setCopyValue();
|
||||
|
||||
document.addEventListener('turbo:load', setCopyValue);
|
||||
50
docs/assets/scripts/outline.js
Normal file
50
docs/assets/scripts/outline.js
Normal file
@@ -0,0 +1,50 @@
|
||||
const getLinks = () => [...document.querySelectorAll('#outline-standard a')];
|
||||
const linkTargets = new WeakMap();
|
||||
const visibleTargets = new WeakSet();
|
||||
const observer = new IntersectionObserver(handleIntersect, { rootMargin: '0px 0px' });
|
||||
|
||||
function handleIntersect(entries) {
|
||||
entries.forEach(entry => {
|
||||
// Remember which targets are visible
|
||||
if (entry.isIntersecting) {
|
||||
visibleTargets.add(entry.target);
|
||||
} else {
|
||||
visibleTargets.delete(entry.target);
|
||||
}
|
||||
});
|
||||
|
||||
updateLinks();
|
||||
}
|
||||
|
||||
function updateLinks() {
|
||||
const links = getLinks();
|
||||
// Find the first visible target and activate the respective link
|
||||
links.find(link => {
|
||||
const target = linkTargets.get(link);
|
||||
|
||||
if (target && visibleTargets.has(target)) {
|
||||
links.forEach(el => el.classList.toggle('current', el === link));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Observe link targets
|
||||
function observeLinks() {
|
||||
getLinks().forEach(link => {
|
||||
const hash = link.hash.slice(1);
|
||||
const target = hash ? document.querySelector(`#content #${hash}`) : null;
|
||||
|
||||
if (target) {
|
||||
linkTargets.set(link, target);
|
||||
observer.observe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
observeLinks();
|
||||
|
||||
document.addEventListener('turbo:load', updateLinks);
|
||||
document.addEventListener('turbo:load', observeLinks);
|
||||
32
docs/assets/scripts/scroll.js
Normal file
32
docs/assets/scripts/scroll.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Smooth links
|
||||
document.addEventListener('click', event => {
|
||||
const link = event.target.closest('a');
|
||||
const id = (link?.hash ?? '').substr(1);
|
||||
|
||||
if (!link || link.getAttribute('data-smooth-link') === 'off') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
const target = document.getElementById(id);
|
||||
const headerHeight = document.querySelector('wa-page > header').clientHeight;
|
||||
|
||||
if (target) {
|
||||
event.preventDefault();
|
||||
window.scroll({
|
||||
top: target.offsetTop - headerHeight,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
history.pushState(undefined, undefined, `#${id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll classes
|
||||
function updateScrollClass() {
|
||||
document.body.classList.toggle('scrolled-down', window.scrollY >= 10);
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', updateScrollClass);
|
||||
window.addEventListener('turbo:render', updateScrollClass);
|
||||
updateScrollClass();
|
||||
193
docs/assets/scripts/search.js
Normal file
193
docs/assets/scripts/search.js
Normal file
@@ -0,0 +1,193 @@
|
||||
// Search data
|
||||
const res = await Promise.all([import('https://cdn.jsdelivr.net/npm/lunr/+esm'), fetch('/search.json')]);
|
||||
const lunr = res[0].default;
|
||||
const searchData = await res[1].json();
|
||||
const searchIndex = lunr.Index.load(searchData.searchIndex);
|
||||
const map = searchData.map;
|
||||
const searchDebounce = 100;
|
||||
const icons = {
|
||||
component: 'puzzle-piece',
|
||||
document: 'file',
|
||||
home: 'house',
|
||||
theme: 'palette'
|
||||
};
|
||||
let searchTimeout;
|
||||
|
||||
// We're using Turbo, so references to these elements aren't guaranteed to remain intact
|
||||
function getElements() {
|
||||
return {
|
||||
dialog: document.getElementById('site-search'),
|
||||
input: document.getElementById('site-search-input'),
|
||||
results: document.getElementById('site-search-listbox')
|
||||
};
|
||||
}
|
||||
|
||||
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Show the search dialog when clicking on elements with the `data-search` attribute
|
||||
document.addEventListener('click', event => {
|
||||
const searchButton = event.target.closest('[data-search]');
|
||||
if (searchButton) {
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
function show() {
|
||||
const { dialog, input, results } = getElements();
|
||||
|
||||
input.addEventListener('input', handleInput);
|
||||
results.addEventListener('click', handleSelection);
|
||||
dialog.addEventListener('keydown', handleKeyDown);
|
||||
dialog.addEventListener('wa-hide', hide, { once: true });
|
||||
dialog.open = true;
|
||||
}
|
||||
|
||||
function hide() {
|
||||
const { dialog, input, results } = getElements();
|
||||
|
||||
input.removeEventListener('input', handleInput);
|
||||
results.removeEventListener('click', handleSelection);
|
||||
dialog.removeEventListener('keydown', handleKeyDown);
|
||||
dialog.open = false;
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
const { input } = getElements();
|
||||
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
const { input } = getElements();
|
||||
|
||||
input.value = '';
|
||||
input.focus();
|
||||
updateResults();
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
const { input, results } = getElements();
|
||||
|
||||
// Handle keyboard selections
|
||||
if (['ArrowDown', 'ArrowUp', 'Home', 'End', 'Enter'].includes(event.key)) {
|
||||
event.preventDefault();
|
||||
|
||||
const currentEl = results.querySelector('[data-selected="true"]');
|
||||
const items = [...results.querySelectorAll('li')];
|
||||
const index = items.indexOf(currentEl);
|
||||
let nextEl;
|
||||
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
nextEl = items[Math.max(0, index - 1)];
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
nextEl = items[Math.min(items.length - 1, index + 1)];
|
||||
break;
|
||||
case 'Home':
|
||||
nextEl = items[0];
|
||||
break;
|
||||
case 'End':
|
||||
nextEl = items[items.length - 1];
|
||||
break;
|
||||
case 'Enter':
|
||||
currentEl?.querySelector('a')?.click();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the selected item
|
||||
items.forEach(item => {
|
||||
if (item === nextEl) {
|
||||
input.setAttribute('aria-activedescendant', item.id);
|
||||
item.setAttribute('data-selected', 'true');
|
||||
nextEl.scrollIntoView({ block: 'nearest' });
|
||||
} else {
|
||||
item.setAttribute('data-selected', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelection(event) {
|
||||
const link = event.target.closest('a');
|
||||
|
||||
if (link) {
|
||||
event.preventDefault();
|
||||
hide();
|
||||
location.href = link.href;
|
||||
}
|
||||
}
|
||||
|
||||
// Queries the search index and updates the results
|
||||
async function updateResults(query = '') {
|
||||
const { dialog, input, results } = getElements();
|
||||
|
||||
try {
|
||||
const hasQuery = query.length > 0;
|
||||
const searchTokens = query
|
||||
.split(' ')
|
||||
.map((term, index, arr) => `${term}${index === arr.length - 1 ? `* ${term}~1` : '~1'}`)
|
||||
.join(' ');
|
||||
const matches = hasQuery ? searchIndex.search(`${query} ${searchTokens}`) : [];
|
||||
const hasResults = hasQuery && matches.length > 0;
|
||||
|
||||
dialog.classList.toggle('has-results', hasQuery && hasResults);
|
||||
dialog.classList.toggle('no-results', hasQuery && !hasResults);
|
||||
|
||||
input.setAttribute('aria-activedescendant', '');
|
||||
results.innerHTML = '';
|
||||
|
||||
matches.forEach((match, index) => {
|
||||
const page = map[match.ref];
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
const displayTitle = page.title ?? '';
|
||||
const displayDescription = page.description ?? '';
|
||||
const displayUrl = page.url.replace(/^\//, '');
|
||||
let icon = icons.document;
|
||||
|
||||
li.classList.add('site-search-result');
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
|
||||
|
||||
if (page.url === '/') icon = icons.home;
|
||||
if (page.url.startsWith('/docs/components')) icon = icons.component;
|
||||
if (page.url.startsWith('/docs/theme') || page.url.startsWith('/docs/restyle')) icon = icons.theme;
|
||||
|
||||
a.href = page.url;
|
||||
a.innerHTML = `
|
||||
<div class="site-search-result-icon" aria-hidden="true">
|
||||
<wa-icon name="${icon}"></wa-icon>
|
||||
</div>
|
||||
<div class="site-search-result-details">
|
||||
<div class="site-search-result-title"></div>
|
||||
<div class="site-search-result-description"></div>
|
||||
<div class="site-search-result-url"></div>
|
||||
</div>
|
||||
`;
|
||||
a.querySelector('.site-search-result-title').textContent = displayTitle;
|
||||
a.querySelector('.site-search-result-description').textContent = displayDescription;
|
||||
a.querySelector('.site-search-result-url').textContent = displayUrl;
|
||||
|
||||
li.appendChild(a);
|
||||
results.appendChild(li);
|
||||
});
|
||||
} catch {
|
||||
// Ignore query errors as the user types
|
||||
}
|
||||
}
|
||||
35
docs/assets/scripts/turbo.js
Normal file
35
docs/assets/scripts/turbo.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'https://cdn.jsdelivr.net/npm/@hotwired/turbo@7.3.0/+esm';
|
||||
|
||||
if (!window.___turboScrollPositions___) {
|
||||
window.___turboScrollPositions___ = {};
|
||||
}
|
||||
|
||||
const positions = window.___turboScrollPositions___;
|
||||
|
||||
function saveScrollPosition() {
|
||||
document.querySelectorAll('[data-remember-scroll]').forEach(el => {
|
||||
if (el.id) {
|
||||
positions[el.id] = {
|
||||
top: el.scrollTop,
|
||||
left: el.scrollLeft
|
||||
};
|
||||
} else {
|
||||
console.warn(`Can't save scroll position for elements without an id.`, el);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function restoreScrollPosition(event) {
|
||||
const el = event.detail?.newBody || document;
|
||||
|
||||
el.querySelectorAll('[data-remember-scroll]').forEach(el => {
|
||||
if (positions[el.id]) {
|
||||
el.scrollTop = positions[el.id].top;
|
||||
el.scrollLeft = positions[el.id].left;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('turbo:before-cache', saveScrollPosition);
|
||||
window.addEventListener('turbo:before-render', restoreScrollPosition);
|
||||
window.addEventListener('turbo:render', restoreScrollPosition);
|
||||
Reference in New Issue
Block a user