Compare commits
209 Commits
konnorroge
...
react-exam
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a884a0c1f0 | ||
|
|
847ad26814 | ||
|
|
e943572d36 | ||
|
|
62f2b1c0fb | ||
|
|
c98945a885 | ||
|
|
e42ac799af | ||
|
|
dbbe580ef1 | ||
|
|
fb33dd7036 | ||
|
|
dc12e16d83 | ||
|
|
2e7096c66f | ||
|
|
700ccff3cd | ||
|
|
530673dcd1 | ||
|
|
5d5ac5576e | ||
|
|
39b5737a7f | ||
|
|
2157b209a2 | ||
|
|
4942eaedb3 | ||
|
|
ca6ebecfdc | ||
|
|
77e1708657 | ||
|
|
42b711b45a | ||
|
|
ca524a03f3 | ||
|
|
4ee831f18a | ||
|
|
8430d6076b | ||
|
|
9c17b0e16b | ||
|
|
1d1080f2e4 | ||
|
|
a715e0c1b6 | ||
|
|
a134b1a359 | ||
|
|
d0b673c99d | ||
|
|
55ba270b83 | ||
|
|
db94c609bd | ||
|
|
823bac0174 | ||
|
|
2423af3da2 | ||
|
|
81a3f29650 | ||
|
|
1b509dd44b | ||
|
|
a326671a21 | ||
|
|
3dcf3af5c5 | ||
|
|
7e72337ff3 | ||
|
|
71bd7c039d | ||
|
|
126c4f1789 | ||
|
|
efc640974a | ||
|
|
9385ebd893 | ||
|
|
0900b012af | ||
|
|
f94ef27a0b | ||
|
|
dccda93f6a | ||
|
|
a293e13540 | ||
|
|
2f586f4437 | ||
|
|
5f01216858 | ||
|
|
3154652a00 | ||
|
|
0ea9fd3f6f | ||
|
|
081ef02b72 | ||
|
|
f32175bf1e | ||
|
|
a1e9192db7 | ||
|
|
c69de376df | ||
|
|
db2e6df86d | ||
|
|
000bce99f8 | ||
|
|
3191b71235 | ||
|
|
6f62f96f11 | ||
|
|
eee439ffd2 | ||
|
|
fc5e3a4cd9 | ||
|
|
3119a75c19 | ||
|
|
c3bd625d46 | ||
|
|
92e343dd8b | ||
|
|
ec41613c23 | ||
|
|
e255006d8c | ||
|
|
3303a31255 | ||
|
|
497ccebb73 | ||
|
|
702ff2fd31 | ||
|
|
1dbef2449a | ||
|
|
50b817093d | ||
|
|
0995da09ef | ||
|
|
53b5484f2b | ||
|
|
f803bee9a9 | ||
|
|
a9e95ee493 | ||
|
|
a30326e4de | ||
|
|
99fe58c96f | ||
|
|
2111b3c6fb | ||
|
|
22e8ae39a2 | ||
|
|
2941db66e4 | ||
|
|
cd8d5d0eea | ||
|
|
56dcd409a7 | ||
|
|
88a9093137 | ||
|
|
f77acb4716 | ||
|
|
2f470f129f | ||
|
|
2d666a0c40 | ||
|
|
51509ddff5 | ||
|
|
d1ae120833 | ||
|
|
6dc847f0a4 | ||
|
|
1a6a45c624 | ||
|
|
79a312acce | ||
|
|
41582042fb | ||
|
|
14556a847f | ||
|
|
463de572fe | ||
|
|
91d3a98fa4 | ||
|
|
a18470e5f7 | ||
|
|
0856f20e2f | ||
|
|
d0b9c42d7c | ||
|
|
a1d614600d | ||
|
|
3cf3492e23 | ||
|
|
05193f55f8 | ||
|
|
b8a357c6cc | ||
|
|
07599df5f4 | ||
|
|
da46b4faf7 | ||
|
|
ec1305c671 | ||
|
|
bb47ca0ccd | ||
|
|
bf7117951a | ||
|
|
24851812c1 | ||
|
|
4be761379d | ||
|
|
25f3c9b508 | ||
|
|
120067a69d | ||
|
|
0cca54b731 | ||
|
|
258d1713e8 | ||
|
|
f3cbec4e96 | ||
|
|
f0d5a115fc | ||
|
|
8222334835 | ||
|
|
f3953bd5c9 | ||
|
|
1d05c315ac | ||
|
|
0abd0dec5f | ||
|
|
6d92f03fb2 | ||
|
|
8deaaf04e3 | ||
|
|
c4a084ee41 | ||
|
|
596697c5c5 | ||
|
|
28c8bf1815 | ||
|
|
296a2e6366 | ||
|
|
4b93aafccf | ||
|
|
c9f9a88637 | ||
|
|
9cb1073082 | ||
|
|
a13c02ab02 | ||
|
|
797d6d1eb1 | ||
|
|
4342242455 | ||
|
|
65c781c086 | ||
|
|
0b9a0bbb72 | ||
|
|
d828437316 | ||
|
|
45064505ce | ||
|
|
0cdc54aba7 | ||
|
|
13207d48df | ||
|
|
e8f7342cb6 | ||
|
|
23249dc4e9 | ||
|
|
1703d1452d | ||
|
|
d0dd3b59e5 | ||
|
|
e6a4eadf1c | ||
|
|
34bba3db19 | ||
|
|
1174200f20 | ||
|
|
1039d8e057 | ||
|
|
51518df076 | ||
|
|
398b4c47de | ||
|
|
598f598bd0 | ||
|
|
b28d0ad5c7 | ||
|
|
2f74995680 | ||
|
|
c7099f67fa | ||
|
|
39c4bcf3ea | ||
|
|
1e059c1fb9 | ||
|
|
bf5ed6c92a | ||
|
|
80ce2088a0 | ||
|
|
61c7329885 | ||
|
|
11137cf6e3 | ||
|
|
4444cf45fb | ||
|
|
659bfda3ba | ||
|
|
ab4813b38e | ||
|
|
456b37fa9b | ||
|
|
7c70242b10 | ||
|
|
757539f5d2 | ||
|
|
dd33acef98 | ||
|
|
46f73540ce | ||
|
|
163bb16a1f | ||
|
|
e8d1b408ac | ||
|
|
608df60a9c | ||
|
|
3fb8910ba1 | ||
|
|
88a1a829f9 | ||
|
|
4901d87ffb | ||
|
|
ef8d22d3bd | ||
|
|
2a25d2aa6f | ||
|
|
edbf152f2c | ||
|
|
8c3b283893 | ||
|
|
ac576eab7b | ||
|
|
d30cf62f50 | ||
|
|
6aa641bb33 | ||
|
|
90fdb93ae1 | ||
|
|
f7b520b251 | ||
|
|
1f83a4619f | ||
|
|
07e0863ce9 | ||
|
|
84782b3cbe | ||
|
|
ffdcb04a84 | ||
|
|
f1a65e7dc8 | ||
|
|
5ebc1b0cf0 | ||
|
|
f2c09ed486 | ||
|
|
eee77a13cf | ||
|
|
6fb7e82b7a | ||
|
|
3dd2fc7669 | ||
|
|
d0a348111a | ||
|
|
6663afdd10 | ||
|
|
878fe3b3de | ||
|
|
67b2888489 | ||
|
|
af8be3216f | ||
|
|
fb8430d9e5 | ||
|
|
c028a7a468 | ||
|
|
6423c766e0 | ||
|
|
6856362174 | ||
|
|
b927019e0c | ||
|
|
1f8640b5f2 | ||
|
|
a49b8af638 | ||
|
|
8e49d99d99 | ||
|
|
bc9eb79e5e | ||
|
|
c80e35ef58 | ||
|
|
e2ca0e4d5e | ||
|
|
e0d79a271a | ||
|
|
a77b9a92cd | ||
|
|
967ffd6e39 | ||
|
|
23dbadab91 | ||
|
|
6b3d2fe052 | ||
|
|
d566a829e6 |
@@ -42,17 +42,19 @@ module.exports = {
|
||||
rules: {
|
||||
'default-param-last': 'off',
|
||||
'@typescript-eslint/default-param-last': 'error',
|
||||
'no-console': 'warn',
|
||||
'no-empty-function': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'warn',
|
||||
'no-implied-eval': 'off',
|
||||
'@typescript-eslint/no-implied-eval': 'error',
|
||||
'no-invalid-this': 'off',
|
||||
'@typescript-eslint/no-invalid-this': 'error',
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'no-throw-literal': 'off',
|
||||
'@typescript-eslint/no-throw-literal': 'error',
|
||||
'no-unused-expressions': 'off',
|
||||
'lit-a11y/no-autofocus': 'off',
|
||||
'@typescript-eslint/no-implied-eval': 'error',
|
||||
'@typescript-eslint/no-invalid-this': 'error',
|
||||
'@typescript-eslint/no-throw-literal': 'error',
|
||||
'@typescript-eslint/no-shadow': 'error',
|
||||
'@typescript-eslint/prefer-regexp-exec': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'error',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
|
||||
1
.gitignore
vendored
@@ -4,7 +4,6 @@ _site
|
||||
package.json
|
||||
package-lock.json
|
||||
dist
|
||||
docs/assets/images/sprite.svg
|
||||
docs/public/pagefind
|
||||
node_modules
|
||||
src/react
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
*.hbs
|
||||
*.mdx
|
||||
*.md
|
||||
.cache
|
||||
.github
|
||||
cspell.json
|
||||
|
||||
@@ -141,6 +141,8 @@
|
||||
"scroller",
|
||||
"Segoe",
|
||||
"semibold",
|
||||
"Shortcode",
|
||||
"Shortcodes",
|
||||
"sitedir",
|
||||
"slotchange",
|
||||
"smartquotes",
|
||||
@@ -156,6 +158,7 @@
|
||||
"testid",
|
||||
"textareas",
|
||||
"textfield",
|
||||
"Themer",
|
||||
"tinycolor",
|
||||
"transitionend",
|
||||
"treeitem",
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
import * as path from 'path';
|
||||
import { customElementJetBrainsPlugin } from 'custom-element-jet-brains-integration';
|
||||
import { customElementVsCodePlugin } from 'custom-element-vs-code-integration';
|
||||
import { customElementVuejsPlugin } from 'custom-element-vuejs-integration';
|
||||
import { parse } from 'comment-parser';
|
||||
import { pascalCase } from 'pascal-case';
|
||||
import commandLineArgs from 'command-line-args';
|
||||
import fs from 'fs';
|
||||
|
||||
const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
||||
const { name, description, version, author, homepage, license } = packageData;
|
||||
|
||||
const { outdir } = commandLineArgs([
|
||||
{ name: 'litelement', type: String },
|
||||
{ name: 'analyze', defaultOption: true },
|
||||
{ name: 'outdir', type: String }
|
||||
]);
|
||||
|
||||
function noDash(string) {
|
||||
return string.replace(/^\s?-/, '').trim();
|
||||
}
|
||||
const outdir = 'dist';
|
||||
|
||||
function replace(string, terms) {
|
||||
terms.forEach(({ from, to }) => {
|
||||
@@ -29,8 +18,10 @@ function replace(string, terms) {
|
||||
}
|
||||
|
||||
export default {
|
||||
globs: ['src/components/**/*.component.ts'],
|
||||
globs: ['src/components/**/*.ts'],
|
||||
exclude: ['**/*.styles.ts', '**/*.test.ts'],
|
||||
litelement: true,
|
||||
outdir,
|
||||
plugins: [
|
||||
// Append package data
|
||||
{
|
||||
@@ -40,35 +31,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// Infer tag names because we no longer use @customElement decorators.
|
||||
{
|
||||
name: 'wa-infer-tag-names',
|
||||
analyzePhase({ ts, node, moduleDoc }) {
|
||||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
|
||||
const importPath = moduleDoc.path;
|
||||
|
||||
// This is kind of a best guess at components. "thing.component.ts"
|
||||
if (!importPath.endsWith('.component.ts')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tagNameWithoutPrefix = path.basename(importPath, '.component.ts');
|
||||
const tagName = 'wa-' + tagNameWithoutPrefix;
|
||||
|
||||
classDoc.tagNameWithoutPrefix = tagNameWithoutPrefix;
|
||||
classDoc.tagName = tagName;
|
||||
|
||||
// This used to be set to true by @customElement
|
||||
classDoc.customElement = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Parse custom jsDoc tags
|
||||
{
|
||||
name: 'wa-custom-tags',
|
||||
@@ -77,7 +39,7 @@ export default {
|
||||
case ts.SyntaxKind.ClassDeclaration: {
|
||||
const className = node.name.getText();
|
||||
const classDoc = moduleDoc?.declarations?.find(declaration => declaration.name === className);
|
||||
const customTags = ['animation', 'dependency', 'documentation', 'since', 'status', 'title'];
|
||||
const customTags = ['dependency', 'documentation', 'since', 'status', 'title'];
|
||||
let customComments = '/**';
|
||||
|
||||
node.jsDoc?.forEach(jsDoc => {
|
||||
@@ -96,17 +58,6 @@ export default {
|
||||
const parsed = parse(`${customComments}\n */`);
|
||||
parsed[0].tags?.forEach(t => {
|
||||
switch (t.tag) {
|
||||
// Animations
|
||||
case 'animation':
|
||||
if (!Array.isArray(classDoc['animations'])) {
|
||||
classDoc['animations'] = [];
|
||||
}
|
||||
classDoc['animations'].push({
|
||||
name: t.name,
|
||||
description: noDash(t.description)
|
||||
});
|
||||
break;
|
||||
|
||||
// Dependencies
|
||||
case 'dependency':
|
||||
if (!Array.isArray(classDoc['dependencies'])) {
|
||||
@@ -151,6 +102,7 @@ export default {
|
||||
|
||||
if (classDoc?.events) {
|
||||
classDoc.events.forEach(event => {
|
||||
if (!event.name) return;
|
||||
event.reactName = `on${pascalCase(event.name)}`;
|
||||
event.eventName = `${pascalCase(event.name)}Event`;
|
||||
});
|
||||
@@ -175,7 +127,7 @@ export default {
|
||||
//
|
||||
const terms = [
|
||||
{ from: /^src\//, to: '' }, // Strip the src/ prefix
|
||||
{ from: /\.component.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
|
||||
{ from: /\.(t|j)sx?$/, to: '.js' } // Convert .ts to .js
|
||||
];
|
||||
|
||||
mod.path = replace(mod.path, terms);
|
||||
@@ -219,12 +171,15 @@ export default {
|
||||
url: `https://shoelace.style/components/${tag.replace('wa-', '')}`
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
customElementVuejsPlugin({
|
||||
outdir: './dist/types/vue',
|
||||
fileName: 'index.d.ts',
|
||||
componentTypePath: (_, tag) => `../../components/${tag.replace('wa-', '')}/${tag.replace('wa-', '')}.component.js`
|
||||
})
|
||||
|
||||
//
|
||||
// TODO - figure out why this broke when events were updated
|
||||
//
|
||||
// customElementVuejsPlugin({
|
||||
// outdir: './dist/types/vue',
|
||||
// fileName: 'index.d.ts',
|
||||
// componentTypePath: (_, tag) => `../../components/${tag.replace('wa-', '')}/${tag.replace('wa-', '')}.js`
|
||||
// })
|
||||
]
|
||||
};
|
||||
126
docs/.eleventy.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import { parse } from 'path';
|
||||
import { markdown } from './_utils/markdown.js';
|
||||
import { anchorHeadingsPlugin } from './_utils/anchor-headings.js';
|
||||
import { codeExamplesPlugin } from './_utils/code-examples.js';
|
||||
import { copyCodePlugin } from './_utils/copy-code.js';
|
||||
import { currentLink } from './_utils/current-link.js';
|
||||
import { highlightCodePlugin } from './_utils/highlight-code.js';
|
||||
import { formatCodePlugin } from './_utils/format-code.js';
|
||||
import { replaceTextPlugin } from './_utils/replace-text.js';
|
||||
import { searchPlugin } from './_utils/search.js';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { outlinePlugin } from './_utils/outline.js';
|
||||
import { getComponents } from './_utils/manifest.js';
|
||||
import process from 'process';
|
||||
|
||||
const packageData = JSON.parse(await readFile('./package.json', 'utf-8'));
|
||||
const isDeveloping = process.argv.includes('--develop');
|
||||
|
||||
export default function (eleventyConfig) {
|
||||
// Add template data
|
||||
eleventyConfig.addGlobalData('package', packageData);
|
||||
|
||||
// Template filters - {{ content | filter }}
|
||||
eleventyConfig.addFilter('inlineMarkdown', content => markdown.renderInline(content || ''));
|
||||
eleventyConfig.addFilter('markdown', content => markdown.render(content || ''));
|
||||
eleventyConfig.addFilter('stripExtension', string => parse(string).name);
|
||||
eleventyConfig.addFilter('stripPrefix', content => content.replace(/^wa-/, ''));
|
||||
eleventyConfig.addFilter('trimPipes', content => {
|
||||
// Trims whitespace and pipes from the start and end of a string. Useful for CEM types, which can be pipe-delimited.
|
||||
// With Prettier 3, this means a leading pipe will exist be present when the line wraps.
|
||||
return typeof content === 'string' ? content.replace(/^(\s|\|)/g, '').replace(/(\s|\|)$/g, '') : content;
|
||||
});
|
||||
|
||||
// Shortcodes - {% shortCode arg1, arg2 %}
|
||||
eleventyConfig.addShortcode('cdnUrl', location => {
|
||||
return (
|
||||
`https://cdn.jsdelivr.net/npm/@shoelace-style/webawesome@${packageData.version}/` + location.replace(/^\//, '')
|
||||
);
|
||||
});
|
||||
|
||||
// Helpers
|
||||
eleventyConfig.addNunjucksGlobal('getComponent', tagName => {
|
||||
const component = getComponents().find(c => c.tagName === tagName);
|
||||
|
||||
if (!component) {
|
||||
throw new Error(
|
||||
`Unable to find "<${tagName}>". Make sure the file name is the same as the tag name (without prefix).`
|
||||
);
|
||||
}
|
||||
return component;
|
||||
});
|
||||
|
||||
// Use our own markdown instance
|
||||
eleventyConfig.setLibrary('md', markdown);
|
||||
|
||||
// Add anchors to headings
|
||||
eleventyConfig.addPlugin(anchorHeadingsPlugin({ container: '#content' }));
|
||||
|
||||
// Add an outline to the page
|
||||
eleventyConfig.addPlugin(
|
||||
outlinePlugin({
|
||||
container: '#content',
|
||||
target: '.outline-links',
|
||||
selector: 'h2, h3',
|
||||
ifEmpty: doc => {
|
||||
doc.querySelector('#outline')?.remove();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Add current link classes
|
||||
eleventyConfig.addPlugin(currentLink());
|
||||
|
||||
// Add code examples for `<code class="example">` blocks
|
||||
eleventyConfig.addPlugin(codeExamplesPlugin());
|
||||
|
||||
// Highlight code blocks with Prism
|
||||
eleventyConfig.addPlugin(highlightCodePlugin());
|
||||
|
||||
// Add copy code buttons to code blocks
|
||||
eleventyConfig.addPlugin(copyCodePlugin());
|
||||
|
||||
// Various text replacements
|
||||
eleventyConfig.addPlugin(
|
||||
replaceTextPlugin([
|
||||
// Replace [issue:1234] with a link to the issue on GitHub
|
||||
{
|
||||
replace: /\[pr:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/pull/$1">#$1</a>'
|
||||
},
|
||||
// Replace [pr:1234] with a link to the pull request on GitHub
|
||||
{
|
||||
replace: /\[issue:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/issues/$1">#$1</a>'
|
||||
},
|
||||
// Replace [discuss:1234] with a link to the discussion on GitHub
|
||||
{
|
||||
replace: /\[discuss:([0-9]+)\]/gs,
|
||||
replaceWith: '<a href="https://github.com/shoelace-style/webawesome/discussions/$1">#$1</a>'
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
// Build the search index
|
||||
eleventyConfig.addPlugin(
|
||||
searchPlugin({
|
||||
filename: '',
|
||||
selectorsToIgnore: ['code.example'],
|
||||
getContent: doc => doc.querySelector('#content')?.textContent ?? ''
|
||||
})
|
||||
);
|
||||
|
||||
// Production-only plugins
|
||||
if (!isDeveloping) {
|
||||
// Run Prettier on each file (prod only because it can be slow)
|
||||
eleventyConfig.addPlugin(formatCodePlugin());
|
||||
}
|
||||
|
||||
return {
|
||||
dir: {
|
||||
includes: '_includes',
|
||||
layouts: '_layouts'
|
||||
},
|
||||
templateFormats: ['njk', 'md']
|
||||
};
|
||||
}
|
||||
4
docs/.vscode/extensions.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
docs/.vscode/launch.json
vendored
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
docs/404.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
title: Not Found
|
||||
description: Sorry, I couldn't find that.
|
||||
layout: page.njk
|
||||
permalink: 404.html
|
||||
noindex: true
|
||||
---
|
||||
|
||||
<p class="subtitle">{{ description }}</p>
|
||||
|
||||
<p>Have you tried searching?</p>
|
||||
98
docs/_includes/base.njk
Normal file
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
61
docs/_includes/search.njk
Normal file
@@ -0,0 +1,61 @@
|
||||
<wa-dialog id="site-search" no-header>
|
||||
<div id="site-search-container">
|
||||
{# Header #}
|
||||
<header>
|
||||
<div id="site-search-input-wrapper">
|
||||
<wa-input
|
||||
id="site-search-input"
|
||||
type="search"
|
||||
filled
|
||||
size="large"
|
||||
clearable
|
||||
placeholder="Search"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
enterkeyhint="go"
|
||||
spellcheck="false"
|
||||
maxlength="100"
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="true"
|
||||
aria-controls="site-search-listbox"
|
||||
aria-haspopup="listbox"
|
||||
aria-activedescendant
|
||||
>
|
||||
<wa-icon slot="prefix" name="search"></wa-icon>
|
||||
</wa-input>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{# Body #}
|
||||
<div id="site-search-body">
|
||||
<ul
|
||||
id="site-search-listbox"
|
||||
role="listbox"
|
||||
aria-label="Search results"
|
||||
></ul>
|
||||
<div id="site-search-empty">
|
||||
<wa-icon name="web-awesome" family="brands"></wa-icon>
|
||||
No results
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Footer #}
|
||||
<footer>
|
||||
<small>
|
||||
<kbd aria-label="Up"><wa-icon name="arrow-up" family="micro"></wa-icon></kbd>
|
||||
<kbd aria-label="Down"><wa-icon name="arrow-down" family="micro"></wa-icon></kbd>
|
||||
Navigate
|
||||
</small>
|
||||
|
||||
<small>
|
||||
<kbd>Enter</kbd>
|
||||
Select
|
||||
</small>
|
||||
|
||||
<small><kbd>Esc</kbd> Close</small>
|
||||
</footer>
|
||||
</div>
|
||||
</wa-dialog>
|
||||
234
docs/_includes/sidebar.njk
Normal file
@@ -0,0 +1,234 @@
|
||||
{# Getting started #}
|
||||
<h2>Getting Started</h2>
|
||||
<ul>
|
||||
<li><a href="/docs/installation">Installation</a></li>
|
||||
<li><a href="/docs/usage">Usage</a></li>
|
||||
<li><a href="/docs/themes">Themes</a></li>
|
||||
<li><a href="/docs/customizing">Customizing</a></li>
|
||||
<li><a href="/docs/localization">Localization</a></li>
|
||||
</ul>
|
||||
|
||||
{# Experimental #}
|
||||
<h2>Experimental</h2>
|
||||
<ul>
|
||||
<li><a href="/docs/experimental/themer">Theme Builder</a></li>
|
||||
<li><a href="/docs/experimental/style-guide">Style Guide</a></li>
|
||||
<li><a href="/docs/experimental/form-validation">Form Validation Styles</a></li>
|
||||
<li><a href="/docs/experimental/sandbox">Sandbox</a></li>
|
||||
<li style="margin-top: .5rem;"><wa-switch id="theme-toggle">Dark mode</wa-switch></li>
|
||||
<script type="module">
|
||||
// Temporary dark toggle
|
||||
const toggle = document.getElementById('theme-toggle');
|
||||
toggle.checked = document.documentElement.classList.contains('wa-theme-default-dark');
|
||||
|
||||
toggle.addEventListener('wa-change', () => {
|
||||
document.documentElement.classList.toggle('wa-theme-default-dark');
|
||||
localStorage.setItem('theme', toggle.checked ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
</ul>
|
||||
|
||||
|
||||
{# Resources #}
|
||||
<h2>Resources</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/shoelace-style/shoelace/discussions">Help & Support</a></li>
|
||||
<li><a href="/docs/resources/community">Community</a></li>
|
||||
<li><a href="/docs/resources/accessibility">Accessibility</a></li>
|
||||
<li><a href="/docs/resources/contributing">Contributing</a></li>
|
||||
<li><a href="/docs/resources/changelog">Changelog</a></li>
|
||||
</ul>
|
||||
|
||||
{# Components #}
|
||||
<h2>Components</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/animated-image">Animated Image</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/animation">Animation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/avatar">Avatar</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/badge">Badge</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/breadcrumb">Breadcrumb</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/breadcrumb-item">Breadcrumb Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/button">Button</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/button-group">Button Group</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/callout">Callout</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/card">Card</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/carousel">Carousel</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/carousel-item">Carousel Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/checkbox">Checkbox</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/color-picker">Color Picker</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/copy-button">Copy Button</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/details">Details</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/dialog">Dialog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/divider">Divider</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/drawer">Drawer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/dropdown">Dropdown</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/format-bytes">Format Bytes</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/format-date">Format Date</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/format-number">Format Number</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/icon">Icon</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/icon-button">Icon Button</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/image-comparer">Image Comparer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/include">Include</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/input">Input</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/menu">Menu</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/menu-item">Menu Item</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/menu-label">Menu Label</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/mutation-observer">Mutation Observer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/option">Option</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/page">Page</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/popup">Popup</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/progress-bar">Progress Bar</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/progress-ring">Progress Ring</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/qr-code">QR Code</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/radio-group">Radio Group</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/radio">Radio</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/radio-button">Radio Button</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/range">Range</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/rating">Rating</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/relative-time">Relative Time</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/resize-observer">Resize Observer</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/select">Select</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/skeleton">Skeleton</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/spinner">Spinner</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/split-panel">Split Panel</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/switch">Switch</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/tab-group">Tab Group</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/tab">Tab</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/tab-panel">Tab Panel</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/tag">Tag</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/textarea">Textarea</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/tooltip">Tooltip</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/tree">Tree</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/docs/components/tree-item">Tree Item</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/components/visually-hidden">Visually Hidden</a>
|
||||
</li>
|
||||
</ul>
|
||||
298
docs/_layouts/component.njk
Normal file
@@ -0,0 +1,298 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
{% set component = getComponent('wa-' + page.fileSlug) %}
|
||||
{% set description = component.summary %}
|
||||
|
||||
{% extends '../_includes/base.njk' %}
|
||||
|
||||
{# Component header #}
|
||||
{% block beforeContent %}
|
||||
<h1 class="title">{{ title }}</h1>
|
||||
<div class="component-info">
|
||||
<code class="component-tag"><{{ component.tagName }}></code>
|
||||
<wa-badge variant="neutral">Since {{ component.since }}</wa-badge>
|
||||
<wa-badge
|
||||
{% if component.status == 'stable' %}variant="brand"{% endif %}
|
||||
{% if component.status == 'experimental' %}variant="warning"{% endif %}
|
||||
>
|
||||
{{ component.status }}
|
||||
</wa-badge>
|
||||
</div>
|
||||
<p class="component-summary">
|
||||
{{ component.summary }}
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{# Content #}
|
||||
{% block content %}
|
||||
{{ content | safe }}
|
||||
{% endblock %}
|
||||
|
||||
{# Component API #}
|
||||
{% block afterContent %}
|
||||
{# Slots #}
|
||||
{% if component.slots.length %}
|
||||
<h2>Slots</h2>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for slot in component.slots %}
|
||||
<tr>
|
||||
<td class="table-name">
|
||||
{% if slot.name %}
|
||||
<code>{{ slot.name }}</code>
|
||||
{% else %}
|
||||
(default)
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="table-description">{{ slot.description | inlineMarkdown | safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Properties #}
|
||||
{% if component.properties.length %}
|
||||
<h2>Properties</h2>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-attribute">Attribute</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-reflects">Reflects</th>
|
||||
<th class="table-type">Type</th>
|
||||
<th class="table-default">Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for prop in component.properties %}
|
||||
<tr>
|
||||
<td class="table-name">
|
||||
<code>{{ prop.name }}</code>
|
||||
</td>
|
||||
<td class="table-attribute">
|
||||
{% if prop.attribute %}
|
||||
<code>{{ prop.attribute }}</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="table-description">
|
||||
{{ prop.description | inlineMarkdown | safe }}
|
||||
</td>
|
||||
<td class="table-checkmark">
|
||||
{% if prop.reflects %}
|
||||
<wa-icon name="check"></wa-icon>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="table-type">
|
||||
{% if prop.type.text %}
|
||||
<code>{{ prop.type.text | trimPipes | inlineMarkdown | safe }}</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="table-default">
|
||||
{% if prop.default %}
|
||||
<code>{{ prop.default | inlineMarkdown | safe }}</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Methods #}
|
||||
{% if component.methods.length %}
|
||||
<h2>Methods</h2>
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-arguments">Arguments</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for method in component.methods %}
|
||||
<tr>
|
||||
<td class="table-name"><code>{{ method.name }}()</code></td>
|
||||
<td class="table-description">{{ method.description | inlineMarkdown | safe }}</td>
|
||||
<td class="table-arguments">
|
||||
{% if method.parameters.length %}
|
||||
<code>
|
||||
{% for param in method.parameters %}
|
||||
{{ param.name }}: {{ param.type.text | trimPipes }}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# States #}
|
||||
{% if component.states.length %}
|
||||
<h2>States</h2>
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-selector">CSS selector</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for state in component.states %}
|
||||
<tr>
|
||||
<td class="table-name"><code>{{ state.name }}</code></td>
|
||||
<td class="table-description">{{ state.description | inlineMarkdown | safe }}</td>
|
||||
<td class="table-selector"><code>[data-state-{{ state.name }}]</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Events #}
|
||||
{% if component.events.length %}
|
||||
<h2>Events</h2>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for event in component.events %}
|
||||
{% if event.name %}
|
||||
<tr>
|
||||
<td class="table-name"><code>{{ event.name }}</code></td>
|
||||
<td class="table-description">{{ event.description | inlineMarkdown | safe }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Custom Properties #}
|
||||
{% if component.cssProperties.length %}
|
||||
<h2>CSS custom properties</h2>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th>Default</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssProperty in component.cssProperties %}
|
||||
<tr>
|
||||
<td class="table-name"><code>{{ cssProperty.name }}</code></td>
|
||||
<td class="table-description">{{ cssProperty.description | inlineMarkdown | safe }}</td>
|
||||
<td class="table-default">
|
||||
{% if cssProperty.default %}
|
||||
<code>{{ cssProperty.default }}</code>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# CSS Parts #}
|
||||
{% if component.cssParts.length %}
|
||||
<h2>CSS parts</h2>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="component-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="table-name">Name</th>
|
||||
<th class="table-description">Description</th>
|
||||
<th class="table-selector">CSS selector</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for cssPart in component.cssParts %}
|
||||
<tr>
|
||||
<td class="table-name"><code>{{ cssPart.name }}</code></td>
|
||||
<td class="table-description">{{ cssPart.description | inlineMarkdown | safe }}</td>
|
||||
<td class="table-selector"><code>::part({{ cssPart.name }})</code></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Dependencies #}
|
||||
{% if component.dependencies.length %}
|
||||
<h2>Dependencies</h2>
|
||||
<p>
|
||||
This component automatically imports the following elements. Subdependencies, if any exist, will also be included in this list.
|
||||
</p>
|
||||
|
||||
<ul class="dependency-list">
|
||||
{% for dependency in component.dependencies %}
|
||||
<li><a href="/docs/components/{{ dependency | stripPrefix }}"><code><{{ dependency }}></code></a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{# Importing #}
|
||||
<h2>Importing</h2>
|
||||
<p>
|
||||
The <a href="/docs/#autoloading">autoloader</a> is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
|
||||
</p>
|
||||
|
||||
<wa-tab-group label="How would you like to import this component?">
|
||||
<wa-tab slot="nav" panel="cdn">CDN</wa-tab>
|
||||
<wa-tab slot="nav" panel="npm">npm</wa-tab>
|
||||
<wa-tab slot="nav" panel="react">React</wa-tab>
|
||||
<wa-tab-panel name="cdn">
|
||||
<p>
|
||||
To manually import this component from the CDN, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '{% cdnUrl component.path %}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="npm">
|
||||
<p>
|
||||
To manually import this component from npm, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@shoelace-style/webawesome/{{ component.path }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
<wa-tab-panel name="react">
|
||||
<p>
|
||||
To manually import this component from React, use the following code.
|
||||
</p>
|
||||
<pre><code class="language-js">import '@shoelace-style/webawesome/react/{{ component.tagName | stripPrefix }}';</code></pre>
|
||||
</wa-tab-panel>
|
||||
</wa-tab-group>
|
||||
{% endblock %}
|
||||
5
docs/_layouts/docs.njk
Normal file
@@ -0,0 +1,5 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = true %}
|
||||
{% set section = 'docs' %}
|
||||
|
||||
{% extends "../_includes/base.njk" %}
|
||||
4
docs/_layouts/page.njk
Normal file
@@ -0,0 +1,4 @@
|
||||
{% set hasSidebar = true %}
|
||||
{% set hasOutline = false %}
|
||||
|
||||
{% extends "../_includes/base.njk" %}
|
||||
78
docs/_utils/anchor-headings.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import slugify from 'slugify';
|
||||
|
||||
function createId(text) {
|
||||
let slug = slugify(String(text), {
|
||||
remove: /[^\w|\s]/g,
|
||||
lower: true
|
||||
});
|
||||
|
||||
// ids must start with a letter
|
||||
if (!/^[a-z]/i.test(slug)) {
|
||||
slug = `wa_${slug}`;
|
||||
}
|
||||
|
||||
return slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add anchors to headings to content.
|
||||
*/
|
||||
export function anchorHeadingsPlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
headingSelector: 'h2, h3, h4, h5, h6',
|
||||
anchorLabel: 'Jump to heading',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('anchor-headings', content => {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for headings
|
||||
container.querySelectorAll(options.headingSelector).forEach(heading => {
|
||||
const hasAnchor = heading.querySelector('a');
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
// Create a clone of the heading so we can remove [data-no-anchor] elements from the text content
|
||||
clone.querySelectorAll('[data-no-anchor]').forEach(el => el.remove());
|
||||
|
||||
const slug = createId(clone.textContent ?? '') ?? uuid().slice(-12);
|
||||
let id = slug;
|
||||
let suffix = 0;
|
||||
|
||||
// Make sure the slug is unique in the document
|
||||
while (doc.getElementById(id) !== null) {
|
||||
id = `${slug}-${++suffix}`;
|
||||
}
|
||||
|
||||
if (hasAnchor || !id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the anchor
|
||||
const anchor = parse(`
|
||||
<a href="#${encodeURIComponent(id)}">
|
||||
<span class="wa-visually-hidden"></span>
|
||||
<span aria-hidden="true">#</span>
|
||||
</a>
|
||||
`);
|
||||
anchor.querySelector('.wa-visually-hidden').textContent = options.anchorLabel;
|
||||
|
||||
// Update the heading
|
||||
heading.setAttribute('id', id);
|
||||
heading.classList.add('anchor-heading');
|
||||
heading.appendChild(anchor);
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
82
docs/_utils/code-examples.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to turn `<code class="example">` blocks into live examples.
|
||||
*/
|
||||
export function codeExamplesPlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('code-examples', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for external links
|
||||
container.querySelectorAll('code.example').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
const hasButtons = !code.classList.contains('no-buttons');
|
||||
const isOpen = code.classList.contains('open') || !hasButtons;
|
||||
const noEdit = code.classList.contains('no-edit');
|
||||
const id = `code-example-${uuid().slice(-12)}`;
|
||||
let preview = pre.textContent;
|
||||
|
||||
// Run preview scripts as modules to prevent collisions
|
||||
const root = parse(preview, { blockTextElements: { script: true } });
|
||||
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
|
||||
preview = root.toString();
|
||||
|
||||
const codeExample = parse(`
|
||||
<div class="code-example ${isOpen ? 'open' : ''}">
|
||||
<div class="code-example-preview">
|
||||
${preview}
|
||||
</div>
|
||||
<div class="code-example-source" id="${id}">
|
||||
${pre.outerHTML}
|
||||
</div>
|
||||
${
|
||||
hasButtons
|
||||
? `
|
||||
<div class="code-example-buttons">
|
||||
<button
|
||||
class="code-example-toggle"
|
||||
type="button"
|
||||
aria-expanded="${isOpen ? 'true' : 'false'}"
|
||||
aria-controls="${id}"
|
||||
>
|
||||
Code
|
||||
<wa-icon name="chevron-down"></wa-icon>
|
||||
</button>
|
||||
|
||||
${
|
||||
noEdit
|
||||
? ''
|
||||
: `
|
||||
<button class="code-example-pen" type="button">
|
||||
<wa-icon name="pen-to-square"></wa-icon>
|
||||
Edit
|
||||
</button>
|
||||
`
|
||||
}
|
||||
|
||||
`
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
pre.replaceWith(codeExample);
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
32
docs/_utils/copy-code.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add copy buttons to code blocks.
|
||||
*/
|
||||
export function copyCodePlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('copy-code', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for code blocks
|
||||
container.querySelectorAll('pre > code').forEach(code => {
|
||||
const pre = code.closest('pre');
|
||||
|
||||
// Add a copy button (we set the copy data at runtime to reduce page bloat)
|
||||
pre.innerHTML = `<wa-copy-button class="copy-button" hoist></wa-copy-button>` + pre.innerHTML;
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
53
docs/_utils/current-link.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
function normalize(pathname) {
|
||||
pathname = pathname.trim();
|
||||
|
||||
// Must start with a slash
|
||||
if (!pathname.startsWith('/')) {
|
||||
pathname = `/${pathname}`;
|
||||
}
|
||||
|
||||
// Must not end in a slash
|
||||
if (pathname.endsWith('/')) {
|
||||
pathname = pathname.slice(0, -1);
|
||||
}
|
||||
|
||||
// Convert /index.html to /
|
||||
if (pathname.endsWith('/index.html')) {
|
||||
pathname = pathname.slice(0, -10);
|
||||
}
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to decorate current links with a custom class.
|
||||
*/
|
||||
export function currentLink(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
className: 'current',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('current-link', function (content) {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Compare the href attribute to 11ty's page URL
|
||||
container.querySelectorAll('a[href]').forEach(a => {
|
||||
if (normalize(a.getAttribute('href')) === normalize(this.page.url)) {
|
||||
a.classList.add(options.className);
|
||||
}
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
32
docs/_utils/format-code.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { format } from 'prettier';
|
||||
import defaultOptions from '../../prettier.config.js';
|
||||
|
||||
/**
|
||||
* Formats a string of code using Prettier.
|
||||
*
|
||||
* @param {string} code - The code to format.
|
||||
* @param {*} options - Prettier options. Defaults are taken from the project's root config. See this page for more
|
||||
* info: https://prettier.io/docs/en/options.html
|
||||
*/
|
||||
export async function formatCode(string, options) {
|
||||
return await format(string, {
|
||||
...defaultOptions,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to format page HTML using Prettier.
|
||||
*/
|
||||
export function formatCodePlugin(options = {}) {
|
||||
options = {
|
||||
parser: 'html',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('format-code', content => {
|
||||
return formatCode(content, options);
|
||||
});
|
||||
};
|
||||
}
|
||||
72
docs/_utils/highlight-code.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/* eslint sort-imports-es6-autofix/sort-imports-es6: 0 */
|
||||
import { parse } from 'node-html-parser';
|
||||
import Prism from 'prismjs';
|
||||
import 'prismjs/plugins/custom-class/prism-custom-class.js';
|
||||
import PrismLoader from 'prismjs/components/index.js';
|
||||
|
||||
PrismLoader('diff');
|
||||
PrismLoader.silent = true;
|
||||
Prism.plugins.customClass.prefix('code-');
|
||||
|
||||
/**
|
||||
* Highlights a string of code using the specified language.
|
||||
*
|
||||
* @param {string} code - The code to highlight.
|
||||
* @param {string} language - The language the code is written in. For available languages, refer to this page:
|
||||
* https://prismjs.com/#supported-languages
|
||||
*/
|
||||
export function highlightCode(code, language = 'plain') {
|
||||
const alias = language.replace(/^diff-/, '');
|
||||
const isDiff = /^diff-/i.test(language);
|
||||
|
||||
if (!Prism.languages[alias]) {
|
||||
PrismLoader(alias);
|
||||
if (!Prism.languages[alias]) {
|
||||
throw new Error(`Unsupported language for code highlighting: "${language}"`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDiff) {
|
||||
Prism.languages[language] = Prism.languages.diff;
|
||||
}
|
||||
|
||||
return Prism.highlight(code, Prism.languages[language], language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to highlight code blocks with the `language-*` attribute using Prism.js. Unlike most plugins, this
|
||||
* works on the entire document — not just markdown content.
|
||||
*/
|
||||
export function highlightCodePlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('highlight-code', content => {
|
||||
const doc = parse(content, { blockTextElements: { code: true } });
|
||||
const container = doc.querySelector(options.container);
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
// Look for <code class="language-*"> and highlight each one
|
||||
container.querySelectorAll('code[class*="language-"]').forEach(code => {
|
||||
const langClass = [...code.classList.values()].find(val => val.startsWith('language-'));
|
||||
const lang = langClass ? langClass.replace(/^language-/, '') : 'plain';
|
||||
|
||||
try {
|
||||
code.innerHTML = highlightCode(code.textContent ?? '', lang);
|
||||
} catch (err) {
|
||||
if (!options.ignoreMissingLangs) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
71
docs/_utils/manifest.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const manifest = JSON.parse(readFileSync(resolve(__dirname, '../../dist/custom-elements.json'), 'utf-8'));
|
||||
/**
|
||||
* @returns Fetches components from custom-elements.json and returns them in more sane format.
|
||||
*/
|
||||
export function getComponents() {
|
||||
const components = [];
|
||||
|
||||
manifest.modules?.forEach(module => {
|
||||
module.declarations?.forEach(declaration => {
|
||||
if (declaration.customElement) {
|
||||
// Generate the dist path based on the src path and attach it to the component
|
||||
declaration.path = module.path.replace(/^src\//, 'dist/').replace(/\.ts$/, '.js');
|
||||
|
||||
// Remove private members and those that lack a description
|
||||
const members = declaration.members?.filter(member => member.description && member.privacy !== 'private');
|
||||
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
|
||||
const properties = members?.filter(prop => {
|
||||
// Look for a corresponding attribute
|
||||
const attribute = declaration.attributes?.find(attr => attr.fieldName === prop.name);
|
||||
if (attribute) {
|
||||
prop.attribute = attribute.name || attribute.fieldName;
|
||||
}
|
||||
|
||||
return prop.kind === 'field' && prop.privacy !== 'private';
|
||||
});
|
||||
components.push({
|
||||
...declaration,
|
||||
methods,
|
||||
properties
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graphs
|
||||
components.forEach(component => {
|
||||
const dependencies = [];
|
||||
|
||||
// Recursively fetch sub-dependencies
|
||||
function getDependencies(tag) {
|
||||
const cmp = components.find(c => c.tagName === tag);
|
||||
if (!cmp || !Array.isArray(component.dependencies)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmp.dependencies?.forEach(dependentTag => {
|
||||
if (!dependencies.includes(dependentTag)) {
|
||||
dependencies.push(dependentTag);
|
||||
}
|
||||
getDependencies(dependentTag);
|
||||
});
|
||||
}
|
||||
|
||||
getDependencies(component.tagName);
|
||||
|
||||
component.dependencies = dependencies.sort();
|
||||
});
|
||||
|
||||
// Sort by name
|
||||
return components.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
63
docs/_utils/markdown.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import markdownItAttrs from 'markdown-it-attrs';
|
||||
import markdownItContainer from 'markdown-it-container';
|
||||
import markdownItIns from 'markdown-it-ins';
|
||||
import markdownItKbd from 'markdown-it-kbd';
|
||||
import markdownItMark from 'markdown-it-mark';
|
||||
|
||||
/**
|
||||
* A custom Markdown It instance with added features.
|
||||
*/
|
||||
export const markdown = MarkdownIt({
|
||||
html: true,
|
||||
xhtmlOut: false,
|
||||
breaks: false,
|
||||
langPrefix: 'language-',
|
||||
linkify: false,
|
||||
typographer: false
|
||||
});
|
||||
|
||||
markdown.use(markdownItIns);
|
||||
markdown.use(markdownItKbd);
|
||||
markdown.use(markdownItMark);
|
||||
|
||||
['info', 'warning'].forEach(type => {
|
||||
markdown.use(markdownItContainer, type, {
|
||||
render: function (tokens, idx) {
|
||||
const variant = type === 'warning' ? 'warning' : 'info';
|
||||
const icon = type === 'warning' ? 'triangle-exclamation' : 'circle-info';
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `
|
||||
<div class="callout callout-${variant}">
|
||||
<wa-icon class="callout-icon" name="${icon}"></wa-icon>
|
||||
<div class="callout-content">
|
||||
`;
|
||||
}
|
||||
return '</div></div>\n';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
markdown.use(markdownItContainer, 'aside', {
|
||||
render: function (tokens, idx) {
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<aside>`;
|
||||
}
|
||||
return '</aside>\n';
|
||||
}
|
||||
});
|
||||
|
||||
markdown.use(markdownItContainer, 'details', {
|
||||
validate: params => params.trim().match(/^details\s+(.*)$/),
|
||||
render: (tokens, idx) => {
|
||||
const m = tokens[idx].info.trim().match(/^details\s+(.*)$/);
|
||||
if (tokens[idx].nesting === 1) {
|
||||
return `<details>\n<summary><span>${markdown.utils.escapeHtml(m[1])}</span></summary>\n`;
|
||||
}
|
||||
return '</details>\n';
|
||||
}
|
||||
});
|
||||
|
||||
markdown.use(markdownItAttrs, {
|
||||
allowedAttributes: ['id', 'class', 'data']
|
||||
});
|
||||
69
docs/_utils/outline.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import { parse } from 'node-html-parser';
|
||||
|
||||
/**
|
||||
* Eleventy plugin to add an outline (table of contents) to the page. Headings must have an id, otherwise they won't be
|
||||
* included in the outline. An unordered list containing links will be appended to the target element.
|
||||
*
|
||||
* If no headings are found for the outline, the `ifEmpty()` function will be called with a `node-html-parser` object as
|
||||
* the first argument. This can be used to toggle classes or remove elements when the outline is empty.
|
||||
*
|
||||
* See the `node-html-parser` docs for more details: https://www.npmjs.com/package/node-html-parser
|
||||
*/
|
||||
export function outlinePlugin(options = {}) {
|
||||
options = {
|
||||
container: 'body',
|
||||
target: '.outline',
|
||||
selector: 'h2,h3',
|
||||
ifEmpty: () => null,
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('outline', content => {
|
||||
const doc = parse(content);
|
||||
const container = doc.querySelector(options.container);
|
||||
const ul = parse('<ul></ul>');
|
||||
let numLinks = 0;
|
||||
|
||||
if (!container) {
|
||||
return content;
|
||||
}
|
||||
|
||||
container.querySelectorAll(options.selector).forEach(heading => {
|
||||
const id = heading.getAttribute('id');
|
||||
const level = heading.tagName.slice(1);
|
||||
const clone = parse(heading.outerHTML);
|
||||
|
||||
if (heading.closest('[data-no-outline]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a clone of the heading so we can remove links and [data-no-outline] elements from the text content
|
||||
clone.querySelectorAll('a').forEach(a => a.remove());
|
||||
clone.querySelectorAll('[data-no-outline]').forEach(el => el.remove());
|
||||
|
||||
// Generate the link
|
||||
const li = parse(`<li data-level="${level}"><a></a></li>`);
|
||||
const a = li.querySelector('a');
|
||||
a.setAttribute('href', `#${encodeURIComponent(id)}`);
|
||||
a.textContent = clone.textContent.trim().replace(/#$/, '');
|
||||
|
||||
// Add it to the list
|
||||
ul.firstChild.appendChild(li);
|
||||
numLinks++;
|
||||
});
|
||||
|
||||
if (numLinks > 0) {
|
||||
// Append the list to all matching targets
|
||||
doc.querySelectorAll(options.target).forEach(target => {
|
||||
target.appendChild(parse(ul.outerHTML));
|
||||
});
|
||||
} else {
|
||||
// Remove if empty
|
||||
options.ifEmpty(doc);
|
||||
}
|
||||
|
||||
return doc.toString();
|
||||
});
|
||||
};
|
||||
}
|
||||
19
docs/_utils/replace-text.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Eleventy plugin to replace arbitrary text in the page's HTML.
|
||||
*
|
||||
* @param replacement - The terms to replace and what to replace them with. This must be an object (or an array of
|
||||
* objects) containing a `replace` key that's a string or RegExp and a `replaceWith` key that's a string.
|
||||
*/
|
||||
export function replaceTextPlugin(replacements = []) {
|
||||
replacements = Array.isArray(replacements) ? replacements : [replacements];
|
||||
|
||||
return function (eleventyConfig) {
|
||||
eleventyConfig.addTransform('replace-text', function (content) {
|
||||
replacements.forEach(replacement => {
|
||||
content = content.replace(replacement.replace, replacement.replaceWith);
|
||||
});
|
||||
|
||||
return content;
|
||||
});
|
||||
};
|
||||
}
|
||||
76
docs/_utils/search.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable no-invalid-this */
|
||||
import { dirname, join } from 'path';
|
||||
import { mkdir, writeFile } from 'fs/promises';
|
||||
import { parse } from 'node-html-parser';
|
||||
import lunr from 'lunr';
|
||||
|
||||
function collapseWhitespace(string) {
|
||||
return string.replace(/\s+/g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Eleventy plugin to build a Lunr search index.
|
||||
*/
|
||||
export function searchPlugin(options = {}) {
|
||||
options = {
|
||||
filename: '',
|
||||
selectorsToIgnore: [],
|
||||
getTitle: doc => doc.querySelector('title')?.textContent ?? '',
|
||||
getDescription: doc => doc.querySelector('meta[name="description"]')?.getAttribute('content') ?? '',
|
||||
getHeadings: doc => [...doc.querySelectorAll('h1, h2, h3, h4, h5, h6')].map(heading => heading.textContent ?? ''),
|
||||
getContent: doc => doc.querySelector('body')?.textContent ?? '',
|
||||
...options
|
||||
};
|
||||
|
||||
return function (eleventyConfig) {
|
||||
const pagesToIndex = [];
|
||||
|
||||
eleventyConfig.addTransform('search', function (content) {
|
||||
const doc = parse(content, {
|
||||
blockTextElements: {
|
||||
script: false,
|
||||
noscript: false,
|
||||
style: false,
|
||||
pre: false,
|
||||
code: false
|
||||
}
|
||||
});
|
||||
|
||||
// Remove content that shouldn't be searchable to reduce the index size
|
||||
options.selectorsToIgnore.forEach(selector => {
|
||||
doc.querySelectorAll(selector).forEach(el => el.remove());
|
||||
});
|
||||
|
||||
pagesToIndex.push({
|
||||
title: collapseWhitespace(options.getTitle(doc)),
|
||||
description: collapseWhitespace(options.getDescription(doc)),
|
||||
headings: options.getHeadings(doc).map(collapseWhitespace),
|
||||
content: collapseWhitespace(options.getContent(doc)),
|
||||
url: this.page.url === '/' ? '/' : this.page.url.replace(/\/$/, '')
|
||||
});
|
||||
|
||||
return content;
|
||||
});
|
||||
|
||||
eleventyConfig.on('eleventy.after', ({ dir }) => {
|
||||
const outputFilename = join(dir.output, 'search.json');
|
||||
const map = [];
|
||||
const searchIndex = lunr(async function () {
|
||||
let index = 0;
|
||||
|
||||
this.ref('id');
|
||||
this.field('t', { boost: 20 });
|
||||
this.field('h', { boost: 10 });
|
||||
this.field('c');
|
||||
|
||||
for (const page of pagesToIndex) {
|
||||
this.add({ id: index, t: page.title, h: page.headings, c: page.content });
|
||||
map[index] = { title: page.title, description: page.description, url: page.url };
|
||||
index++;
|
||||
}
|
||||
await mkdir(dirname(outputFilename), { recursive: true });
|
||||
await writeFile(outputFilename, JSON.stringify({ searchIndex, map }), 'utf-8');
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 175 KiB |
|
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 356 B |
|
Before Width: | Height: | Size: 786 B After Width: | Height: | Size: 786 B |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 770 B |
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 330 B |
|
Before Width: | Height: | Size: 754 B After Width: | Height: | Size: 754 B |
|
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 812 B |
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 322 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
|
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
|
Before Width: | Height: | Size: 289 B After Width: | Height: | Size: 289 B |
|
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 267 B |
|
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 460 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 333 B |
|
Before Width: | Height: | Size: 305 B After Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 383 B After Width: | Height: | Size: 383 B |
|
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 272 B |
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 1003 B After Width: | Height: | Size: 1003 B |
|
Before Width: | Height: | Size: 619 B After Width: | Height: | Size: 619 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 351 B After Width: | Height: | Size: 351 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
|
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 387 B |
|
Before Width: | Height: | Size: 973 B After Width: | Height: | Size: 973 B |
|
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 441 B |
|
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
|
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 334 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 402 B |
|
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 423 B After Width: | Height: | Size: 423 B |
|
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 789 B |
|
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 360 B |
|
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 267 B |
|
Before Width: | Height: | Size: 998 B After Width: | Height: | Size: 998 B |
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 399 B |
|
Before Width: | Height: | Size: 574 B After Width: | Height: | Size: 574 B |
|
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 454 B |
|
Before Width: | Height: | Size: 919 B After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 375 B |
|
Before Width: | Height: | Size: 427 B After Width: | Height: | Size: 427 B |
|
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 443 B After Width: | Height: | Size: 443 B |
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 492 B |
|
Before Width: | Height: | Size: 303 B After Width: | Height: | Size: 303 B |
|
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 685 B |
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 1020 B After Width: | Height: | Size: 1020 B |
|
Before Width: | Height: | Size: 910 B After Width: | Height: | Size: 910 B |
|
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 412 B |
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 356 B |
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |