mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-19 07:29:14 +00:00
Compare commits
29 Commits
remove-slo
...
demo-layou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f278ce1d6d | ||
|
|
0bbf9bb275 | ||
|
|
4deb928682 | ||
|
|
4066e8c591 | ||
|
|
8eb6ee6609 | ||
|
|
620525891d | ||
|
|
384147502f | ||
|
|
730e20bd38 | ||
|
|
9b3669c428 | ||
|
|
b003273175 | ||
|
|
faae5c21fd | ||
|
|
af89149939 | ||
|
|
9b5ceb989c | ||
|
|
7862c374dc | ||
|
|
03508ec523 | ||
|
|
aadf4830c6 | ||
|
|
cd282e50ef | ||
|
|
57ddd25662 | ||
|
|
a37f2d594e | ||
|
|
6fbf921f4b | ||
|
|
c11f3d468b | ||
|
|
306eefa44b | ||
|
|
cd237d3057 | ||
|
|
c7757b8cbe | ||
|
|
4b8a86f0e2 | ||
|
|
41de947779 | ||
|
|
11337197d7 | ||
|
|
f1739309eb | ||
|
|
5007924dbd |
2
.github/CODE_OF_CONDUCT.md
vendored
2
.github/CODE_OF_CONDUCT.md
vendored
@@ -35,7 +35,7 @@ This Code of Conduct applies within all project spaces, and it also applies when
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@fontawesome.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cory@abeautifulsite.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: [claviska]
|
||||
2
.github/SECURITY.md
vendored
2
.github/SECURITY.md
vendored
@@ -2,6 +2,6 @@
|
||||
|
||||
We take security issues in Web Awesome very seriously and appreciate your efforts to disclose your findings responsibly.
|
||||
|
||||
To report a security issue, email [support@fontawesome.com](mailto:support@fontawesome.com) and include "WEB AWESOME SECURITY" in the subject line.
|
||||
To report a security issue, email [cory@fontawesome.com](mailto:cory@abeautifulsite.net) and include "WEB AWESOME SECURITY" in the subject line.
|
||||
|
||||
We'll respond as soon as possible and keep you updated throughout the process.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
*.hbs
|
||||
*.mdx
|
||||
.cache
|
||||
.github
|
||||
cspell.json
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Web Awesome
|
||||
|
||||
A forward-thinking library of web components.
|
||||
|
||||
- Works with all frameworks 🧩
|
||||
- Works with CDNs 🚛
|
||||
- Fully customizable with CSS 🎨
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"cdndir",
|
||||
"chatbubble",
|
||||
"checkmark",
|
||||
"claviska",
|
||||
"Clippy",
|
||||
"codebases",
|
||||
"codepen",
|
||||
@@ -85,6 +86,7 @@
|
||||
"Kool",
|
||||
"labelledby",
|
||||
"Laravel",
|
||||
"LaViska",
|
||||
"linkify",
|
||||
"listbox",
|
||||
"listitem",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.63 3.625C11.63 4.27911 11.2435 4.84296 10.6865 5.10064L14 8L17.2622 7.34755C17.0968 7.10642 17 6.81452 17 6.5C17 5.67157 17.6716 5 18.5 5C19.3284 5 20 5.67157 20 6.5C20 7.31157 19.3555 7.9726 18.5504 7.99917L15.0307 15.8207C14.7077 16.5384 13.9939 17 13.2068 17H6.79317C6.00615 17 5.29229 16.5384 4.96933 15.8207L1.44963 7.99917C0.64452 7.9726 0 7.31157 0 6.5C0 5.67157 0.671573 5 1.5 5C2.32843 5 3 5.67157 3 6.5C3 6.81452 2.9032 7.10642 2.73777 7.34755L6 8L9.31702 5.09761C8.76346 4.83855 8.38 4.27656 8.38 3.625C8.38 2.72754 9.10754 2 10.005 2C10.9025 2 11.63 2.72754 11.63 3.625Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 722 B |
@@ -33,23 +33,22 @@ export default defineConfig({
|
||||
server: {
|
||||
open: true,
|
||||
port: 4000,
|
||||
host: true
|
||||
host: true,
|
||||
fs: {
|
||||
strict: false
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
watch: {
|
||||
ignored: ['./public/pagefind/**/*.*'] // HERE
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
FullReload([
|
||||
path.relative(__dirname, '../dist/custom-elements.json')
|
||||
// path.relative(__dirname, './public/**/*.*')
|
||||
path.relative(__dirname, '../dist/custom-elements.json'),
|
||||
path.relative(__dirname, './public/**/*.*')
|
||||
])
|
||||
]
|
||||
},
|
||||
outDir: '../_site',
|
||||
site: 'https://shoelace.style',
|
||||
compressHTML: false,
|
||||
markdown: {
|
||||
syntaxHighlight: 'prism',
|
||||
remarkPlugins: [
|
||||
|
||||
389
docs/src/components/Search.astro
Normal file
389
docs/src/components/Search.astro
Normal file
@@ -0,0 +1,389 @@
|
||||
---
|
||||
---
|
||||
|
||||
<script>
|
||||
;(() => {
|
||||
// Append the search dialog to the body
|
||||
const siteSearch = document.createElement('div');
|
||||
const scrollbarWidth = Math.abs(window.innerWidth - document.documentElement.clientWidth);
|
||||
|
||||
siteSearch.classList.add('search');
|
||||
siteSearch.innerHTML = `
|
||||
<div class="search__overlay"></div>
|
||||
<dialog id="search-dialog" class="search__dialog">
|
||||
<div class="search__content">
|
||||
<div class="search__header">
|
||||
<div id="search-combobox" class="search__input-wrapper">
|
||||
<wa-icon name="search"></wa-icon>
|
||||
<input
|
||||
id="search-input"
|
||||
class="search__input"
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
enterkeyhint="go"
|
||||
spellcheck="false"
|
||||
maxlength="100"
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="true"
|
||||
aria-controls="search-listbox"
|
||||
aria-haspopup="listbox"
|
||||
aria-activedescendant
|
||||
>
|
||||
<button type="button" class="search__clear-button" aria-label="Clear entry" tabindex="-1" hidden>
|
||||
<wa-icon name="circle-xmark" variant="regular"></wa-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search__body">
|
||||
<ul
|
||||
id="search-listbox"
|
||||
class="search__results"
|
||||
role="listbox"
|
||||
aria-label="Search results"
|
||||
></ul>
|
||||
<div class="search__empty">No matching pages</div>
|
||||
</div>
|
||||
<footer class="search__footer">
|
||||
<small><kbd><wa-icon label="Up" name="arrow-up"></wa-icon></kbd> <kbd><wa-icon label="Down" name="arrow-down"></wa-icon></kbd> Navigate</small>
|
||||
<small><kbd><wa-icon label="Enter" name="arrow-turn-down-left"></wa-icon></kbd> Select</small>
|
||||
<small><kbd>Esc</kbd> Close</small>
|
||||
</footer>
|
||||
</div>
|
||||
</dialog>
|
||||
`;
|
||||
|
||||
const overlay = siteSearch.querySelector('.search__overlay');
|
||||
const dialog = siteSearch.querySelector('.search__dialog');
|
||||
const input = siteSearch.querySelector('.search__input');
|
||||
const clearButton = siteSearch.querySelector('.search__clear-button');
|
||||
const results = siteSearch.querySelector('.search__results');
|
||||
const version = document.documentElement.getAttribute('data-wa-version');
|
||||
const key = `search_${version}`;
|
||||
const searchDebounce = 50;
|
||||
const animationDuration = 150;
|
||||
let isShowing = false;
|
||||
let searchTimeout;
|
||||
let searchIndex;
|
||||
let map;
|
||||
|
||||
const loadSearchIndex = new Promise(resolve => {
|
||||
const cache = localStorage.getItem(key);
|
||||
const wait = 'requestIdleCallback' in window ? requestIdleCallback : requestAnimationFrame;
|
||||
|
||||
// Cleanup older search indices (everything before this version)
|
||||
try {
|
||||
const items = { ...localStorage };
|
||||
|
||||
Object.keys(items).forEach(k => {
|
||||
if (key > k) {
|
||||
localStorage.removeItem(k);
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
// Look for a cached index
|
||||
try {
|
||||
if (cache) {
|
||||
const data = JSON.parse(cache);
|
||||
|
||||
searchIndex = window.lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
|
||||
return resolve();
|
||||
}
|
||||
} catch {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
// Wait until idle to fetch the index
|
||||
wait(() => {
|
||||
fetch('/assets/search.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (!window.lunr) {
|
||||
console.error('The Lunr search client has not yet been loaded.');
|
||||
}
|
||||
|
||||
searchIndex = window.lunr.Index.load(data.searchIndex);
|
||||
map = data.map;
|
||||
|
||||
// Cache the search index for this version
|
||||
if (version) {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(data));
|
||||
} catch (err) {
|
||||
console.warn(`Unable to cache the search index: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function show() {
|
||||
isShowing = true;
|
||||
document.body.append(siteSearch);
|
||||
document.body.classList.add('search-visible');
|
||||
document.body.style.setProperty('--docs-search-scroll-lock-size', `${scrollbarWidth}px`);
|
||||
clearButton.hidden = true;
|
||||
requestAnimationFrame(() => input.focus());
|
||||
updateResults();
|
||||
|
||||
dialog.showModal();
|
||||
|
||||
await Promise.all([
|
||||
dialog.animate(
|
||||
[
|
||||
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' },
|
||||
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 0 }, { opacity: 1 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
dialog.addEventListener('mousedown', handleMouseDown);
|
||||
dialog.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
async function hide() {
|
||||
isShowing = false;
|
||||
|
||||
await Promise.all([
|
||||
dialog.animate(
|
||||
[
|
||||
{ opacity: 1, transform: 'scale(1)', transformOrigin: 'top' },
|
||||
{ opacity: 0, transform: 'scale(.9)', transformOrigin: 'top' }
|
||||
],
|
||||
{ duration: animationDuration }
|
||||
).finished,
|
||||
overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: animationDuration }).finished
|
||||
]);
|
||||
|
||||
dialog.close();
|
||||
|
||||
input.blur(); // otherwise Safari will scroll to the bottom of the page on close
|
||||
input.value = '';
|
||||
document.body.classList.remove('search-visible');
|
||||
document.body.style.removeProperty('--docs-search-scroll-lock-size');
|
||||
siteSearch.remove();
|
||||
updateResults();
|
||||
|
||||
dialog.removeEventListener('mousedown', handleMouseDown);
|
||||
dialog.removeEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
function handleInput() {
|
||||
clearButton.hidden = input.value === '';
|
||||
|
||||
// Debounce search queries
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => updateResults(input.value), searchDebounce);
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
clearButton.hidden = true;
|
||||
input.value = '';
|
||||
input.focus();
|
||||
updateResults();
|
||||
}
|
||||
|
||||
function handleMouseDown(event) {
|
||||
if (!event.target.closest('.search__content')) {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyDown(event) {
|
||||
// Close when pressing escape
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault(); // prevent <dialog> from closing immediately so it can animate
|
||||
event.stopImmediatePropagation();
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function updateResults(query = '') {
|
||||
try {
|
||||
await loadSearchIndex;
|
||||
|
||||
const hasQuery = query.length > 0;
|
||||
const searchTerms = query
|
||||
.split(' ')
|
||||
.map((term, index, arr) => {
|
||||
// Search API: https://lunrjs.com/guides/searching.html
|
||||
if (index === arr.length - 1) {
|
||||
// The last term is not mandatory and 1x fuzzy. We also duplicate it with a wildcard to match partial words
|
||||
// as the user types.
|
||||
return `${term}~1 ${term}*`;
|
||||
} else {
|
||||
// All other terms are mandatory and 1x fuzzy
|
||||
return `+${term}~1`;
|
||||
}
|
||||
})
|
||||
.join(' ');
|
||||
const matches = hasQuery ? searchIndex.search(searchTerms) : [];
|
||||
const hasResults = hasQuery && matches.length > 0;
|
||||
|
||||
siteSearch.classList.toggle('search--has-results', hasQuery && hasResults);
|
||||
siteSearch.classList.toggle('search--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(/^\//, '').replace(/\/$/, '');
|
||||
let icon = 'file-text';
|
||||
|
||||
a.setAttribute('role', 'option');
|
||||
a.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
|
||||
if (page.url.includes('getting-started/')) {
|
||||
icon = 'lightbulb';
|
||||
}
|
||||
if (page.url.includes('resources/')) {
|
||||
icon = 'book';
|
||||
}
|
||||
if (page.url.includes('components/')) {
|
||||
icon = 'puzzle-piece';
|
||||
}
|
||||
if (page.url.includes('tokens/')) {
|
||||
icon = 'swatchbook';
|
||||
}
|
||||
if (page.url.includes('utilities/')) {
|
||||
icon = 'wrench';
|
||||
}
|
||||
if (page.url.includes('tutorials/')) {
|
||||
icon = 'gamepad';
|
||||
}
|
||||
|
||||
li.classList.add('search__result');
|
||||
li.setAttribute('role', 'option');
|
||||
li.setAttribute('id', `search-result-item-${match.ref}`);
|
||||
li.setAttribute('data-selected', index === 0 ? 'true' : 'false');
|
||||
|
||||
a.href = page.url;
|
||||
a.innerHTML = `
|
||||
<div class="search__result-icon" aria-hidden="true">
|
||||
<wa-icon name="${icon}"></wa-icon>
|
||||
</div>
|
||||
<div class="search__result__details">
|
||||
<div class="search__result-title"></div>
|
||||
<div class="search__result-description"></div>
|
||||
<div class="search__result-url"></div>
|
||||
</div>
|
||||
`;
|
||||
a.querySelector('.search__result-title').textContent = displayTitle;
|
||||
a.querySelector('.search__result-description').textContent = displayDescription;
|
||||
a.querySelector('.search__result-url').textContent = displayUrl;
|
||||
|
||||
li.appendChild(a);
|
||||
results.appendChild(li);
|
||||
});
|
||||
} catch {
|
||||
// Ignore query errors as the user types
|
||||
}
|
||||
}
|
||||
|
||||
// Show the search dialog when clicking on data-plugin="search"
|
||||
document.addEventListener('click', event => {
|
||||
const searchButton = event.target.closest('[data-plugin="search"]');
|
||||
if (searchButton) {
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Show the search dialog when slash (or CMD+K) is pressed and focus is not inside a form element
|
||||
document.addEventListener('keydown', event => {
|
||||
if (
|
||||
!isShowing &&
|
||||
(event.key === '/' || (event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
|
||||
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
|
||||
) {
|
||||
event.preventDefault();
|
||||
show();
|
||||
}
|
||||
});
|
||||
|
||||
// Purge cache when we press CMD+CTRL+R
|
||||
document.addEventListener('keydown', event => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'r') {
|
||||
localStorage.clear();
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('input', handleInput);
|
||||
clearButton.addEventListener('click', handleClear);
|
||||
|
||||
// Close when a result is selected
|
||||
results.addEventListener('click', event => {
|
||||
if (event.target.closest('a')) {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
// We're using Turbo, so when a user searches for something, visits a result, and presses the back button, the search
|
||||
// UI will still be visible but not interactive. This removes the search UI when Turbo renders a page so they don't
|
||||
// get trapped.
|
||||
window.addEventListener('turbo:render', () => {
|
||||
document.body.classList.remove('search-visible');
|
||||
document.querySelectorAll('.search__overlay, .search__dialog').forEach(el => el.remove());
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -328,7 +328,7 @@ wa-select[label="Signet"]::part(form-control-help-text) {
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<wa-dialog id="icon-chooser" label="Browse Icons" with-header>
|
||||
<wa-dialog id="icon-chooser" label="Browse Icons">
|
||||
<div style="display: grid; grid-template-rows: minmax(0, auto) minmax(0, 1fr); height: 100%; gap: 1rem;">
|
||||
<div style="display: flex; gap: 1.25rem;">
|
||||
<wa-input name="icon-search" placeholder="Search Icons" clearable style="flex: 1 1 auto;">
|
||||
|
||||
@@ -8,7 +8,6 @@ import Search from "../Search.astro"
|
||||
import '../../styles/global.css'
|
||||
import '../../styles/syntax-highlight.css'
|
||||
import '../../styles/code-previews.css'
|
||||
import '../../styles/font-awesome.css'
|
||||
import { customElementsManifest } from '../../js/cem';
|
||||
|
||||
const version = customElementsManifest().package.version
|
||||
|
||||
@@ -52,220 +52,143 @@ const pagefindTranslations = {
|
||||
|
||||
<script>
|
||||
class SiteSearch extends HTMLElement {
|
||||
connectedCallback() {
|
||||
// super.connectedCallback();
|
||||
const openBtn = () => this.querySelector<HTMLButtonElement>('button[data-open-modal]')!;
|
||||
const closeBtn = () => this.querySelector<HTMLButtonElement>('button[data-close-modal]')!;
|
||||
const dialog = () => this.querySelector('dialog')!;
|
||||
const dialogFrame = () => this.querySelector('.dialog-frame')!;
|
||||
translations = {}
|
||||
stripTrailingSlash = (path: string) => path.replace(/(.)\/(#.*)?$/, '$1$2');
|
||||
|
||||
/** Close the modal if a user clicks on a link or outside of the modal. */
|
||||
const onClick = (event: MouseEvent) => {
|
||||
const isLink = 'href' in (event.target || {});
|
||||
if (
|
||||
isLink ||
|
||||
(document.body.contains(event.target as Node) &&
|
||||
!dialogFrame().contains(event.target as Node))
|
||||
) {
|
||||
closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
const openModal = (event?: MouseEvent) => {
|
||||
dialog().showModal();
|
||||
document.body.toggleAttribute('data-search-modal-open', true);
|
||||
this.querySelector('input')?.focus();
|
||||
event?.stopPropagation();
|
||||
window.addEventListener('click', onClick);
|
||||
};
|
||||
|
||||
const closeModal = () => dialog().close();
|
||||
|
||||
openBtn().addEventListener('click', openModal);
|
||||
openBtn().disabled = false;
|
||||
closeBtn().addEventListener('click', closeModal);
|
||||
|
||||
dialog().addEventListener('close', () => {
|
||||
document.body.toggleAttribute('data-search-modal-open', false);
|
||||
window.removeEventListener('click', onClick);
|
||||
});
|
||||
|
||||
// Listen for `/` and `cmd + k` keyboard shortcuts.
|
||||
window.addEventListener('keydown', (e) => {
|
||||
const isInput =
|
||||
document.activeElement instanceof HTMLElement &&
|
||||
(['input', 'select', 'textarea'].includes(document.activeElement.tagName.toLowerCase()) ||
|
||||
document.activeElement.isContentEditable);
|
||||
if (e.metaKey === true && e.key === 'k') {
|
||||
dialog().open ? closeModal() : openModal();
|
||||
e.preventDefault();
|
||||
} else if (e.key === '/' && !dialog().open && !isInput) {
|
||||
openModal();
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
let translations = {};
|
||||
connectedCallback () {
|
||||
this.translations = {};
|
||||
try {
|
||||
translations = JSON.parse(this.dataset.translations || '{}');
|
||||
this.translations = JSON.parse(this.dataset.translations || '{}');
|
||||
} catch {}
|
||||
|
||||
const shouldStrip = this.dataset.stripTrailingSlash !== undefined;
|
||||
const stripTrailingSlash = (path: string) => path.replace(/(.)\/(#.*)?$/, '$1$2');
|
||||
const formatURL = shouldStrip ? stripTrailingSlash : (path: string) => path;
|
||||
|
||||
document.querySelector("#starlight__search").innerHTML = ""
|
||||
this.openBtn.addEventListener('click', this.openModal);
|
||||
this.openBtn.disabled = false;
|
||||
this.closeBtn.addEventListener('click', this.closeModal);
|
||||
this.dialog.addEventListener('close', this.handleClose)
|
||||
|
||||
window.addEventListener('turbo:load', () => {
|
||||
document.querySelector("#starlight__search").innerHTML = ""
|
||||
// Listen for `/` and `cmd + k` keyboard shortcuts.
|
||||
window.addEventListener('keydown', this.handleKeyDown);
|
||||
|
||||
const onIdle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
|
||||
onIdle(async () => {
|
||||
if (import.meta.env.DEV) {
|
||||
// Generate a fake search in dev by calling a JSON endpoint that generates the search.
|
||||
await fetch(import.meta.env.BASE_URL.replace(/\/$/, '') + '/pagefind-dev.json')
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', this.loadPageFind);
|
||||
window.addEventListener('turbo:load', this.loadPageFind);
|
||||
|
||||
// @ts-expect-error — Missing types for @pagefind/default-ui package.
|
||||
const { PagefindUI } = await import('@pagefind/default-ui');
|
||||
}
|
||||
|
||||
document.querySelector("#starlight__search").innerHTML = ""
|
||||
new PagefindUI({
|
||||
element: '#starlight__search',
|
||||
baseUrl: import.meta.env.BASE_URL,
|
||||
bundlePath: import.meta.env.BASE_URL.replace(/\/$/, '') + '/pagefind/',
|
||||
showImages: false,
|
||||
translations,
|
||||
showSubResults: true,
|
||||
processResult: (result: { url: string; sub_results: Array<{ url: string }> }) => {
|
||||
result.url = formatURL(result.url);
|
||||
result.sub_results = result.sub_results.map((sub_result) => {
|
||||
sub_result.url = formatURL(sub_result.url);
|
||||
return sub_result;
|
||||
});
|
||||
},
|
||||
});
|
||||
disconnectedCallback () {
|
||||
this.openBtn.removeEventListener('click', this.openModal);
|
||||
this.closeBtn.removeEventListener('click', this.closeModal);
|
||||
this.dialog.removeEventListener('close', this.handleClose)
|
||||
|
||||
// Listen for `/` and `cmd + k` keyboard shortcuts.
|
||||
window.removeEventListener('keydown', this.handleKeyDown);
|
||||
window.removeEventListener('DOMContentLoaded', this.loadPageFind);
|
||||
window.removeEventListener('turbo:load', this.loadPageFind);
|
||||
}
|
||||
|
||||
/** Close the modal if a user clicks on a link or outside of the modal. */
|
||||
onClick = (event: MouseEvent) => {
|
||||
const isLink = 'href' in (event.target || {});
|
||||
if (
|
||||
isLink ||
|
||||
(document.body.contains(event.target as Node) &&
|
||||
!this.dialogFrame.contains(event.target as Node))
|
||||
) {
|
||||
this.closeModal();
|
||||
}
|
||||
};
|
||||
|
||||
closeModal = () => this.dialog.close();
|
||||
|
||||
openModal = (event?: MouseEvent) => {
|
||||
this.dialog.showModal();
|
||||
document.body.toggleAttribute('data-search-modal-open', true);
|
||||
this.querySelector('input')?.focus();
|
||||
event?.stopPropagation();
|
||||
window.addEventListener('click', this.onClick);
|
||||
};
|
||||
|
||||
|
||||
get openBtn () {
|
||||
return this.querySelector<HTMLButtonElement>('button[data-open-modal]')!;
|
||||
}
|
||||
|
||||
get closeBtn () {
|
||||
return this.querySelector<HTMLButtonElement>('button[data-close-modal]')!;
|
||||
}
|
||||
|
||||
get dialog () {
|
||||
return this.querySelector('dialog')!;
|
||||
}
|
||||
|
||||
get dialogFrame () {
|
||||
return this.querySelector('.dialog-frame')!;
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
document.body.toggleAttribute('data-search-modal-open', false);
|
||||
window.removeEventListener('click', this.onClick);
|
||||
};
|
||||
|
||||
handleKeyDown = (e: KeyboardEvent) => {
|
||||
const isInput =
|
||||
document.activeElement instanceof HTMLElement &&
|
||||
(['input', 'select', 'textarea'].includes(document.activeElement.tagName.toLowerCase()) ||
|
||||
document.activeElement.isContentEditable);
|
||||
if (e.metaKey === true && e.key === 'k') {
|
||||
this.dialog.open ? this.closeModal() : this.openModal();
|
||||
e.preventDefault();
|
||||
} else if (e.key === '/' && !this.dialog.open && !isInput) {
|
||||
this.openModal();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
loadPageFind = () => {
|
||||
const onIdle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
|
||||
onIdle(async () => {
|
||||
if (import.meta.env.DEV) {
|
||||
// Generate a fake search in dev by calling a JSON endpoint that generates the search.
|
||||
await fetch(import.meta.env.BASE_URL.replace(/\/$/, '') + '/pagefind-dev.json')
|
||||
}
|
||||
|
||||
const search = document.querySelector("#starlight__search")
|
||||
|
||||
// Clear out search between page loads.
|
||||
if (search) {
|
||||
search.firstElementChild?.remove()
|
||||
}
|
||||
|
||||
// @ts-expect-error — Missing types for @pagefind/default-ui package.
|
||||
const { PagefindUI } = await import('@pagefind/default-ui');
|
||||
new PagefindUI({
|
||||
element: '#starlight__search',
|
||||
baseUrl: import.meta.env.BASE_URL,
|
||||
bundlePath: import.meta.env.BASE_URL.replace(/\/$/, '') + '/pagefind/',
|
||||
showImages: false,
|
||||
translations: this.translations,
|
||||
showSubResults: true,
|
||||
processResult: (result: { url: string; sub_results: Array<{ url: string }> }) => {
|
||||
result.url = this.formatURL(result.url);
|
||||
result.sub_results = result.sub_results.map((sub_result) => {
|
||||
sub_result.url = this.formatURL(sub_result.url);
|
||||
return sub_result;
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
let links: (HTMLAnchorElement | HTMLButtonElement)[] = []
|
||||
|
||||
function findSelectionIndex () {
|
||||
let index = links.findIndex((link) => link.getAttribute("aria-selected") === "true")
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
function selectPrevious () {
|
||||
selectInDirection("backward")
|
||||
}
|
||||
|
||||
function selectInDirection (direction: "forward" | "backward") {
|
||||
const index = findSelectionIndex()
|
||||
const step = direction === "forward" ? 1 : -1
|
||||
const isOutOfRange = !(index + step < links.length && index + step >= 0)
|
||||
|
||||
if (isOutOfRange) {
|
||||
return
|
||||
}
|
||||
|
||||
const prevLink = links[index]
|
||||
if (prevLink) {
|
||||
prevLink.removeAttribute("aria-selected")
|
||||
let prevResult = prevLink.closest(".pagefind-ui__result-nested, .pagefind-ui__button")
|
||||
if (!prevResult) {
|
||||
prevResult = prevLink.closest(".pagefind-ui__result-title")
|
||||
}
|
||||
if (prevResult) {
|
||||
prevResult.classList.remove("is-active")
|
||||
}
|
||||
}
|
||||
|
||||
const nextLink = links[index + step]
|
||||
nextLink.setAttribute("aria-selected", "true")
|
||||
nextLink.scrollIntoView({ block: "center" })
|
||||
|
||||
let nextResult = nextLink.closest(".pagefind-ui__result-nested, .pagefind-ui__button")
|
||||
if (!nextResult) {
|
||||
nextResult = nextLink.closest(".pagefind-ui__result-title")
|
||||
}
|
||||
if (nextResult) {
|
||||
nextResult.classList.add("is-active")
|
||||
}
|
||||
}
|
||||
|
||||
function selectNext () {
|
||||
selectInDirection("forward")
|
||||
}
|
||||
|
||||
function handleKeyDown(e) {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault()
|
||||
|
||||
const currentItem = links[findSelectionIndex()]
|
||||
|
||||
// When we're on the page find UI button, we want to jump to the previous result before showing more results.
|
||||
if (currentItem.tagName === "BUTTON") {
|
||||
selectPrevious()
|
||||
}
|
||||
|
||||
currentItem.click()
|
||||
}
|
||||
if (e.key === "ArrowUp") {
|
||||
e.preventDefault()
|
||||
selectPrevious()
|
||||
return
|
||||
}
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault()
|
||||
selectNext()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const observerOptions = {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
};
|
||||
|
||||
const search = document.querySelector("#starlight__search")?.closest("dialog")
|
||||
|
||||
function debounce(this: any, func: (...args: any[]) => unknown, timeout = 300){
|
||||
let timer: ReturnType<typeof setTimeout>;
|
||||
return (...args: any[]) => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => { func.apply(this, args); }, timeout);
|
||||
};
|
||||
}
|
||||
const setFirstSelection = debounce(() => {
|
||||
if (links.length > 0 && links[0].tagName !== "BUTTON" && findSelectionIndex() === -1) {
|
||||
selectNext()
|
||||
}
|
||||
}, 30)
|
||||
|
||||
if (search) {
|
||||
search.addEventListener("keydown", handleKeyDown)
|
||||
|
||||
const resultObserver = new MutationObserver(() => {
|
||||
links = Array.from(document.querySelectorAll(".pagefind-ui__result-link"))
|
||||
.concat(Array.from(document.querySelectorAll(".pagefind-ui__button")))
|
||||
for (const link of links) {
|
||||
if (!link.hasAttribute("aria-selected")) {
|
||||
link.removeAttribute("aria-selected")
|
||||
}
|
||||
link.setAttribute("tabindex", "-1")
|
||||
}
|
||||
|
||||
setFirstSelection()
|
||||
})
|
||||
|
||||
resultObserver.observe(search, observerOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('site-search', SiteSearch);
|
||||
|
||||
// :is(.pagefind-ui__result-title, .pagefind-ui__result-nested) .pagefind-ui__result-link {}
|
||||
get formatURL () {
|
||||
return this.shouldStrip ? this.stripTrailingSlash : (path: string) => path;
|
||||
}
|
||||
|
||||
get shouldStrip () {
|
||||
return this.dataset.stripTrailingSlash != null;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('site-search', SiteSearch);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -435,12 +358,6 @@ const pagefindTranslations = {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#starlight__search .pagefind-ui__results-area {
|
||||
overflow: auto;
|
||||
max-height: 70vh;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#starlight__search .pagefind-ui__results > * + * {
|
||||
margin-top: var(--sl-search-result-spacing);
|
||||
}
|
||||
@@ -463,17 +380,15 @@ const pagefindTranslations = {
|
||||
|
||||
#starlight__search .pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):hover,
|
||||
#starlight__search
|
||||
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):is(.is-active, :focus-within),
|
||||
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):focus-within,
|
||||
#starlight__search .pagefind-ui__result-nested:hover,
|
||||
#starlight__search .pagefind-ui__result-nested:is(.is-active, :focus-within),
|
||||
#starlight__search .pagefind-ui__button.is-active {
|
||||
#starlight__search .pagefind-ui__result-nested:focus-within {
|
||||
outline: 1px solid var(--sl-color-accent-high);
|
||||
}
|
||||
|
||||
#starlight__search
|
||||
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):is(.is-active, :focus-within),
|
||||
#starlight__search .pagefind-ui__result-nested:is(.is-active, :focus-within),
|
||||
#starlight__search .pagefind-ui__button.is-active {
|
||||
.pagefind-ui__result-title:not(:where(.pagefind-ui__result-nested *)):focus-within,
|
||||
#starlight__search .pagefind-ui__result-nested:focus-within {
|
||||
background-color: var(--sl-color-accent-low);
|
||||
}
|
||||
|
||||
@@ -564,7 +479,4 @@ const pagefindTranslations = {
|
||||
background-color: transparent;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -9,15 +9,6 @@ layout: ../../../layouts/ComponentLayout.astro
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
This is a standard alert. You can customize its content and even the icon.
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert open>
|
||||
<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>
|
||||
<div style="border: solid 2px tomato; height: 2rem;"></div>
|
||||
</wa-alert>
|
||||
|
||||
<wa-alert open>
|
||||
<div style="border: solid 2px tomato; height: 2rem;"></div>
|
||||
</wa-alert>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
|
||||
@@ -5,7 +5,7 @@ layout: ../../../layouts/ComponentLayout.astro
|
||||
---
|
||||
|
||||
```html:preview
|
||||
<wa-card with-image with-footer class="card-overview">
|
||||
<wa-card class="card-overview">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
@@ -62,7 +62,7 @@ const css = `
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard with-image with-footer className="card-overview">
|
||||
<WaCard className="card-overview">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
@@ -129,7 +129,7 @@ const App = () => (
|
||||
Headers can be used to display titles and more.
|
||||
|
||||
```html:preview
|
||||
<wa-card with-header class="card-header">
|
||||
<wa-card class="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
<wa-icon-button name="gear" variant="solid" label="Settings"></wa-icon-button>
|
||||
@@ -185,7 +185,7 @@ const css = `
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard with-header className="card-header">
|
||||
<WaCard className="card-header">
|
||||
<div slot="header">
|
||||
Header Title
|
||||
<WaIconButton name="gear" variant="solid"></WaIconButton>
|
||||
@@ -203,7 +203,7 @@ const App = () => (
|
||||
Footers can be used to display actions, summaries, or other relevant content.
|
||||
|
||||
```html:preview
|
||||
<wa-card with-footer class="card-footer">
|
||||
<wa-card class="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
|
||||
<div slot="footer">
|
||||
@@ -244,7 +244,7 @@ const css = `
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard with-footer className="card-footer">
|
||||
<WaCard className="card-footer">
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
<div slot="footer">
|
||||
<WaRating></WaRating>
|
||||
@@ -264,7 +264,7 @@ const App = () => (
|
||||
Cards accept an `image` slot. The image is displayed atop the card and stretches to fit.
|
||||
|
||||
```html:preview
|
||||
<wa-card with-image class="card-image">
|
||||
<wa-card class="card-image">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
|
||||
@@ -291,7 +291,7 @@ const css = `
|
||||
|
||||
const App = () => (
|
||||
<>
|
||||
<WaCard with-image className="card-image">
|
||||
<WaCard className="card-image">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1547191783-94d5f8f6d8b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&q=80"
|
||||
|
||||
@@ -7,7 +7,7 @@ layout: ../../../layouts/ComponentLayout.astro
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-overview">
|
||||
<wa-dialog label="Dialog" class="dialog-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
@@ -34,7 +34,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -54,7 +54,7 @@ const App = () => {
|
||||
Use the `--width` custom property to set the dialog's width.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-width" style="--width: 50vw;">
|
||||
<wa-dialog label="Dialog" class="dialog-width" style="--width: 50vw;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
@@ -81,7 +81,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} style={{ '--width': '50vw' }} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} style={{ '--width': '50vw' }} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -99,7 +99,7 @@ const App = () => {
|
||||
By design, a dialog's height will never exceed that of the viewport. As such, dialogs will not scroll with the page ensuring the header and footer are always accessible to the user.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-scrolling">
|
||||
<wa-dialog label="Dialog" class="dialog-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
@@ -128,7 +128,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
@@ -155,7 +155,7 @@ const App = () => {
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-header-actions">
|
||||
<wa-dialog label="Dialog" class="dialog-header-actions">
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
@@ -186,7 +186,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaIconButton
|
||||
class="new-window"
|
||||
slot="header-actions"
|
||||
@@ -214,7 +214,7 @@ To keep the dialog open in such cases, you can cancel the `wa-request-close` eve
|
||||
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the dialog from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-deny-close">
|
||||
<wa-dialog label="Dialog" class="dialog-deny-close">
|
||||
This dialog will not close when you click on the overlay.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
@@ -255,7 +255,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
This dialog will not close when you click on the overlay.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -273,7 +273,7 @@ const App = () => {
|
||||
By default, the dialog's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the dialog. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-dialog label="Dialog" with-header with-footer class="dialog-focus">
|
||||
<wa-dialog label="Dialog" class="dialog-focus">
|
||||
<wa-input autofocus placeholder="I will have focus when the dialog is opened"></wa-input>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-dialog>
|
||||
@@ -302,7 +302,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDialog label="Dialog" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDialog label="Dialog" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaInput autofocus placeholder="I will have focus when the dialog is opened" />
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
|
||||
@@ -7,7 +7,7 @@ layout: ../../../layouts/ComponentLayout.astro
|
||||
<!-- cspell:dictionaries lorem-ipsum -->
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-overview">
|
||||
<wa-drawer label="Drawer" class="drawer-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -34,7 +34,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -54,7 +54,7 @@ const App = () => {
|
||||
By default, drawers slide in from the end. To make the drawer slide in from the start, set the `placement` attribute to `start`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="start" with-header with-footer class="drawer-placement-start">
|
||||
<wa-drawer label="Drawer" placement="start" class="drawer-placement-start">
|
||||
This drawer slides in from the start.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -81,7 +81,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="start" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" placement="start" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the start.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -99,7 +99,7 @@ const App = () => {
|
||||
To make the drawer slide in from the top, set the `placement` attribute to `top`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="top" with-header with-footer class="drawer-placement-top">
|
||||
<wa-drawer label="Drawer" placement="top" class="drawer-placement-top">
|
||||
This drawer slides in from the top.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -126,7 +126,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="top" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" placement="top" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the top.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -144,7 +144,7 @@ const App = () => {
|
||||
To make the drawer slide in from the bottom, set the `placement` attribute to `bottom`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" placement="bottom" with-header with-footer class="drawer-placement-bottom">
|
||||
<wa-drawer label="Drawer" placement="bottom" class="drawer-placement-bottom">
|
||||
This drawer slides in from the bottom.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -171,7 +171,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" placement="bottom" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" placement="bottom" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer slides in from the bottom.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -184,12 +184,84 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
### Contained to an Element
|
||||
|
||||
By default, drawers slide out of their [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport. To make a drawer slide out of a parent element, add the `contained` attribute to the drawer and apply `position: relative` to its parent.
|
||||
|
||||
Unlike normal drawers, contained drawers are not modal. This means they do not show an overlay, they do not trap focus, and they are not dismissible with [[Escape]]. This is intentional to allow users to interact with elements outside of the drawer.
|
||||
|
||||
```html:preview
|
||||
<div
|
||||
style="position: relative; border: solid 2px var(--wa-color-surface-border); height: 300px; padding: 1rem; margin-bottom: 1rem;"
|
||||
>
|
||||
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer opens.
|
||||
|
||||
<wa-drawer label="Drawer" contained class="drawer-contained" style="--size: 50%;">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
</div>
|
||||
|
||||
<wa-button>Toggle Drawer</wa-button>
|
||||
|
||||
<script>
|
||||
const drawer = document.querySelector('.drawer-contained');
|
||||
const openButton = drawer.parentElement.nextElementSibling;
|
||||
const closeButton = drawer.querySelector('wa-button[variant="brand"]');
|
||||
|
||||
openButton.addEventListener('click', () => (drawer.open = !drawer.open));
|
||||
closeButton.addEventListener('click', () => drawer.hide());
|
||||
</script>
|
||||
```
|
||||
|
||||
```jsx:react
|
||||
import { useState } from 'react';
|
||||
import WaButton from '@shoelace-style/shoelace/dist/react/button';
|
||||
import WaDrawer from '@shoelace-style/shoelace/dist/react/drawer';
|
||||
|
||||
const App = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
border: 'solid 2px var(--wa-color-surface-border)',
|
||||
height: '300px',
|
||||
padding: '1rem',
|
||||
marginBottom: '1rem'
|
||||
}}
|
||||
>
|
||||
The drawer will be contained to this box. This content won't shift or be affected in any way when the drawer
|
||||
opens.
|
||||
<WaDrawer
|
||||
label="Drawer"
|
||||
contained
|
||||
no-modal
|
||||
open={open}
|
||||
onWaAfterHide={() => setOpen(false)}
|
||||
style={{ '--size': '50%' }}
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</WaButton>
|
||||
</WaDrawer>
|
||||
</div>
|
||||
|
||||
<WaButton onClick={() => setOpen(true)}>Open Drawer</WaButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Custom Size
|
||||
|
||||
Use the `--size` custom property to set the drawer's size. This will be applied to the drawer's width or height depending on its `placement`.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-custom-size" style="--size: 50vw;">
|
||||
<wa-drawer label="Drawer" class="drawer-custom-size" style="--size: 50vw;">
|
||||
This drawer is always 50% of the viewport.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -216,7 +288,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)} style={{ '--size': '50vw' }}>
|
||||
This drawer is always 50% of the viewport.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
@@ -234,7 +306,7 @@ const App = () => {
|
||||
By design, a drawer's height will never exceed 100% of its container. As such, drawers will not scroll with the page to ensure the header and footer are always accessible to the user.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-scrolling">
|
||||
<wa-drawer label="Drawer" class="drawer-scrolling">
|
||||
<div style="height: 150vh; border: dashed 2px var(--wa-color-surface-border); padding: 0 1rem;">
|
||||
<p>Scroll down and give it a try! 👇</p>
|
||||
</div>
|
||||
@@ -263,7 +335,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<div
|
||||
style={{
|
||||
height: '150vh',
|
||||
@@ -289,7 +361,7 @@ const App = () => {
|
||||
The header shows a functional close button by default. You can use the `header-actions` slot to add additional [icon buttons](/components/icon-button) if needed.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-header-actions">
|
||||
<wa-drawer label="Drawer" class="drawer-header-actions">
|
||||
<wa-icon-button class="new-window" slot="header-actions" name="arrow-up-right-from-square" variant="solid"></wa-icon-button>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
@@ -320,7 +392,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaIconButton slot="header-actions" name="arrow-up-right-from-square" onClick={() => window.open(location.href)} />
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
@@ -343,7 +415,7 @@ To keep the drawer open in such cases, you can cancel the `wa-request-close` eve
|
||||
You can use `event.detail.source` to determine what triggered the request to close. This example prevents the drawer from closing when the overlay is clicked, but allows the close button or [[Escape]] to dismiss it.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-deny-close">
|
||||
<wa-drawer label="Drawer" class="drawer-deny-close">
|
||||
This drawer will not close when you click on the overlay.
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -384,7 +456,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" open={open} onWaRequestClose={handleRequestClose} onWaAfterHide={() => setOpen(false)}>
|
||||
This drawer will not close when you click on the overlay.
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Save & Close
|
||||
@@ -402,7 +474,7 @@ const App = () => {
|
||||
By default, the drawer's panel will gain focus when opened. This allows a subsequent tab press to focus on the first tabbable element in the drawer. If you want a different element to have focus, add the `autofocus` attribute to it as shown below.
|
||||
|
||||
```html:preview
|
||||
<wa-drawer label="Drawer" with-header with-footer class="drawer-focus">
|
||||
<wa-drawer label="Drawer" class="drawer-focus">
|
||||
<wa-input autofocus placeholder="I will have focus when the drawer is opened"></wa-input>
|
||||
<wa-button slot="footer" variant="brand">Close</wa-button>
|
||||
</wa-drawer>
|
||||
@@ -431,7 +503,7 @@ const App = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<WaDrawer label="Drawer" with-header with-footer open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaDrawer label="Drawer" open={open} onWaAfterHide={() => setOpen(false)}>
|
||||
<WaInput autofocus placeholder="I will have focus when the drawer is opened" />
|
||||
<WaButton slot="footer" variant="brand" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
|
||||
@@ -115,4 +115,4 @@ Alternatively, you can apply `nav-state="open"` and `nav-state="closed"` to the
|
||||
|
||||
### Docs Layout
|
||||
|
||||
- TODO - Menu + main + aside + footer (docs)
|
||||
- TODO - Menu + main + aside + footer (docs)
|
||||
@@ -6,7 +6,7 @@ description: TODO
|
||||
## Card
|
||||
|
||||
```html:preview
|
||||
<wa-card with-image class="card-overview">
|
||||
<wa-card class="card-overview">
|
||||
<img
|
||||
slot="image"
|
||||
src="https://images.unsplash.com/photo-1559209172-0ff8f6d49ff7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80"
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
---
|
||||
title: 'Web Awesome'
|
||||
title: 'Web Awesome: A forward-thinking library of web components.'
|
||||
description: Hand-crafted custom elements for any occasion.
|
||||
---
|
||||
|
||||
import Logo from '../../components/Logo.astro';
|
||||
|
||||
<div class="splash">
|
||||
<div class="splash-start">
|
||||
<Logo />
|
||||
</div>
|
||||
<div class="splash-end">
|
||||
<div>
|
||||
|
||||
# <wa-visually-hidden>Web Awesome:</wa-visually-hidden>
|
||||
|
||||
- Works with all frameworks 🧩
|
||||
- Works with CDNs 🚛
|
||||
- Fully customizable with CSS 🎨
|
||||
- Includes a dark theme 🌛
|
||||
- Built with accessibility in mind ♿️
|
||||
- First-class [React support](/frameworks/react) ⚛️
|
||||
- Built-in localization 💬
|
||||
- Open source 😸
|
||||
- [More awesome than ever](https://blog.fontawesome.com/shoelace-joins-font-awesome/) 
|
||||
|
||||
</div>
|
||||
<img
|
||||
class="splash-image"
|
||||
src="/assets/images/undraw-content-team.svg"
|
||||
alt="Cartoon of people assembling components while standing on a giant laptop."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="badges">
|
||||
[](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace)
|
||||
[](https://www.npmjs.com/package/@shoelace-style/shoelace)
|
||||
[](https://github.com/shoelace-style/shoelace/blob/next/LICENSE.md)
|
||||
|
||||
[](https://discord.gg/mg8f26C)
|
||||
[](https://twitter.com/shoelace_style)
|
||||
|
||||
@@ -19,8 +48,14 @@ import Logo from '../../components/Logo.astro';
|
||||
Add the following code to your page.
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/themes/default.css">
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/autoloader.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/themes/default.css"
|
||||
/>
|
||||
<script
|
||||
type="module"
|
||||
src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@%VERSION%/%CDNDIR%/autoloader.js"
|
||||
></script>
|
||||
```
|
||||
|
||||
Now you have access to all of Web Awesome's components! Try adding a button:
|
||||
|
||||
@@ -17,4 +17,5 @@ I’m fully aware that I may not get it right every time for every user, so I in
|
||||
|
||||
This is the path forward. Together, we will continue to make Web Awesome accessible to as many users as possible.
|
||||
|
||||
— The Web Awesome team
|
||||
— Cory LaViska<br>
|
||||
_Creator of Web Awesome_
|
||||
|
||||
@@ -37,7 +37,7 @@ The [community chat](https://discord.gg/mg8f26C) is open to the public and power
|
||||
|
||||
## Twitter
|
||||
|
||||
Follow [@shoelace_style](https://twitter.com/shoelace_style) on Twitter for general updates and announcements about Web Awesome. This is a great place to say "hi" or to share something you're working on.
|
||||
Follow [@shoelace_style](https://twitter.com/shoelace_style) on Twitter for general updates and announcements about Web Awesome. This is a great place to say "hi" or to share something you're working on. You're also welcome to follow [@claviska](https://twitter.com/claviska), the creator, for tweets about web components, web development, and life.
|
||||
|
||||
**Please avoid using Twitter for support questions.** The [discussion forum](https://github.com/shoelace-style/shoelace/discussions) is a much better place to share code snippets, screenshots, and other troubleshooting info. You'll have much better luck there, as more users will have a chance to help you.
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ Internally, each component uses the [BEM methodology](http://getbem.com/) for cl
|
||||
|
||||
### Boolean Props
|
||||
|
||||
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset them using only attributes. To keep the API as friendly and consistent as possible, use a property such as `noValue` and a corresponding kebab-case attribute such as `no-value`.
|
||||
Boolean props should _always_ default to `false`, otherwise there's no way for the user to unset them using only attributes. To keep the API as friendly and consistent as possible, use a property such as `noHeader` and a corresponding kebab-case attribute such as `no-header`.
|
||||
|
||||
When naming boolean props that hide or disable things, prefix them with `no-`, e.g. `no-spin-buttons` and avoid using other verbs such as `hide-` and `disable-` for consistency.
|
||||
|
||||
|
||||
37
docs/src/js/generate-search.js
Normal file
37
docs/src/js/generate-search.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as pagefind from 'pagefind';
|
||||
import * as path from 'node:path';
|
||||
|
||||
// clean up once complete
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
export async function generateSearch() {
|
||||
const { index } = await pagefind.createIndex({});
|
||||
if (!index) return;
|
||||
|
||||
// Get all `src/content/docs/` entries
|
||||
let allContent = await getCollection('docs');
|
||||
|
||||
allContent = allContent.filter(doc => {
|
||||
return doc.data.pagefind !== false;
|
||||
});
|
||||
|
||||
await Promise.allSettled(
|
||||
allContent.map(async entry => {
|
||||
const { category, title, description } = entry.data;
|
||||
return await index?.addCustomRecord({
|
||||
content: entry.body,
|
||||
language: 'en',
|
||||
url: entry.slug,
|
||||
meta: {
|
||||
category: category || '',
|
||||
title,
|
||||
description: description || ''
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const { errors } = await index.writeFiles({
|
||||
outputPath: path.join(process.cwd(), 'public', 'pagefind')
|
||||
});
|
||||
}
|
||||
@@ -94,10 +94,7 @@ const {
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a script tag:
|
||||
</p>
|
||||
<div class="code-preview">
|
||||
<pre><code id="code-block-importing-script" class="language-html" set:html={highlight("html", `<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/${cdndir}/${component.path}"></script>`)}></code></pre>
|
||||
<wa-copy-button from="code-block-importing-script" class="copy-code-button"></wa-copy-button>
|
||||
</div>
|
||||
<pre><code class="language-html" set:html={highlight("html", `<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/${cdndir}/${component.path}"></script>`)}></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="import">
|
||||
@@ -105,32 +102,23 @@ const {
|
||||
To import this component from <a href="https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace">the CDN</a>
|
||||
using a JavaScript import:
|
||||
</p>
|
||||
<div class="code-preview">
|
||||
<pre><code id="code-block-importing-cdn" class="language-js" set:html={
|
||||
highlight("js", `import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/${cdndir}/${component.path}';`)
|
||||
}></code></pre>
|
||||
<wa-copy-button from="code-block-importing-cdn" class="copy-code-button"></wa-copy-button>
|
||||
</div>
|
||||
<pre><code class="language-js" set:html={
|
||||
highlight("js", `import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/${cdndir}/${component.path}';`)
|
||||
}></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="bundler">
|
||||
<p>
|
||||
To import this component using <a href={ rootUrl('/getting-started/installation#bundling') }>a bundler</a>:
|
||||
</p>
|
||||
<div class="code-preview">
|
||||
<pre><code id="code-block-importing-bundler" class="language-js" set:html={highlight("js", `import '@shoelace-style/shoelace/${npmdir}/${component.path}';`)}></code></pre>
|
||||
<wa-copy-button from="code-block-importing-bundler" class="copy-code-button"></wa-copy-button>
|
||||
</div>
|
||||
<pre><code class="language-js" set:html={highlight("js", `import '@shoelace-style/shoelace/${npmdir}/${component.path}';`)}></code></pre>
|
||||
</wa-tab-panel>
|
||||
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To import this component as a <a href="/frameworks/react">React component</a>:
|
||||
</p>
|
||||
<div class="code-preview">
|
||||
<pre><code id="code-block-importing-react" class="language-js" set:html={highlight("js", `import ${component.name} from '@shoelace-style/shoelace/${npmdir}/react/${component.tagNameWithoutPrefix}';`)}></code></pre>
|
||||
<wa-copy-button from="code-block-importing-react" class="copy-code-button"></wa-copy-button>
|
||||
</div>
|
||||
<pre><code class="language-js" set:html={highlight("js", `import ${component.name} from '@shoelace-style/shoelace/${npmdir}/react/${component.tagNameWithoutPrefix}';`)}></code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.grid {
|
||||
wa-page > * {
|
||||
font-size: 1.35rem;
|
||||
text-align: center;
|
||||
display: grid;
|
||||
@@ -6,58 +6,84 @@
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
wa-page[view='mobile'] {
|
||||
--menu-width: 0px;
|
||||
}
|
||||
|
||||
wa-page[view='desktop'] [data-toggle-nav] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-toggle-nav] {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
[slot='banner'],
|
||||
[slot='header'] {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[slot='banner'] {
|
||||
background-color: var(--wa-color-yellow-95);
|
||||
}
|
||||
|
||||
[slot='header'] {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
aside {
|
||||
wa-page[view='mobile'] [slot='header'] {
|
||||
grid-template-columns: 1.5rem 1fr;
|
||||
}
|
||||
|
||||
wa-page[view='desktop'] [slot='header'] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
[slot='menu'],
|
||||
[slot='navigation'],
|
||||
[slot='aside'] {
|
||||
min-width: 250px;
|
||||
max-width: 250px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[slot='menu'],
|
||||
[slot='navigation'],
|
||||
[slot='navigation-header'],
|
||||
[slot='navigation-footer'] {
|
||||
background-color: var(--wa-color-red-80);
|
||||
}
|
||||
|
||||
[slot='aside'] {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: var(--wa-color-green-90);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--wa-color-blue-80);
|
||||
}
|
||||
|
||||
.banner {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: var(--wa-color-blue-90);
|
||||
}
|
||||
|
||||
.banner,
|
||||
.header {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[slot='header'] {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
[slot='aside'] {
|
||||
background-color: var(--wa-color-yellow-90);
|
||||
}
|
||||
|
||||
[slot='menu'] {
|
||||
background-color: var(--wa-color-red-80);
|
||||
}
|
||||
|
||||
[slot='main-header'] {
|
||||
background-color: var(--wa-color-red-90);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[slot='main-footer'] {
|
||||
background-color: var(--wa-color-red-70);
|
||||
}
|
||||
|
||||
[slot='footer'] {
|
||||
background-color: var(--wa-color-blue-80);
|
||||
}
|
||||
|
||||
wa-page::part(drawer) {
|
||||
--size: 300px;
|
||||
--panel-background: var(--wa-color-red-80);
|
||||
}
|
||||
|
||||
astro-dev-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
<wa-page main-id="main-content" class="wa-theme-light">
|
||||
<header slot="banner" class="grid banner">banner</header>
|
||||
|
||||
<header slot="header" class="grid header">header</header>
|
||||
<header slot="banner">banner</header>
|
||||
|
||||
<aside class="grid" slot="menu">menu</aside>
|
||||
<header slot="header">
|
||||
<wa-icon-button name="bars" data-toggle-nav></wa-icon-button>
|
||||
header
|
||||
</header>
|
||||
|
||||
<header class="grid" slot="main-header">main-header</header>
|
||||
<aside slot="navigation">menu</aside>
|
||||
|
||||
<main class="grid" id="main-content">main</main>
|
||||
<header slot="main-header">main-header</header>
|
||||
|
||||
<footer class="grid" slot="main-footer">main-footer</footer>
|
||||
<main id="main-content">main</main>
|
||||
|
||||
<aside class="grid" slot="aside">aside</aside>
|
||||
<footer class="grid" slot="footer">footer</footer>
|
||||
</wa-page>
|
||||
<footer slot="main-footer">main-footer</footer>
|
||||
|
||||
<aside slot="aside">aside</aside>
|
||||
|
||||
<footer slot="footer">footer</footer>
|
||||
|
||||
</wa-page>
|
||||
@@ -15,7 +15,7 @@
|
||||
fun.
|
||||
</p>
|
||||
|
||||
<wa-dialog id="dialog" label="Dialog" with-header> I'm just a lowly dialog. </wa-dialog>
|
||||
<wa-dialog id="dialog"> I'm just a lowly dialog. </wa-dialog>
|
||||
|
||||
<wa-button>Open Dialog</wa-button>
|
||||
</wa-page>
|
||||
|
||||
@@ -196,3 +196,7 @@ table td {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
astro-dev-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px">
|
||||
<h3 slot="header">Total listening time</h3>
|
||||
|
||||
<p>
|
||||
@@ -146,7 +146,7 @@
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px">
|
||||
<h3 slot="header">Total songs played</h3>
|
||||
|
||||
<p>
|
||||
@@ -160,7 +160,7 @@
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px">
|
||||
<h3 slot="header">Average listening session</h3>
|
||||
|
||||
<p>
|
||||
@@ -174,7 +174,7 @@
|
||||
</p>
|
||||
</wa-card>
|
||||
|
||||
<wa-card with-header class="wa-card--muted" style="--padding: 8px">
|
||||
<wa-card class="wa-card--muted" style="--padding: 8px">
|
||||
<h3 slot="header">Average track listening time</h3>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -195,3 +195,7 @@ wa-page[view='mobile'] .navigation--desktop {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
astro-dev-toolbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,58 +1,19 @@
|
||||
// import { APIContext } from "astro";
|
||||
import * as pagefind from 'pagefind';
|
||||
import * as path from 'node:path';
|
||||
|
||||
// clean up once complete
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
export async function generateSearch() {
|
||||
const { index } = await pagefind.createIndex({});
|
||||
if (!index) return;
|
||||
|
||||
let json: Array<{ url: string; content: string }> = [];
|
||||
|
||||
// Get all `src/content/docs/` entries
|
||||
let allContent = await getCollection('docs');
|
||||
|
||||
allContent = allContent.filter(doc => {
|
||||
return doc.data.pagefind !== false;
|
||||
});
|
||||
|
||||
await Promise.allSettled(
|
||||
allContent.map(async entry => {
|
||||
const { category, title, description } = entry.data;
|
||||
const resp = await fetch('http://localhost:4000/' + entry.slug);
|
||||
const html = await resp.text();
|
||||
|
||||
// json.push({
|
||||
// content: html,
|
||||
// url: entry.slug
|
||||
// });
|
||||
return await index?.addHTMLFile({
|
||||
content: html,
|
||||
url: entry.slug
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const { errors } = await index.writeFiles({
|
||||
outputPath: path.join(process.cwd(), 'public', 'pagefind')
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
let json: Record<string, unknown> = {};
|
||||
import { generateSearch } from '../js/generate-search';
|
||||
|
||||
if (process.env.DEV_SEARCH !== 'generated') {
|
||||
await generateSearch();
|
||||
|
||||
// If you're debugging search, comment the next line.
|
||||
process.env.DEV_SEARCH = 'generated';
|
||||
|
||||
// setTimeout(() => {
|
||||
// process.env.DEV_SEARCH = ""
|
||||
// }, 200)
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return new Response(JSON.stringify(json), {
|
||||
return new Response(null, {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -66,10 +66,10 @@ function generateCodeBlock(node: Node) {
|
||||
language = 'plaintext';
|
||||
}
|
||||
|
||||
node.value = html`<div class="code-preview">
|
||||
<pre><code id='code-block-${++count}' class="language-${language}">${highlight(language, node.value)}</code></pre>
|
||||
${copyButton(`code-block-${count}`)}
|
||||
</div>`;
|
||||
node.value = html`<pre><code id='code-block-${++count}' class="language-${language}">${highlight(
|
||||
language,
|
||||
node.value
|
||||
)}</code>${copyButton(`code-block-${count}`)}</pre>`;
|
||||
}
|
||||
|
||||
function generatePreviewCodeBlock(node: Node, reactCode: string) {
|
||||
|
||||
285
docs/src/styles/font-awesome.css
vendored
285
docs/src/styles/font-awesome.css
vendored
@@ -1,285 +0,0 @@
|
||||
:root {
|
||||
--sl-font: 'cera-round-pro', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
}
|
||||
|
||||
div.ec-line {
|
||||
/* Fixes extra spacing on code blocks */
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* Dark mode colors. */
|
||||
:root {
|
||||
--sl-color-text: #ffffff;
|
||||
--sl-color-accent-low: #1d242f;
|
||||
--sl-color-accent: #506b92;
|
||||
--sl-color-accent-high: #bfc9d8;
|
||||
--sl-color-white: #ffffff;
|
||||
--sl-color-gray-1: #e3efff;
|
||||
--sl-color-gray-2: #b4c3d9;
|
||||
--sl-color-gray-3: #708db5;
|
||||
--sl-color-gray-4: #3e597e;
|
||||
--sl-color-gray-5: #1f395b;
|
||||
--sl-color-gray-6: #0e2748;
|
||||
--sl-color-black: #0d1928;
|
||||
|
||||
--fa-dk-teal: #0ca678;
|
||||
--fa-gravy: #c3c6d1;
|
||||
--fa-md-gravy: rgb(97, 109, 138);
|
||||
--fa-dk-red: #e03131;
|
||||
--fa-dk-yellow: rgb(250, 176, 5);
|
||||
--fa-yellow: #ffd43b;
|
||||
--link-color: rgb(20, 110, 190);
|
||||
|
||||
--balloon-border-radius: 2px;
|
||||
--balloon-color: rgb(24 49 83);
|
||||
--balloon-font-size: 12px;
|
||||
--balloon-move: 4px;
|
||||
--balloon-text-color: #fff;
|
||||
}
|
||||
|
||||
/* Light mode colors. */
|
||||
:root[data-theme='light'] {
|
||||
--sl-color-text: rgb(24, 49, 83);
|
||||
|
||||
--sl-color-accent-low: #d0d7e3;
|
||||
--sl-color-accent: #526d94;
|
||||
--sl-color-accent-high: #273344;
|
||||
--sl-color-white: #0d1928;
|
||||
--sl-color-gray-1: #0e2748;
|
||||
--sl-color-gray-2: #1f395b;
|
||||
--sl-color-gray-3: #3e597e;
|
||||
--sl-color-gray-4: #708db5;
|
||||
--sl-color-gray-5: #b4c3d9;
|
||||
--sl-color-gray-6: #e3efff;
|
||||
--sl-color-gray-7: #f1f7ff;
|
||||
--sl-color-black: #ffffff;
|
||||
|
||||
--fa-dk-teal: #0ca678;
|
||||
--fa-gravy: #c3c6d1;
|
||||
--fa-md-gravy: rgb(97, 109, 138);
|
||||
--fa-dk-red: #e03131;
|
||||
--fa-dk-yellow: rgb(250, 176, 5);
|
||||
--fa-yellow: #ffd43b;
|
||||
--link-color: rgb(20, 110, 190);
|
||||
|
||||
--balloon-border-radius: 2px;
|
||||
--balloon-color: rgb(24 49 83);
|
||||
--balloon-font-size: 12px;
|
||||
--balloon-move: 4px;
|
||||
--balloon-text-color: #fff;
|
||||
}
|
||||
|
||||
.icon-padding {
|
||||
padding: 0 8px 0 8px;
|
||||
}
|
||||
|
||||
.remove-margins {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
border-radius: 999rem;
|
||||
text-decoration: none;
|
||||
padding: 1rem 1.25rem;
|
||||
background: var(--sl-color-text-accent);
|
||||
color: var(--sl-color-black) !important;
|
||||
}
|
||||
|
||||
.action-button > i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* table, */
|
||||
/* tr, */
|
||||
/* td, */
|
||||
/* th { */
|
||||
/* border: none !important; */
|
||||
/* } */
|
||||
/**/
|
||||
/* table thead { */
|
||||
/* text-align: left; */
|
||||
/* font-weight: bold; */
|
||||
/* color: rgb(24, 49, 83); */
|
||||
/* border-bottom: 2px solid rgb(195, 198, 209); */
|
||||
/* } */
|
||||
/**/
|
||||
/* table tr { */
|
||||
/* vertical-align: top; */
|
||||
/* } */
|
||||
/**/
|
||||
/* td { */
|
||||
/* border-bottom: 1px solid rgb(224, 226, 232); */
|
||||
/* } */
|
||||
/**/
|
||||
/* table tr:nth-child(odd) td { */
|
||||
/* background: #fff; */
|
||||
/* border-bottom: 1px solid rgb(224, 226, 232) !important; */
|
||||
/* } */
|
||||
/**/
|
||||
/* table tr:nth-child(even) td { */
|
||||
/* background: #fff; */
|
||||
/* border-bottom: 1px solid rgb(224, 226, 232) !important; */
|
||||
/* } */
|
||||
|
||||
.frame.is-terminal pre {
|
||||
margin: 0px !important;
|
||||
/* margin-top: 1px !important; */
|
||||
background: rgb(24 49 83) !important;
|
||||
}
|
||||
|
||||
.frame.has-title pre {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
figcaption.header {
|
||||
background: rgb(0, 28, 64) !important;
|
||||
}
|
||||
|
||||
img {
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
}
|
||||
|
||||
.site-title > img {
|
||||
border-radius: 0px;
|
||||
color: rgb(20, 110, 190);
|
||||
}
|
||||
|
||||
.starlight-aside {
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
}
|
||||
|
||||
.starlight-aside__content > ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.starlight-aside__content > ul > li:before {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-feature-settings: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: 'Font Awesome 6 Pro', 'Font Awesome 5 Pro';
|
||||
font-weight: 900;
|
||||
content: '\f058';
|
||||
margin-right: calc(1em * 1);
|
||||
margin-right: 16px;
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: rgb(97, 109, 138);
|
||||
}
|
||||
|
||||
img + em {
|
||||
display: block;
|
||||
color: rgb(97, 109, 138);
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
p > em {
|
||||
color: rgb(97, 109, 138);
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
h1 h2 h3 {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.theme-ravenclaw {
|
||||
--fa-secondary-opacity: 1;
|
||||
--fa-primary-color: rgb(4, 56, 161);
|
||||
--fa-secondary-color: rgb(108, 108, 108);
|
||||
}
|
||||
|
||||
.fa-ul {
|
||||
padding-left: 0;
|
||||
margin-left: 2.14285714em;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.fa-ul > li {
|
||||
position: relative;
|
||||
}
|
||||
.fa-li {
|
||||
position: absolute;
|
||||
left: -2.14285714em;
|
||||
width: 2.14285714em;
|
||||
top: 0.14285714em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
:root {
|
||||
--sl-text-h1: var(--sl-text-4xl);
|
||||
--sl-text-h2: var(--sl-text-3xl);
|
||||
--sl-text-h3: var(--sl-text-2xl);
|
||||
--sl-text-h4: var(--sl-text-1xl);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
/* border: 2px solid rgb(24, 49, 83); */
|
||||
}
|
||||
|
||||
.card {
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
padding: 0px 20px 10px 20px !important;
|
||||
border: 2px solid rgb(24, 49, 83) !important;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.hero > img {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
a.site-title > img {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.grid-button {
|
||||
padding: 10px 32px 10px 32px;
|
||||
margin: 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.return-button {
|
||||
border-radius: 999rem;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--sl-color-text-accent);
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.right-sidebar-panel h2 a {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.right-sidebar-container ul {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -22,10 +22,6 @@
|
||||
--docs-shadow-x-large: 0 4px 16px hsl(240 3.8% 46.1% / 24%);
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
/* Utils */
|
||||
html.wa-theme-dark .only-light,
|
||||
html:not(.wa-theme-dark) .only-dark {
|
||||
@@ -48,26 +44,10 @@ html:not(.wa-theme-dark) .only-dark {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.sidebar-content ul,
|
||||
.sidebar-content details,
|
||||
.sidebar-content summary {
|
||||
padding: unset;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
.sidebar-content summary {
|
||||
text-indent: 0.5rem;
|
||||
padding: 0.2em var(--sl-sidebar-item-padding-inline);
|
||||
margin-block: 0.5rem;
|
||||
}
|
||||
|
||||
.sidebar-content a {
|
||||
text-decoration: unset;
|
||||
}
|
||||
|
||||
.main-frame main h1 {
|
||||
margin: 0;
|
||||
margin-block-start: 1rem;
|
||||
@media screen and (max-width: 900px) {
|
||||
:root {
|
||||
--docs-content-padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -188,6 +168,11 @@ th.table-description {
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
|
||||
pre:not(:last-child) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
@@ -206,6 +191,71 @@ pre > code {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre .token.comment {
|
||||
color: var(--wa-color-base-40);
|
||||
}
|
||||
|
||||
pre .token.prolog,
|
||||
pre .token.doctype,
|
||||
pre .token.cdata,
|
||||
pre .token.operator,
|
||||
pre .token.punctuation {
|
||||
color: var(--wa-color-base-40);
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
pre .token.property,
|
||||
pre .token.keyword,
|
||||
pre .token.tag,
|
||||
pre .token.url {
|
||||
color: var(--wa-color-brand-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.symbol,
|
||||
pre .token.deleted {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.boolean,
|
||||
pre .token.constant,
|
||||
pre .token.selector,
|
||||
pre .token.attr-name,
|
||||
pre .token.string,
|
||||
pre .token.char,
|
||||
pre .token.builtin,
|
||||
pre .token.inserted {
|
||||
color: var(--wa-color-success-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.atrule,
|
||||
pre .token.attr-value,
|
||||
pre .token.number,
|
||||
pre .token.variable {
|
||||
color: var(--wa-color-warning-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.function,
|
||||
pre .token.class-name,
|
||||
pre .token.regex {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.important {
|
||||
color: var(--wa-color-danger-text-on-fill);
|
||||
}
|
||||
|
||||
pre .token.important,
|
||||
pre .token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
pre .token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Copy code button */
|
||||
.copy-code-button {
|
||||
position: absolute;
|
||||
@@ -219,30 +269,594 @@ pre > code {
|
||||
}
|
||||
|
||||
.copy-code-button::part(button) {
|
||||
background-color: var(--wa-color-primary-fill-subtle);
|
||||
background-color: var(--wa-color-neutral-fill-subtle);
|
||||
border-radius: 0 var(--wa-corners-s) 0 var(--wa-corners-s);
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.copy-code-button::part(button):hover {
|
||||
background-color: color-mix(in oklab, var(--wa-color-primary-fill-subtle), var(--wa-color-mix-hover));
|
||||
background-color: color-mix(in oklab, var(--wa-color-neutral-fill-subtle), var(--wa-color-mix-hover));
|
||||
}
|
||||
|
||||
.copy-code-button::part(button):active {
|
||||
background-color: color-mix(in oklab, var(--wa-color-primary-fill-subtle), var(--wa-color-mix-active));
|
||||
background-color: color-mix(in oklab, var(--wa-color-neutral-fill-subtle), var(--wa-color-mix-active));
|
||||
}
|
||||
|
||||
:is(.code-preview__source, pre, .code-preview) > .copy-code-button {
|
||||
:is(.code-preview__source, pre) .copy-code-button {
|
||||
opacity: 0;
|
||||
scale: 0.75;
|
||||
}
|
||||
|
||||
:is(.code-preview__source, pre, .code-preview):hover > .copy-code-button,
|
||||
:is(.code-preview__source, pre):hover .copy-code-button,
|
||||
.copy-code-button:focus-within {
|
||||
opacity: 1;
|
||||
scale: 1;
|
||||
}
|
||||
|
||||
/* Callouts */
|
||||
.callout {
|
||||
margin-bottom: var(--docs-content-vertical-spacing);
|
||||
}
|
||||
|
||||
.callout > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.callout > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.callout a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.callout p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Aside */
|
||||
.content aside {
|
||||
float: right;
|
||||
min-width: 300px;
|
||||
max-width: 50%;
|
||||
background: var(--wa-color-surface-lowered);
|
||||
border-radius: var(--wa-corners-s);
|
||||
padding: var(--wa-space-m);
|
||||
margin-left: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.content aside > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content aside > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.content aside {
|
||||
float: none;
|
||||
width: calc(100% + (var(--docs-content-padding) * 2));
|
||||
max-width: none;
|
||||
margin: var(--docs-content-vertical-spacing) calc(-1 * var(--docs-content-padding));
|
||||
}
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
flex: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 20;
|
||||
width: var(--docs-sidebar-width);
|
||||
background-color: var(--docs-background-color);
|
||||
border-right: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
border-radius: 0;
|
||||
padding: 2rem;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
transition: var(--docs-sidebar-transition-speed) translate ease-in-out;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
#sidebar:hover::-webkit-scrollbar-thumb {
|
||||
background: var(--wa-color-base-60);
|
||||
}
|
||||
|
||||
#sidebar:hover::-webkit-scrollbar-track {
|
||||
background: var(--wa-color-base-95);
|
||||
}
|
||||
|
||||
#sidebar > header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#sidebar > header h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sidebar > header svg {
|
||||
margin-bottom: var(--wa-space-s);
|
||||
}
|
||||
|
||||
#sidebar > header a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sidebar nav a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sidebar nav h2 {
|
||||
font-size: var(--wa-font-size-m);
|
||||
font-weight: var(--wa-font-weight-medium);
|
||||
border-bottom: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
margin: var(--wa-space-xl) 0 var(--wa-space-xs) 0;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0.125rem 0.5rem;
|
||||
}
|
||||
|
||||
#sidebar ul ul ul {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
line-height: 1.33;
|
||||
color: inherit;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li a:not(.active-link):hover {
|
||||
color: var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
#sidebar nav .active-link {
|
||||
color: var(--wa-color-text-link);
|
||||
border-bottom: dashed 1px var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
#sidebar > header img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#sidebar {
|
||||
translate: -100%;
|
||||
}
|
||||
|
||||
.sidebar-open #sidebar {
|
||||
translate: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-version {
|
||||
font-size: var(--wa-font-size-s);
|
||||
color: var(--wa-color-text-quiet);
|
||||
text-align: right;
|
||||
margin-top: calc(-1 * var(--wa-space-s));
|
||||
margin-bottom: calc(-1 * var(--wa-space-s));
|
||||
}
|
||||
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* Main content */
|
||||
main {
|
||||
position: relative;
|
||||
padding: var(--docs-content-vertical-spacing) var(--docs-content-padding)
|
||||
calc(var(--docs-content-vertical-spacing) * 2) var(--docs-content-padding);
|
||||
/* margin-left: var(--docs-sidebar-width); */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-open .content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.content__body > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
main {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component layouts */
|
||||
.content {
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
max-width: var(--docs-content-max-width);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content--with-toc {
|
||||
/* There's a 2rem gap, so we need to remove it from the column */
|
||||
grid-template-columns: calc(75% - 2rem) min(25%, var(--docs-content-toc-max-width));
|
||||
max-width: calc(var(--docs-content-max-width) + var(--docs-content-toc-max-width));
|
||||
}
|
||||
|
||||
.content__body {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content:not(.content--with-toc) .content__toc {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content__toc {
|
||||
order: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.content__toc ul {
|
||||
position: sticky;
|
||||
top: 5rem;
|
||||
max-height: calc(100vh - 6rem);
|
||||
font-size: var(--wa-font-size-s);
|
||||
line-height: 1.33;
|
||||
border-left: var(--wa-border-style) var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
list-style: none;
|
||||
padding: 1rem 0;
|
||||
margin: 0;
|
||||
padding-left: 1rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.content__toc li {
|
||||
padding: 0 0 0 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.content__toc li[data-level='3'] {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
/* We don't use them, but just in case */
|
||||
.content__toc li[data-level='4'],
|
||||
.content__toc li[data-level='5'],
|
||||
.content__toc li[data-level='6'] {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.content__toc li:not(:last-of-type) {
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
.content__toc a {
|
||||
color: var(--wa-color-text-normal);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content__toc a:hover {
|
||||
color: var(--wa-color-text-link);
|
||||
}
|
||||
|
||||
.content__toc a.active {
|
||||
color: var(--wa-color-brand-text-on-surface);
|
||||
border-bottom: dashed 1px var(--wa-color-brand-text-on-surface);
|
||||
}
|
||||
|
||||
.content__toc .top a {
|
||||
font-weight: var(--wa-font-weight-medium);
|
||||
color: var(--wa-color-text-quiet);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.content {
|
||||
grid-template-columns: 100%;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.content__toc {
|
||||
position: relative;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.content__toc ul {
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
gap: 1rem 1.5rem;
|
||||
position: static;
|
||||
border: none;
|
||||
border-bottom: solid 1px var(--wa-color-surface-border);
|
||||
border-radius: 0;
|
||||
padding: 1rem 1.5rem 1rem 0.5rem; /* extra right padding to hide the fade effect */
|
||||
margin-top: 1rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.content__toc ul::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 1rem; /* don't cover the scrollbar */
|
||||
right: 0;
|
||||
width: 2rem;
|
||||
background: linear-gradient(90deg, rgba(0 0 0 / 0) 0%, var(--wa-color-surface-default) 100%);
|
||||
}
|
||||
|
||||
.content__toc li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content__toc li:not(:last-of-type) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.content__toc [data-level]:not([data-level='2']) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content__body {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Menu toggle */
|
||||
#menu-toggle {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 30;
|
||||
top: 0.25rem;
|
||||
left: 0.25rem;
|
||||
height: auto;
|
||||
width: auto;
|
||||
color: var(--wa-color-neutral-spot);
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: var(--wa-color-surface-default);
|
||||
padding: 0.5rem;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
transition: 250ms rotate ease;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#menu-toggle {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
#menu-toggle svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
html.sidebar-open #menu-toggle {
|
||||
rotate: 180deg;
|
||||
}
|
||||
|
||||
/* Skip to main content */
|
||||
#skip-to-main {
|
||||
position: fixed;
|
||||
top: 0.25rem;
|
||||
left: calc(50% - var(--docs-skip-to-main-width) / 2);
|
||||
z-index: 100;
|
||||
width: var(--docs-skip-to-main-width);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-radius: 9999px;
|
||||
background: var(--wa-color-surface-default);
|
||||
color: var(--wa-color-text-normal);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
a:not(.anchor-heading)[href]::after {
|
||||
content: ' (' attr(href) ')';
|
||||
}
|
||||
|
||||
details,
|
||||
pre {
|
||||
border: solid var(--wa-border-width-s) var(--wa-color-surface-border);
|
||||
}
|
||||
|
||||
details summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
details summary span {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
details summary::marker,
|
||||
details summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.component-page__navigation,
|
||||
.copy-code-button,
|
||||
.code-preview__buttons,
|
||||
.code-preview__resizer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.flavor-html .code-preview__source--html,
|
||||
.flavor-react .code-preview__source--react {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.flavor-html .code-preview__source--html > pre,
|
||||
.flavor-react .code-preview__source--react > pre {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.code-preview__source-group {
|
||||
border-bottom: solid 1px var(--wa-border-width-s);
|
||||
border-bottom-left-radius: var(--wa-corners-s);
|
||||
border-bottom-right-radius: var(--wa-corners-s);
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
#menu-toggle,
|
||||
#icon-toolbar,
|
||||
.external-link__icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Splash */
|
||||
.splash {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
|
||||
.splash-start {
|
||||
min-width: 440px;
|
||||
}
|
||||
|
||||
.splash li img {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
.splash svg {
|
||||
margin-block-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.splash-end {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
main .sl-container {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.splash-image {
|
||||
width: calc(100% - 300px);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.splash-start h1:first-of-type {
|
||||
font-size: var(--wa-font-size-l);
|
||||
font-weight: var(--wa-font-weight-normal);
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
.splash {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.splash-start {
|
||||
min-width: 0;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.splash-end {
|
||||
display: block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.splash-image {
|
||||
display: block;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
/* Shields */
|
||||
.splash + p {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Component headers */
|
||||
.component-header h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.component-header__tag {
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.component-header__tag code {
|
||||
background: none;
|
||||
color: var(--wa-color-text-quiet);
|
||||
font-size: var(--wa-font-size-l);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.component-header__info {
|
||||
margin-bottom: var(--wa-space-2xl);
|
||||
}
|
||||
|
||||
.component-summary {
|
||||
font-size: var(--wa-font-size-l);
|
||||
line-height: 1.6;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
/* Repo buttons */
|
||||
.sidebar-buttons {
|
||||
display: flex;
|
||||
gap: 0.125rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sidebar-buttons .repo-button {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.repo-button wa-icon {
|
||||
color: var(--wa-color-neutral-text-on-spot);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 400px) {
|
||||
:not(.sidebar-buttons) > .repo-button {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child,
|
||||
body[data-page^='/tokens/'] .table-wrapper td:first-child code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Border demos */
|
||||
.border-demo {
|
||||
height: 60px;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
:root {
|
||||
--sl-content-width: 50rem;
|
||||
}
|
||||
|
||||
/* Code highlighter */
|
||||
pre,
|
||||
.sl-markdown-content pre:not(:where(.not-content *)) {
|
||||
|
||||
1387
package-lock.json
generated
1387
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@
|
||||
"build": "node scripts/build.js",
|
||||
"docs:build": "cd docs && npm run build -- --silent",
|
||||
"verify": "npm run prettier:check && npm run lint && npm run build && npm run test",
|
||||
"prepare": "./scripts/clear-vercel-patches.sh && npm i --ignore-scripts && npx patch-package && npx playwright install",
|
||||
"postinstall": "npx playwright install && ./scripts/clear-vercel-patches.sh && npm i --ignore-scripts && npx patch-package",
|
||||
"prepublishOnly": "npm run verify",
|
||||
"prettier": "prettier --write --log-level warn .",
|
||||
"prettier:check": "prettier --check --log-level warn .",
|
||||
@@ -86,9 +86,9 @@
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.5",
|
||||
"@typescript-eslint/parser": "^6.7.5",
|
||||
"@web/dev-server-esbuild": "^0.3.6",
|
||||
"@web/test-runner": "^0.18.1",
|
||||
"@web/test-runner-commands": "^0.9.0",
|
||||
"@web/test-runner-playwright": "^0.11.0",
|
||||
"@web/test-runner": "^0.15.3",
|
||||
"@web/test-runner-commands": "^0.6.6",
|
||||
"@web/test-runner-playwright": "^0.9.0",
|
||||
"astro": "^4.0.1",
|
||||
"bootstrap-icons": "^1.11.1",
|
||||
"browser-sync": "^2.29.3",
|
||||
@@ -133,7 +133,6 @@
|
||||
"npm-check-updates": "^16.14.6",
|
||||
"pascal-case": "^3.1.2",
|
||||
"patch-package": "^8.0.0",
|
||||
"playwright": "^1.42.0",
|
||||
"plop": "^4.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"prismjs": "^1.29.0",
|
||||
|
||||
@@ -24,7 +24,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --example - An example CSS custom property.
|
||||
*/
|
||||
export default class {{ properCase tag }} extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './alert.styles.js';
|
||||
import WaIconButton from '../icon-button/icon-button.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -50,10 +50,11 @@ const toastStack = Object.assign(document.createElement('div'), { className: 'wa
|
||||
* @animation alert.hide - The animation to use when hiding the alert.
|
||||
*/
|
||||
export default class WaAlert extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon-button': WaIconButton };
|
||||
|
||||
private autoHideTimeout: number;
|
||||
private readonly hasSlotController = new HasSlotController(this, 'icon', 'suffix');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('[part~="base"]') base: HTMLElement;
|
||||
@@ -196,6 +197,7 @@ export default class WaAlert extends WebAwesomeElement {
|
||||
alert: true,
|
||||
'alert--open': this.open,
|
||||
'alert--closable': this.closable,
|
||||
'alert--has-icon': this.hasSlotController.test('icon'),
|
||||
'alert--brand': this.variant === 'brand',
|
||||
'alert--success': this.variant === 'success',
|
||||
'alert--neutral': this.variant === 'neutral',
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--border-radius: var(--wa-panel-corners);
|
||||
--border-style: var(--wa-panel-border-style);
|
||||
@@ -56,10 +59,10 @@ export default css`
|
||||
border-width: var(--border-width);
|
||||
color: var(--content-color);
|
||||
font: inherit;
|
||||
padding: var(--padding);
|
||||
margin: inherit;
|
||||
}
|
||||
|
||||
.alert:not(.alert--has-icon) .alert__icon,
|
||||
.alert:not(.alert--closable) .alert__close-button {
|
||||
display: none;
|
||||
}
|
||||
@@ -70,15 +73,13 @@ export default css`
|
||||
align-items: center;
|
||||
color: var(--icon-color);
|
||||
font-size: var(--icon-size);
|
||||
}
|
||||
|
||||
.alert__icon ::slotted(*) {
|
||||
margin-inline-end: var(--padding) !important;
|
||||
padding-inline-start: var(--padding);
|
||||
}
|
||||
|
||||
.alert__message {
|
||||
flex: 1 1 auto;
|
||||
display: block;
|
||||
padding: var(--padding);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ export default css`
|
||||
align-items: center;
|
||||
color: currentColor;
|
||||
font-size: var(--wa-font-size-m);
|
||||
padding-inline-start: var(--padding);
|
||||
padding-inline-end: var(--padding);
|
||||
}
|
||||
|
||||
.alert__close-button:hover::part(base) {
|
||||
|
||||
@@ -63,8 +63,7 @@ describe('<wa-alert>', () => {
|
||||
|
||||
afterEach(async () => {
|
||||
clock?.restore();
|
||||
// eslint-disable-next-line
|
||||
await resetMouse().catch(() => {});
|
||||
await resetMouse();
|
||||
});
|
||||
|
||||
it('renders', async () => {
|
||||
@@ -97,30 +96,28 @@ describe('<wa-alert>', () => {
|
||||
|
||||
expectAlertToBeInvisible(alert);
|
||||
|
||||
await expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, async () => await alert.show());
|
||||
await expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, () => alert.show());
|
||||
});
|
||||
|
||||
it('should emit wa-hide and wa-after-hide when calling hide()', async () => {
|
||||
const alert = await fixture<WaAlert>(html` <wa-alert open>I am an alert</wa-alert>`);
|
||||
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, async () => await alert.hide());
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, () => alert.hide());
|
||||
});
|
||||
|
||||
it('should emit wa-show and wa-after-show when setting open = true', async () => {
|
||||
const alert = await fixture<WaAlert>(html` <wa-alert>I am an alert</wa-alert> `);
|
||||
|
||||
await expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, async () => {
|
||||
await expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, () => {
|
||||
alert.open = true;
|
||||
await alert.updateComplete;
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit wa-hide and wa-after-hide when setting open = false', async () => {
|
||||
const alert = await fixture<WaAlert>(html` <wa-alert open>I am an alert</wa-alert> `);
|
||||
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, async () => {
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, () => {
|
||||
alert.open = false;
|
||||
await alert.updateComplete;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -137,8 +134,8 @@ describe('<wa-alert>', () => {
|
||||
const alert = await fixture<WaAlert>(html` <wa-alert open closable>I am an alert</wa-alert> `);
|
||||
const closeButton = getCloseButton(alert);
|
||||
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, async () => {
|
||||
await clickOnElement(closeButton!);
|
||||
await expectHideAndAfterHideToBeEmittedInCorrectOrder(alert, () => {
|
||||
clickOnElement(closeButton!);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -162,7 +159,7 @@ describe('<wa-alert>', () => {
|
||||
it('can be rendered as a toast', async () => {
|
||||
const alert = await fixture<WaAlert>(html`<wa-alert>I am an alert</wa-alert>`);
|
||||
|
||||
await expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, async () => await alert.toast());
|
||||
expectShowAndAfterShowToBeEmittedInCorrectOrder(alert, () => alert.toast());
|
||||
const toastStack = getToastStack();
|
||||
expect(toastStack).to.be.visible;
|
||||
expect(toastStack?.firstChild).to.be.equal(alert);
|
||||
@@ -295,15 +292,17 @@ describe('<wa-alert>', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should properly render alert variants', async () => {
|
||||
const variants = ['brand', 'success', 'neutral', 'warning', 'danger'];
|
||||
|
||||
for (const variant of variants) {
|
||||
const alert = await fixture<WaAlert>(html`<wa-alert variant="${variant}" open>I am an alert</wa-alert>`);
|
||||
|
||||
const alertContainer = getAlertContainer(alert);
|
||||
expect(alertContainer).to.have.class(`alert--${variant}`);
|
||||
}
|
||||
|
||||
describe('alert variants', () => {
|
||||
const variants = ['brand', 'success', 'neutral', 'warning', 'danger'];
|
||||
|
||||
variants.forEach(variant => {
|
||||
it(`adapts to the variant: ${variant}`, async () => {
|
||||
const alert = await fixture<WaAlert>(html`<wa-alert variant="${variant}" open>I am an alert</wa-alert>`);
|
||||
|
||||
const alertContainer = getAlertContainer(alert);
|
||||
expect(alertContainer).to.have.class(`alert--${variant}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './animated-image.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -27,7 +26,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --icon-size - The size of the play/pause icons.
|
||||
*/
|
||||
export default class WaAnimatedImage extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
@query('.animated-image__animated') animatedImage: HTMLImageElement;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--control-box-size: 3rem;
|
||||
--icon-size: calc(var(--control-box-size) * 0.625);
|
||||
|
||||
@@ -2,7 +2,6 @@ import { animations } from './animations.js';
|
||||
import { html } from 'lit';
|
||||
import { property, queryAsync } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './animation.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -21,7 +20,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* animate multiple elements, either wrap them in a single container or use multiple `<wa-animation>` elements.
|
||||
*/
|
||||
export default class WaAnimation extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private animation?: Animation;
|
||||
private hasStarted = false;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { html } from 'lit';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './avatar.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -26,7 +25,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --size - The size of the avatar.
|
||||
*/
|
||||
export default class WaAvatar extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = {
|
||||
'wa-icon': WaIcon
|
||||
};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './badge.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -24,7 +23,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --content-color - The color of the badge's content.
|
||||
*/
|
||||
export default class WaBadge extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
/** The badge's theme variant. */
|
||||
@property({ reflect: true }) variant: 'brand' | 'success' | 'neutral' | 'warning' | 'danger' = 'brand';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--border-color: var(--wa-color-surface-default);
|
||||
--border-radius: var(--wa-corners-xs);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './breadcrumb-item.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -25,7 +26,9 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @csspart separator - The container that wraps the separator.
|
||||
*/
|
||||
export default class WaBreadcrumbItem extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'prefix', 'suffix');
|
||||
|
||||
/**
|
||||
* Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be rendered
|
||||
@@ -43,7 +46,14 @@ export default class WaBreadcrumbItem extends WebAwesomeElement {
|
||||
const isLink = this.href ? true : false;
|
||||
|
||||
return html`
|
||||
<div part="base" class="breadcrumb-item">
|
||||
<div
|
||||
part="base"
|
||||
class=${classMap({
|
||||
'breadcrumb-item': true,
|
||||
'breadcrumb-item--has-prefix': this.hasSlotController.test('prefix'),
|
||||
'breadcrumb-item--has-suffix': this.hasSlotController.test('suffix')
|
||||
})}
|
||||
>
|
||||
<span part="prefix" class="breadcrumb-item__prefix">
|
||||
<slot name="prefix"></slot>
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-flex;
|
||||
}
|
||||
@@ -58,13 +61,14 @@ export default css`
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.breadcrumb-item__prefix,
|
||||
.breadcrumb-item__suffix {
|
||||
.breadcrumb-item--has-prefix .breadcrumb-item__prefix {
|
||||
display: inline-flex;
|
||||
margin-inline-end: var(--wa-space-s);
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
margin-inline-end: var(--wa-space-s) !important;
|
||||
}
|
||||
.breadcrumb-item--has-suffix .breadcrumb-item__suffix {
|
||||
display: inline-flex;
|
||||
margin-inline-start: var(--wa-space-s);
|
||||
}
|
||||
|
||||
:host(:last-of-type) .breadcrumb-item__separator {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './breadcrumb.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -22,7 +21,7 @@ import type WaBreadcrumbItem from '../breadcrumb-item/breadcrumb-item.js';
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class WaBreadcrumb extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { html } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './button-group.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -16,7 +15,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class WaButtonGroup extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { FormControlController, validValidityState } from '../../internal/form.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html, literal } from 'lit/static-html.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './button.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WaSpinner from '../spinner/spinner.component.js';
|
||||
@@ -53,7 +53,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-element.js
|
||||
* @cssproperty --label-color-hover - The color of the button's label on hover.
|
||||
*/
|
||||
export default class WaButton extends WebAwesomeElement implements WebAwesomeFormControl {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = {
|
||||
'wa-icon': WaIcon,
|
||||
'wa-spinner': WaSpinner
|
||||
@@ -62,6 +62,7 @@ export default class WaButton extends WebAwesomeElement implements WebAwesomeFor
|
||||
private readonly formControlController = new FormControlController(this, {
|
||||
assumeInteractionOn: ['click']
|
||||
});
|
||||
private readonly hasSlotController = new HasSlotController(this, '[default]', 'prefix', 'suffix');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
@@ -286,7 +287,10 @@ export default class WaButton extends WebAwesomeElement implements WebAwesomeFor
|
||||
'button--standard': !this.outline,
|
||||
'button--outline': this.outline,
|
||||
'button--pill': this.pill,
|
||||
'button--rtl': this.localize.dir() === 'rtl'
|
||||
'button--rtl': this.localize.dir() === 'rtl',
|
||||
'button--has-label': this.hasSlotController.test('[default]'),
|
||||
'button--has-prefix': this.hasSlotController.test('prefix'),
|
||||
'button--has-suffix': this.hasSlotController.test('suffix')
|
||||
})}
|
||||
?disabled=${ifDefined(isLink ? undefined : this.disabled)}
|
||||
type=${ifDefined(isLink ? undefined : this.type)}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--border-radius: var(--wa-form-controls-corners);
|
||||
--border-style: var(--wa-border-style);
|
||||
@@ -213,13 +216,13 @@ export default css`
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.button:hover:not(.button--disabled, .button--loading) {
|
||||
.button:hover:not(.button--disabled) {
|
||||
background: var(--background-hover, var(--background, none));
|
||||
border-color: var(--border-color-hover, var(--border-color, transparent));
|
||||
color: var(--label-color-hover, var(--label-color));
|
||||
}
|
||||
|
||||
.button:active:not(.button--disabled, .button--loading) {
|
||||
.button:active:not(.button--disabled) {
|
||||
background: var(--background-active, var(--background, none));
|
||||
border-color: var(--border-color-active, var(--border-color, transparent));
|
||||
color: var(--label-color-active, var(--label-color));
|
||||
@@ -300,21 +303,6 @@ export default css`
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button--small .button__caret {
|
||||
margin-inline-start: calc(-0.5 * var(--wa-space-xs));
|
||||
margin-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.button--medium .button__caret {
|
||||
margin-inline-start: calc(-0.5 * var(--wa-space-s));
|
||||
margin-inline-end: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--large .button__caret {
|
||||
margin-inline-start: calc(-0.5 * var(--wa-space-m));
|
||||
margin-inline-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.button--caret .button__caret::part(svg) {
|
||||
width: 0.875em;
|
||||
height: 0.875em;
|
||||
@@ -369,46 +357,70 @@ export default css`
|
||||
* Button spacing
|
||||
*/
|
||||
|
||||
.button--small {
|
||||
::slotted([slot='prefix']) {
|
||||
margin-inline-start: var(--wa-space-xs) !important;
|
||||
}
|
||||
|
||||
::slotted([slot='suffix']) {
|
||||
margin-inline-end: var(--wa-space-xs) !important;
|
||||
}
|
||||
|
||||
.button__label {
|
||||
padding: 0 var(--wa-space-s);
|
||||
}
|
||||
.button--has-label.button--small .button__label {
|
||||
padding: 0 var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--medium {
|
||||
::slotted([slot='prefix']) {
|
||||
margin-inline-start: var(--wa-space-s) !important;
|
||||
}
|
||||
|
||||
::slotted([slot='suffix']) {
|
||||
margin-inline-end: var(--wa-space-s) !important;
|
||||
}
|
||||
|
||||
.button__label {
|
||||
padding: 0 var(--wa-space-m);
|
||||
}
|
||||
.button--has-label.button--medium .button__label {
|
||||
padding: 0 var(--wa-space-m);
|
||||
}
|
||||
|
||||
.button--large {
|
||||
::slotted([slot='prefix']) {
|
||||
margin-inline-start: var(--wa-space-m) !important;
|
||||
}
|
||||
.button--has-label.button--large .button__label {
|
||||
padding: 0 var(--wa-space-l);
|
||||
}
|
||||
|
||||
::slotted([slot='suffix']) {
|
||||
margin-inline-end: var(--wa-space-m) !important;
|
||||
}
|
||||
.button--has-prefix.button--small {
|
||||
padding-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.button__label {
|
||||
padding: 0 var(--wa-space-l);
|
||||
}
|
||||
.button--has-prefix.button--small .button__label {
|
||||
padding-inline-start: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.button--has-prefix.button--medium {
|
||||
padding-inline-start: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--has-prefix.button--medium .button__label {
|
||||
padding-inline-start: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--has-prefix.button--large {
|
||||
padding-inline-start: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.button--has-prefix.button--large .button__label {
|
||||
padding-inline-start: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--small,
|
||||
.button--caret.button--small {
|
||||
padding-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--small .button__label,
|
||||
.button--caret.button--small .button__label {
|
||||
padding-inline-end: var(--wa-space-xs);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--medium,
|
||||
.button--caret.button--medium {
|
||||
padding-inline-end: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--medium .button__label,
|
||||
.button--caret.button--medium .button__label {
|
||||
padding-inline-end: var(--wa-space-s);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--large,
|
||||
.button--caret.button--large {
|
||||
padding-inline-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
.button--has-suffix.button--large .button__label,
|
||||
.button--caret.button--large .button__label {
|
||||
padding-inline-end: var(--wa-space-m);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './card.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -32,16 +31,9 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --padding - The padding for each section in the card. Expects a single value.
|
||||
*/
|
||||
export default class WaCard extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
/** Renders the card with a header */
|
||||
@property({ attribute: 'with-header', type: Boolean }) withHeader = false;
|
||||
|
||||
/** Renders the card with an image */
|
||||
@property({ attribute: 'with-image', type: Boolean }) withImage = false;
|
||||
|
||||
/** Renders the card with a footer */
|
||||
@property({ attribute: 'with-footer', type: Boolean }) withFooter = false;
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image');
|
||||
|
||||
render() {
|
||||
return html`
|
||||
@@ -49,9 +41,9 @@ export default class WaCard extends WebAwesomeElement {
|
||||
part="base"
|
||||
class=${classMap({
|
||||
card: true,
|
||||
'card--has-footer': this.withFooter,
|
||||
'card--has-image': this.withImage,
|
||||
'card--has-header': this.withHeader
|
||||
'card--has-footer': this.hasSlotController.test('footer'),
|
||||
'card--has-image': this.hasSlotController.test('image'),
|
||||
'card--has-header': this.hasSlotController.test('header')
|
||||
})}
|
||||
>
|
||||
<slot name="image" part="image" class="card__image"></slot>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--background: var(--wa-color-surface-default);
|
||||
--border-color: var(--wa-color-surface-border);
|
||||
@@ -37,8 +40,6 @@ export default css`
|
||||
.card__image::slotted(img) {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
}
|
||||
|
||||
.card:not(.card--has-image) .card__image {
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('<wa-card>', () => {
|
||||
describe('when provided an element in the slot "header" to render a header', () => {
|
||||
before(async () => {
|
||||
el = await fixture<WaCard>(
|
||||
html`<wa-card with-header>
|
||||
html`<wa-card>
|
||||
<div slot="header">Header Title</div>
|
||||
This card has a header. You can put all sorts of things in it!
|
||||
</wa-card>`
|
||||
@@ -65,7 +65,7 @@ describe('<wa-card>', () => {
|
||||
describe('when provided an element in the slot "footer" to render a footer', () => {
|
||||
before(async () => {
|
||||
el = await fixture<WaCard>(
|
||||
html`<wa-card with-footer>
|
||||
html`<wa-card>
|
||||
This card has a footer. You can put all sorts of things in it!
|
||||
|
||||
<div slot="footer">Footer Content</div>
|
||||
@@ -102,7 +102,7 @@ describe('<wa-card>', () => {
|
||||
describe('when provided an element in the slot "image" to render a image', () => {
|
||||
before(async () => {
|
||||
el = await fixture<WaCard>(
|
||||
html`<wa-card with-image>
|
||||
html`<wa-card>
|
||||
<img
|
||||
slot="image"
|
||||
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { html } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './carousel-item.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -16,7 +15,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
*
|
||||
*/
|
||||
export default class WaCarouselItem extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--aspect-ratio: inherit;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import { prefersReducedMotion } from '../../internal/animate.js';
|
||||
import { range } from 'lit/directives/range.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './carousel.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -48,7 +47,7 @@ import type WaCarouselItem from '../carousel-item/carousel-item.component.js';
|
||||
* partially visible as a scroll hint.
|
||||
*/
|
||||
export default class SlCarousel extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
/** When set, allows the user to navigate the carousel in the same direction indefinitely. */
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--aspect-ratio: 16 / 9;
|
||||
--navigation-color: var(--wa-color-text-quiet);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../../../dist/webawesome.js';
|
||||
import '../../../dist/shoelace.js';
|
||||
import { clickOnElement, dragElement, moveMouseOnElement } from '../../internal/test.js';
|
||||
import { expect, fixture, html, nextFrame, oneEvent } from '@open-wc/testing';
|
||||
import { map } from 'lit/directives/map.js';
|
||||
@@ -11,8 +11,7 @@ describe('<wa-carousel>', () => {
|
||||
const sandbox = sinon.createSandbox();
|
||||
|
||||
afterEach(async () => {
|
||||
// eslint-disable-next-line
|
||||
await resetMouse().catch(() => {});
|
||||
await resetMouse();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -642,7 +641,7 @@ describe('<wa-carousel>', () => {
|
||||
</wa-carousel>
|
||||
`);
|
||||
sandbox.spy(el, 'goToSlide');
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('wa-carousel-item:nth-child(2)')!;
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(2)')!;
|
||||
|
||||
// Act
|
||||
el.next();
|
||||
@@ -669,7 +668,7 @@ describe('<wa-carousel>', () => {
|
||||
<wa-carousel-item>Node 3</wa-carousel-item>
|
||||
</wa-carousel>
|
||||
`);
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('wa-carousel-item:nth-child(1)')!;
|
||||
const expectedCarouselItem: HTMLElement = el.querySelector('sl-carousel-item:nth-child(1)')!;
|
||||
|
||||
el.goToSlide(1);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { live } from 'lit/directives/live.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './checkbox.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -52,7 +51,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-element.js
|
||||
|
||||
*/
|
||||
export default class WaCheckbox extends WebAwesomeElement implements WebAwesomeFormControl {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
private readonly formControlController = new FormControlController(this, {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
${formControlStyles}
|
||||
|
||||
:host {
|
||||
--background: var(--wa-form-controls-background);
|
||||
--background-checked: var(--wa-form-controls-activated-color);
|
||||
|
||||
@@ -63,7 +63,6 @@ describe('<wa-checkbox>', () => {
|
||||
el.addEventListener('wa-change', changeHandler);
|
||||
el.addEventListener('wa-input', inputHandler);
|
||||
el.click();
|
||||
await aTimeout(0);
|
||||
await el.updateComplete;
|
||||
|
||||
expect(changeHandler).to.have.been.calledOnce;
|
||||
@@ -94,10 +93,8 @@ describe('<wa-checkbox>', () => {
|
||||
el.addEventListener('wa-input', () => expect.fail('wa-input should not be emitted'));
|
||||
el.checked = true;
|
||||
await el.updateComplete;
|
||||
await aTimeout(0);
|
||||
el.checked = false;
|
||||
await el.updateComplete;
|
||||
await aTimeout(0);
|
||||
});
|
||||
|
||||
it('should hide the native input with the correct positioning to scroll correctly when contained in an overflow', async () => {
|
||||
|
||||
@@ -10,7 +10,6 @@ import { property, query, state } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './color-picker.styles.js';
|
||||
import WaButton from '../button/button.component.js';
|
||||
import WaButtonGroup from '../button-group/button-group.component.js';
|
||||
@@ -91,7 +90,7 @@ declare const EyeDropper: EyeDropperConstructor;
|
||||
* @cssproperty --swatch-size - The size of each predefined color swatch.
|
||||
*/
|
||||
export default class WaColorPicker extends WebAwesomeElement implements WebAwesomeFormControl {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
static dependencies = {
|
||||
'wa-button-group': WaButtonGroup,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--grid-width: 280px;
|
||||
--grid-height: 200px;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { getAnimation, setDefaultAnimation } from '../../utilities/animation-reg
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './copy-button.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WaTooltip from '../tooltip/tooltip.component.js';
|
||||
@@ -42,7 +41,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @animation copy.out - The animation to use when feedback icons animate out.
|
||||
*/
|
||||
export default class WaCopyButton extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = {
|
||||
'wa-icon': WaIcon,
|
||||
'wa-tooltip': WaTooltip
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--error-color: var(--wa-color-danger-spot);
|
||||
--success-color: var(--wa-color-success-spot);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './details.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -48,7 +47,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @animation details.hide - The animation to use when hiding details. You can use `height: auto` with this animation.
|
||||
*/
|
||||
export default class WaDetails extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
static dependencies = {
|
||||
'wa-icon': WaIcon
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--background: var(--wa-color-surface-default);
|
||||
--border-color: var(--wa-color-surface-border);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
@@ -8,7 +9,6 @@ import { lockBodyScrolling, unlockBodyScrolling } from '../../internal/scroll.js
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import styles from './dialog.styles.js';
|
||||
import WaIconButton from '../icon-button/icon-button.component.js';
|
||||
@@ -65,11 +65,12 @@ import type { CSSResultGroup } from 'lit';
|
||||
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
|
||||
*/
|
||||
export default class WaDialog extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = {
|
||||
'wa-icon-button': WaIconButton
|
||||
};
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private originalTrigger: HTMLElement | null;
|
||||
public modal = new Modal(this);
|
||||
@@ -86,16 +87,16 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
@property({ type: Boolean, reflect: true }) open = false;
|
||||
|
||||
/**
|
||||
* The dialog's label as displayed in the header. You should always include a relevant label, as it is required for
|
||||
* proper accessibility. If you need to display HTML, use the `label` slot instead.
|
||||
* The dialog's label as displayed in the header. You should always include a relevant label even when using
|
||||
* `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.
|
||||
*/
|
||||
@property({ reflect: true }) label = '';
|
||||
|
||||
/** Renders the dialog with a header. */
|
||||
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;
|
||||
|
||||
/** Renders the dialog with a footer. */
|
||||
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
|
||||
/**
|
||||
* Disables the header. This will also remove the default close button, so please ensure you provide an easy,
|
||||
* accessible way for users to dismiss the dialog.
|
||||
*/
|
||||
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
|
||||
|
||||
firstUpdated() {
|
||||
this.dialog.hidden = !this.open;
|
||||
@@ -270,8 +271,7 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
class=${classMap({
|
||||
dialog: true,
|
||||
'dialog--open': this.open,
|
||||
'dialog--with-header': this.withHeader,
|
||||
'dialog--with-footer': this.withFooter
|
||||
'dialog--has-footer': this.hasSlotController.test('footer')
|
||||
})}
|
||||
>
|
||||
<div part="overlay" class="dialog__overlay" @click=${() => this.requestClose('overlay')} tabindex="-1"></div>
|
||||
@@ -282,11 +282,11 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-hidden=${this.open ? 'false' : 'true'}
|
||||
aria-label=${ifDefined(this.withHeader ? undefined : this.label)}
|
||||
aria-labelledby=${ifDefined(this.withHeader ? 'title' : undefined)}
|
||||
aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
|
||||
aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
|
||||
tabindex="-1"
|
||||
>
|
||||
${this.withHeader
|
||||
${!this.noHeader
|
||||
? html`
|
||||
<header part="header" class="dialog__header">
|
||||
<h2 part="title" class="dialog__title" id="title">
|
||||
@@ -313,13 +313,9 @@ export default class WaDialog extends WebAwesomeElement {
|
||||
}
|
||||
<div part="body" class="dialog__body" tabindex="-1"><slot></slot></div>
|
||||
|
||||
${this.withFooter
|
||||
? html`
|
||||
<footer part="footer" class="dialog__footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
`
|
||||
: ''}
|
||||
<footer part="footer" class="dialog__footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--width: 31rem;
|
||||
--header-spacing: var(--wa-space-l);
|
||||
@@ -99,6 +102,10 @@ export default css`
|
||||
margin-inline-start: var(--wa-spacing-xs);
|
||||
}
|
||||
|
||||
.dialog:not(.dialog--has-footer) .dialog__footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dialog__overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
||||
@@ -6,10 +6,10 @@ import { sendKeys } from '@web/test-runner-commands';
|
||||
import sinon from 'sinon';
|
||||
import type WaDialog from './dialog.js';
|
||||
|
||||
describe('<wa-dialog with-header>', () => {
|
||||
describe('<wa-dialog>', () => {
|
||||
it('should be visible with the open attribute', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should not be visible without the open attribute', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should emit wa-show and wa-after-show when calling show()', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
const showHandler = sinon.spy();
|
||||
@@ -47,7 +47,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should emit wa-hide and wa-after-hide when calling hide()', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
const hideHandler = sinon.spy();
|
||||
@@ -67,7 +67,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should emit wa-show and wa-after-show when setting open = true', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
const showHandler = sinon.spy();
|
||||
@@ -87,7 +87,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should emit wa-hide and wa-after-hide when setting open = false', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const base = el.shadowRoot!.querySelector<HTMLElement>('[part~="base"]')!;
|
||||
const hideHandler = sinon.spy();
|
||||
@@ -107,7 +107,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
|
||||
it('should not close when wa-request-close is prevented', async () => {
|
||||
const el = await fixture<WaDialog>(html`
|
||||
<wa-dialog with-header open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
<wa-dialog open>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</wa-dialog>
|
||||
`);
|
||||
const overlay = el.shadowRoot!.querySelector<HTMLElement>('[part~="overlay"]')!;
|
||||
|
||||
@@ -120,7 +120,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
});
|
||||
|
||||
it('should allow initial focus to be set', async () => {
|
||||
const el = await fixture<WaDialog>(html` <wa-dialog with-header><input /></wa-dialog> `);
|
||||
const el = await fixture<WaDialog>(html` <wa-dialog><input /></wa-dialog> `);
|
||||
const input = el.querySelector('input')!;
|
||||
const initialFocusHandler = sinon.spy((event: Event) => {
|
||||
event.preventDefault();
|
||||
@@ -137,7 +137,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
});
|
||||
|
||||
it('should close when pressing Escape', async () => {
|
||||
const el = await fixture<WaDialog>(html` <wa-dialog with-header open></wa-dialog> `);
|
||||
const el = await fixture<WaDialog>(html` <wa-dialog open></wa-dialog> `);
|
||||
const hideHandler = sinon.spy();
|
||||
|
||||
el.addEventListener('wa-hide', hideHandler);
|
||||
@@ -162,7 +162,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
render() {
|
||||
return html`
|
||||
<h1>Dialog Example</h1>
|
||||
<wa-dialog with-header label="Dialog" class="dialog-overview">
|
||||
<wa-dialog label="Dialog" class="dialog-overview">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
<br />
|
||||
<label><input type="checkbox" />A</label>
|
||||
@@ -200,7 +200,7 @@ describe('<wa-dialog with-header>', () => {
|
||||
const dialog = container.shadowRoot?.querySelector('wa-dialog');
|
||||
|
||||
if (!dialog) {
|
||||
throw Error('Could not find <wa-dialog with-header> element.');
|
||||
throw Error('Could not find <wa-dialog> element.');
|
||||
}
|
||||
|
||||
const closeButton = dialog.shadowRoot?.querySelector('wa-icon-button');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './divider.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -16,7 +15,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --spacing - The spacing of the divider.
|
||||
*/
|
||||
export default class WaDivider extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
/** Draws the divider in a vertical orientation. */
|
||||
@property({ type: Boolean, reflect: true }) vertical = false;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--color: var(--wa-color-surface-border);
|
||||
--width: var(--wa-border-width-s);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { animateTo, stopAnimations } from '../../internal/animate.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { getAnimation, setDefaultAnimation } from '../../utilities/animation-registry.js';
|
||||
import { HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
@@ -9,7 +10,6 @@ import { property, query } from 'lit/decorators.js';
|
||||
import { uppercaseFirstLetter } from '../../internal/string.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import Modal from '../../internal/modal.js';
|
||||
import styles from './drawer.styles.js';
|
||||
import WaIconButton from '../icon-button/icon-button.component.js';
|
||||
@@ -74,9 +74,10 @@ import type { CSSResultGroup } from 'lit';
|
||||
* the third-party modal opens. Upon closing, call `modal.deactivateExternal()` to restore Shoelace's focus trapping.
|
||||
*/
|
||||
export default class WaDrawer extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon-button': WaIconButton };
|
||||
|
||||
private readonly hasSlotController = new HasSlotController(this, 'footer');
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private originalTrigger: HTMLElement | null;
|
||||
public modal = new Modal(this);
|
||||
@@ -93,27 +94,36 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
@property({ type: Boolean, reflect: true }) open = false;
|
||||
|
||||
/**
|
||||
* The drawer's label as displayed in the header. You should always include a relevant label, as it is required for
|
||||
* proper accessibility. If you need to display HTML, use the `label` slot instead.
|
||||
* The drawer's label as displayed in the header. You should always include a relevant label even when using
|
||||
* `no-header`, as it is required for proper accessibility. If you need to display HTML, use the `label` slot instead.
|
||||
*/
|
||||
@property({ reflect: true }) label = '';
|
||||
|
||||
/** The direction from which the drawer will open. */
|
||||
@property({ reflect: true }) placement: 'top' | 'end' | 'bottom' | 'start' = 'end';
|
||||
|
||||
/** Renders the drawer with a header. */
|
||||
@property({ attribute: 'with-header', type: Boolean, reflect: true }) withHeader = false;
|
||||
/**
|
||||
* By default, the drawer slides out of its containing block (usually the viewport). To make the drawer slide out of
|
||||
* its parent element, set this attribute and add `position: relative` to the parent.
|
||||
*/
|
||||
@property({ type: Boolean, reflect: true }) contained = false;
|
||||
|
||||
/** Renders the drawer with a footer. */
|
||||
@property({ attribute: 'with-footer', type: Boolean, reflect: true }) withFooter = false;
|
||||
/**
|
||||
* Removes the header. This will also remove the default close button, so please ensure you provide an easy,
|
||||
* accessible way for users to dismiss the drawer.
|
||||
*/
|
||||
@property({ attribute: 'no-header', type: Boolean, reflect: true }) noHeader = false;
|
||||
|
||||
firstUpdated() {
|
||||
this.drawer.hidden = !this.open;
|
||||
|
||||
if (this.open) {
|
||||
this.addOpenListeners();
|
||||
this.modal.activate();
|
||||
lockBodyScrolling(this);
|
||||
|
||||
if (!this.contained) {
|
||||
this.modal.activate();
|
||||
lockBodyScrolling(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,8 +151,10 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
private addOpenListeners() {
|
||||
if ('CloseWatcher' in window) {
|
||||
this.closeWatcher?.destroy();
|
||||
this.closeWatcher = new CloseWatcher();
|
||||
this.closeWatcher.onclose = () => this.requestClose('keyboard');
|
||||
if (!this.contained) {
|
||||
this.closeWatcher = new CloseWatcher();
|
||||
this.closeWatcher.onclose = () => this.requestClose('keyboard');
|
||||
}
|
||||
} else {
|
||||
document.addEventListener('keydown', this.handleDocumentKeyDown);
|
||||
this.closeWatcher?.destroy();
|
||||
@@ -154,6 +166,11 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
private handleDocumentKeyDown = (event: KeyboardEvent) => {
|
||||
// Contained drawers aren't modal and don't response to the escape key
|
||||
if (this.contained) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Escape' && this.modal.isActive() && this.open) {
|
||||
event.stopImmediatePropagation();
|
||||
this.requestClose('keyboard');
|
||||
@@ -167,8 +184,12 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
this.emit('wa-show');
|
||||
this.addOpenListeners();
|
||||
this.originalTrigger = document.activeElement as HTMLElement;
|
||||
this.modal.activate();
|
||||
lockBodyScrolling(this);
|
||||
|
||||
// Lock body scrolling only if the drawer isn't contained
|
||||
if (!this.contained) {
|
||||
this.modal.activate();
|
||||
lockBodyScrolling(this);
|
||||
}
|
||||
|
||||
// When the drawer is shown, Safari will attempt to set focus on whatever element has autofocus. This causes the
|
||||
// drawer's animation to jitter, so we'll temporarily remove the attribute, call `focus({ preventScroll: true })`
|
||||
@@ -217,8 +238,11 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
// Hide
|
||||
this.emit('wa-hide');
|
||||
this.removeOpenListeners();
|
||||
this.modal.deactivate();
|
||||
unlockBodyScrolling(this);
|
||||
|
||||
if (!this.contained) {
|
||||
this.modal.deactivate();
|
||||
unlockBodyScrolling(this);
|
||||
}
|
||||
|
||||
await Promise.all([stopAnimations(this.drawer), stopAnimations(this.overlay)]);
|
||||
const panelAnimation = getAnimation(this, `drawer.hide${uppercaseFirstLetter(this.placement)}`, {
|
||||
@@ -254,6 +278,19 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
}
|
||||
}
|
||||
|
||||
@watch('contained', { waitUntilFirstUpdate: true })
|
||||
handleNoModalChange() {
|
||||
if (this.open && !this.contained) {
|
||||
this.modal.activate();
|
||||
lockBodyScrolling(this);
|
||||
}
|
||||
|
||||
if (this.open && this.contained) {
|
||||
this.modal.deactivate();
|
||||
unlockBodyScrolling(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Shows the drawer. */
|
||||
async show() {
|
||||
if (this.open) {
|
||||
@@ -285,9 +322,10 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
'drawer--end': this.placement === 'end',
|
||||
'drawer--bottom': this.placement === 'bottom',
|
||||
'drawer--start': this.placement === 'start',
|
||||
'drawer--contained': this.contained,
|
||||
'drawer--fixed': !this.contained,
|
||||
'drawer--rtl': this.localize.dir() === 'rtl',
|
||||
'drawer--with-header': this.withHeader,
|
||||
'drawer--with-footer': this.withFooter
|
||||
'drawer--has-footer': this.hasSlotController.test('footer')
|
||||
})}
|
||||
>
|
||||
<div part="overlay" class="drawer__overlay" @click=${() => this.requestClose('overlay')} tabindex="-1"></div>
|
||||
@@ -298,11 +336,11 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-hidden=${this.open ? 'false' : 'true'}
|
||||
aria-label=${ifDefined(this.withHeader ? undefined : this.label)}
|
||||
aria-labelledby=${ifDefined(this.withHeader ? 'title' : undefined)}
|
||||
aria-label=${ifDefined(this.noHeader ? this.label : undefined)}
|
||||
aria-labelledby=${ifDefined(!this.noHeader ? 'title' : undefined)}
|
||||
tabindex="0"
|
||||
>
|
||||
${this.withHeader
|
||||
${!this.noHeader
|
||||
? html`
|
||||
<header part="header" class="drawer__header">
|
||||
<h2 part="title" class="drawer__title" id="title">
|
||||
@@ -328,13 +366,9 @@ export default class WaDrawer extends WebAwesomeElement {
|
||||
|
||||
<slot part="body" class="drawer__body"></slot>
|
||||
|
||||
${this.withFooter
|
||||
? html`
|
||||
<footer part="footer" class="drawer__footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
`
|
||||
: ''}
|
||||
<footer part="footer" class="drawer__footer">
|
||||
<slot name="footer"></slot>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--size: 25rem;
|
||||
--header-spacing: var(--wa-space-l);
|
||||
--body-spacing: var(--wa-space-l);
|
||||
--footer-spacing: var(--wa-space-l);
|
||||
--panel-background: var(--wa-color-surface-raised);
|
||||
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.drawer {
|
||||
position: fixed;
|
||||
z-index: var(--wa-z-index-drawer);
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
@@ -21,6 +23,16 @@ export default css`
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.drawer--contained {
|
||||
position: absolute;
|
||||
z-index: initial;
|
||||
}
|
||||
|
||||
.drawer--fixed {
|
||||
position: fixed;
|
||||
z-index: var(--wa-z-index-drawer);
|
||||
}
|
||||
|
||||
.drawer__panel {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
@@ -28,7 +40,7 @@ export default css`
|
||||
z-index: 2;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
background-color: var(--wa-color-surface-raised);
|
||||
background-color: var(--panel-background);
|
||||
box-shadow: var(--wa-shadow-level-3);
|
||||
overflow: auto;
|
||||
pointer-events: all;
|
||||
@@ -121,6 +133,10 @@ export default css`
|
||||
margin-inline-end: var(--wa-spacing-xs);
|
||||
}
|
||||
|
||||
.drawer:not(.drawer--has-footer) .drawer__footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.drawer__overlay {
|
||||
display: block;
|
||||
position: fixed;
|
||||
@@ -132,6 +148,10 @@ export default css`
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.drawer--contained .drawer__overlay {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.drawer__panel {
|
||||
border: solid 1px white;
|
||||
|
||||
@@ -7,7 +7,6 @@ import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { waitForEvent } from '../../internal/event.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './dropdown.styles.js';
|
||||
import WaPopup from '../popup/popup.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -41,7 +40,7 @@ import type WaMenu from '../menu/menu.js';
|
||||
* @animation dropdown.hide - The animation to use when hiding the dropdown.
|
||||
*/
|
||||
export default class WaDropdown extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-popup': WaPopup };
|
||||
|
||||
@query('.dropdown') popup: WaPopup;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { classMap } from 'lit/directives/class-map.js';
|
||||
import { html, literal } from 'lit/static-html.js';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './icon-button.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -22,7 +21,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class WaIconButton extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
@query('.icon-button') button: HTMLButtonElement | HTMLLinkElement;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
color: var(--wa-color-text-quiet);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { html } from 'lit';
|
||||
import { isTemplateResult } from 'lit/directive-helpers.js';
|
||||
import { property, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './icon.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
|
||||
@@ -34,7 +33,7 @@ interface IconSource {
|
||||
* @csspart use - The <use> element generated when using `spriteSheet: true`
|
||||
*/
|
||||
export default class WaIcon extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
private initialRender = false;
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
|
||||
@@ -6,7 +6,6 @@ import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './image-comparer.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -36,7 +35,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty --handle-size - The size of the compare handle.
|
||||
*/
|
||||
export default class WaImageComparer extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static scopedElement = { 'wa-icon': WaIcon };
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--divider-width: 2px;
|
||||
--handle-size: 2.5rem;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { html } from 'lit';
|
||||
import { property } from 'lit/decorators.js';
|
||||
import { requestInclude } from './request.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './include.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -17,7 +16,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @event {{ status: number }} wa-error - Emitted when the included file fails to load due to an error.
|
||||
*/
|
||||
export default class WaInclude extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
/**
|
||||
* The location of the HTML file to include. Be sure you trust the content you are including as it will be executed as
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import { live } from 'lit/directives/live.js';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
import styles from './input.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
@@ -58,7 +56,7 @@ import type { WebAwesomeFormControl } from '../../internal/webawesome-element.js
|
||||
* @cssproperty --box-shadow - The shadow effects around the edges of the input.
|
||||
*/
|
||||
export default class WaInput extends WebAwesomeElement implements WebAwesomeFormControl {
|
||||
static styles: CSSResultGroup = [componentStyles, formControlStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = { 'wa-icon': WaIcon };
|
||||
|
||||
private readonly formControlController = new FormControlController(this, {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import formControlStyles from '../../styles/form-control.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
${formControlStyles}
|
||||
|
||||
:host {
|
||||
--background: var(--wa-form-controls-background);
|
||||
--border-color: var(--wa-form-controls-resting-color);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { getTextContent } from '../../internal/slot.js';
|
||||
import { getTextContent, HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { LocalizeController } from '../../utilities/localize.js';
|
||||
import { property, query } from 'lit/decorators.js';
|
||||
import { SubmenuController } from './submenu-controller.js';
|
||||
import { watch } from '../../internal/watch.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './menu-item.styles.js';
|
||||
import WaIcon from '../icon/icon.component.js';
|
||||
import WaPopup from '../popup/popup.component.js';
|
||||
@@ -39,7 +38,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @cssproperty [--submenu-offset=-2px] - The distance submenus shift to overlap the parent menu.
|
||||
*/
|
||||
export default class WaMenuItem extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
static dependencies = {
|
||||
'wa-icon': WaIcon,
|
||||
'wa-popup': WaPopup,
|
||||
@@ -67,7 +66,8 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
private submenuController: SubmenuController = new SubmenuController(this, this.localize);
|
||||
private readonly hasSlotController = new HasSlotController(this, 'submenu');
|
||||
private submenuController: SubmenuController = new SubmenuController(this, this.hasSlotController, this.localize);
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
@@ -149,7 +149,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
}
|
||||
|
||||
isSubmenu() {
|
||||
return this.querySelector(`:scope > [slot="submenu"]`) !== null;
|
||||
return this.hasSlotController.test('submenu');
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -191,7 +191,7 @@ export default class WaMenuItem extends WebAwesomeElement {
|
||||
></wa-icon>
|
||||
</span>
|
||||
|
||||
${this.submenuController.renderSubmenu()} ${this.loading ? html`<wa-spinner part="spinner"></wa-spinner>` : ''}
|
||||
${this.submenuController.renderSubmenu()} ${this.loading ? html`<sl-spinner part="spinner"></sl-spinner>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
--submenu-offset: -2px;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createRef, ref, type Ref } from 'lit/directives/ref.js';
|
||||
import { type HasSlotController } from '../../internal/slot.js';
|
||||
import { html } from 'lit';
|
||||
import { type LocalizeController } from '../../utilities/localize.js';
|
||||
import type { ReactiveController, ReactiveControllerHost } from 'lit';
|
||||
@@ -13,20 +14,22 @@ export class SubmenuController implements ReactiveController {
|
||||
private isConnected = false;
|
||||
private isPopupConnected = false;
|
||||
private skidding = 0;
|
||||
private readonly hasSlotController: HasSlotController;
|
||||
private readonly localize: LocalizeController;
|
||||
private readonly submenuOpenDelay = 100;
|
||||
|
||||
constructor(host: ReactiveControllerHost & WaMenuItem, localize: LocalizeController) {
|
||||
constructor(
|
||||
host: ReactiveControllerHost & WaMenuItem,
|
||||
hasSlotController: HasSlotController,
|
||||
localize: LocalizeController
|
||||
) {
|
||||
(this.host = host).addController(this);
|
||||
this.hasSlotController = hasSlotController;
|
||||
this.localize = localize;
|
||||
}
|
||||
|
||||
private hasSubmenu() {
|
||||
return this.host.querySelector(`:scope > [slot="submenu"]`) !== null;
|
||||
}
|
||||
|
||||
hostConnected() {
|
||||
if (this.hasSubmenu() && !this.host.disabled) {
|
||||
if (this.hasSlotController.test('submenu') && !this.host.disabled) {
|
||||
this.addListeners();
|
||||
}
|
||||
}
|
||||
@@ -36,7 +39,7 @@ export class SubmenuController implements ReactiveController {
|
||||
}
|
||||
|
||||
hostUpdated() {
|
||||
if (this.hasSubmenu() && !this.host.disabled) {
|
||||
if (this.hasSlotController.test('submenu') && !this.host.disabled) {
|
||||
this.addListeners();
|
||||
this.updateSkidding();
|
||||
} else {
|
||||
@@ -90,7 +93,7 @@ export class SubmenuController implements ReactiveController {
|
||||
};
|
||||
|
||||
private handleMouseOver = () => {
|
||||
if (this.hasSubmenu()) {
|
||||
if (this.hasSlotController.test('submenu')) {
|
||||
this.enableSubmenu();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { html } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './menu-label.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
@@ -15,7 +14,7 @@ import type { CSSResultGroup } from 'lit';
|
||||
* @csspart base - The component's base wrapper.
|
||||
*/
|
||||
export default class WaMenuLabel extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
render() {
|
||||
return html` <slot part="base" class="menu-label"></slot> `;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { css } from 'lit';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
|
||||
export default css`
|
||||
${componentStyles}
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
font-size: var(--wa-font-size-s);
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { html } from 'lit';
|
||||
import { query } from 'lit/decorators.js';
|
||||
import componentStyles from '../../styles/component.styles.js';
|
||||
import styles from './menu.styles.js';
|
||||
import WebAwesomeElement from '../../internal/webawesome-element.js';
|
||||
import type { CSSResultGroup } from 'lit';
|
||||
import type WaMenuItem from '../menu-item/menu-item.component.js';
|
||||
|
||||
export interface MenuSelectEventDetail {
|
||||
item: WaMenuItem;
|
||||
}
|
||||
@@ -21,7 +19,7 @@ export interface MenuSelectEventDetail {
|
||||
* @event {{ item: WaMenuItem }} wa-select - Emitted when a menu item is selected.
|
||||
*/
|
||||
export default class WaMenu extends WebAwesomeElement {
|
||||
static styles: CSSResultGroup = [componentStyles, styles];
|
||||
static styles: CSSResultGroup = styles;
|
||||
|
||||
@query('slot') defaultSlot: HTMLSlotElement;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user