(() => {
const isDev = location.hostname === 'localhost';
const isNext = location.hostname === 'next.shoelace.style';
const customElements = fetch('/dist/custom-elements.json')
.then(res => res.json())
.catch(err => console.error(err));
function createPropsTable(props) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
Reflects |
Type |
Default |
${props
.map(prop => {
const hasAttribute = typeof prop.attribute !== 'undefined';
const isAttributeDifferent = prop.attribute !== prop.name;
let attributeInfo = '';
if (!hasAttribute) {
attributeInfo = `
(property only)`;
} else if (isAttributeDifferent) {
attributeInfo = `
${escapeHtml(prop.attribute)}
`;
}
return `
${escapeHtml(prop.name)}
${attributeInfo}
|
${escapeHtml(prop.description)}
|
${
prop.reflects ? '' : ''
} |
${prop.type?.text ? `${escapeHtml(prop.type?.text || '')} ` : '-'} |
${prop.default ? `${escapeHtml(prop.default)} ` : '-'} |
`;
})
.join('')}
`;
return table.outerHTML;
}
function createEventsTable(events) {
const table = document.createElement('table');
table.innerHTML = `
Name |
React Event |
Description |
Event Detail |
${events
.map(
event => `
${escapeHtml(event.name)} |
${escapeHtml(event.reactName)} |
${escapeHtml(event.description)} |
${event.type?.text ? `${escapeHtml(event.type?.text)}` : '-'} |
`
)
.join('')}
`;
return table.outerHTML;
}
function createMethodsTable(methods) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
Arguments |
${methods
.map(
method => `
${escapeHtml(method.name)} |
${escapeHtml(method.description)} |
${
method.parameters?.length
? `
${escapeHtml(
method.parameters.map(param => `${param.name}: ${param.type.text}`).join(', ')
)}
`
: '-'
}
|
`
)
.join('')}
`;
return table.outerHTML;
}
function createSlotsTable(slots) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
${slots
.map(
slot => `
${slot.name ? `${escapeHtml(slot.name)} ` : '(default)'} |
${escapeHtml(slot.description)} |
`
)
.join('')}
`;
return table.outerHTML;
}
function createCustomPropertiesTable(styles) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
Default |
${styles
.map(
style => `
${escapeHtml(style.name)} |
${escapeHtml(style.description)} |
${escapeHtml(style.default)} |
`
)
.join('')}
`;
return table.outerHTML;
}
function createPartsTable(parts) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
${parts
.map(
part => `
${escapeHtml(part.name)} |
${escapeHtml(part.description)} |
`
)
.join('')}
`;
return table.outerHTML;
}
function createAnimationsTable(animations) {
const table = document.createElement('table');
table.innerHTML = `
Name |
Description |
${animations
.map(
animation => `
${escapeHtml(animation.name)} |
${escapeHtml(animation.description)} |
`
)
.join('')}
`;
return table.outerHTML;
}
function createDependenciesList(targetComponent, allComponents) {
const ul = document.createElement('ul');
const dependencies = [];
// Recursively fetch sub-dependencies
function getDependencies(tag) {
const component = allComponents.find(c => c.tagName === tag);
if (!component || !Array.isArray(component.dependencies)) {
return;
}
component.dependencies?.forEach(dependentTag => {
if (!dependencies.includes(dependentTag)) {
dependencies.push(dependentTag);
}
getDependencies(dependentTag);
});
}
getDependencies(targetComponent);
dependencies.sort().forEach(tag => {
const li = document.createElement('li');
li.innerHTML = `<${tag}>
`;
ul.appendChild(li);
});
return ul.outerHTML;
}
function escapeHtml(html) {
if (typeof html === 'undefined') {
return '';
}
return html
.toString()
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/`(.*?)`/g, '$1
');
}
function getAllComponents(metadata) {
const allComponents = [];
metadata.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');
allComponents.push(declaration);
}
});
});
return allComponents;
}
function getComponent(metadata, tagName) {
return getAllComponents(metadata).find(component => component.tagName === tagName);
}
if (!window.$docsify) {
throw new Error('Docsify must be loaded before installing this plugin.');
}
window.$docsify.plugins.push(hook => {
hook.mounted(async () => {
const metadata = await customElements;
const target = document.querySelector('.app-name');
// Add version
const version = document.createElement('div');
version.classList.add('sidebar-version');
version.textContent = isDev ? 'Development' : isNext ? 'Next' : metadata.package.version;
target.appendChild(version);
// Store version for reuse
sessionStorage.setItem('sl-version', metadata.package.version);
// Add repo buttons
const buttons = document.createElement('div');
buttons.classList.add('sidebar-buttons');
buttons.innerHTML = `
Star
`;
target.appendChild(buttons);
});
hook.beforeEach(async (content, next) => {
const metadata = await customElements;
// Replace %VERSION% placeholders
content = content.replace(/%VERSION%/g, metadata.package.version);
// Handle [component-header] tags
content = content.replace(/\[component-header:([a-z-]+)\]/g, (match, tag) => {
const component = getComponent(metadata, tag);
let result = '';
if (!component) {
console.error(`Component not found in metadata: ${tag}`);
return next(content);
}
let badgeType = 'neutral';
if (component.status === 'stable') {
badgeType = 'primary';
}
if (component.status === 'experimental') {
badgeType = 'warning';
}
if (component.status === 'planned') {
badgeType = 'neutral';
}
if (component.status === 'deprecated') {
badgeType = 'danger';
}
result += `
`;
return result.replace(/^ +| +$/gm, '');
});
// Handle [component-metadata] tags
content = content.replace(/\[component-metadata:([a-z-]+)\]/g, (match, tag) => {
const component = getComponent(metadata, tag);
let result = '';
if (!component) {
console.error(`Component not found in metadata: ${tag}`);
return next(content);
}
// Remove members that are private or don't have a description
const members = component.members?.filter(member => member.description && member.privacy !== 'private');
const methods = members?.filter(prop => prop.kind === 'method' && prop.privacy !== 'private');
const props = members?.filter(prop => {
// Look for a corresponding attribute
const attribute = component.attributes?.find(attr => attr.fieldName === prop.name);
if (attribute) {
prop.attribute = attribute.name || attribute.fieldName;
}
return prop.kind === 'field' && prop.privacy !== 'private';
});
if (component.path) {
/* prettier-ignore */
result += `
## Importing
Script
Import
Bundler
React
\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a script tag:
\`\`\`html
\`\`\`
\n
To import this component from [the CDN](https://www.jsdelivr.com/package/npm/@shoelace-style/shoelace) using a JavaScript import:
\`\`\`js
import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${metadata.package.version}/${component.path}';
\`\`\`
\n
To import this component using [a bundler](/getting-started/installation#bundling):
\`\`\`js
import '@shoelace-style/shoelace/${component.path}';
\`\`\`
\n
To import this component as a [React component](/frameworks/react):
\`\`\`js
import { ${component.name} } from '@shoelace-style/shoelace/dist/react';
\`\`\`
`;
}
if (props?.length) {
result += `
## Properties
${createPropsTable(props)}
`;
}
if (component.events?.length) {
result += `
## Events
${createEventsTable(component.events)}
`;
}
if (methods?.length) {
result += `
## Methods
Methods can be called by obtaining a reference to the element and calling
el.methodName()
.
Methods can be called by obtaining a ref
to the element and calling
ref.current.methodName()
.
${createMethodsTable(methods)}
`;
}
if (component.slots?.length) {
result += `
## Slots
${createSlotsTable(component.slots)}
`;
}
if (component.cssProperties?.length) {
result += `
## CSS Custom Properties
${createCustomPropertiesTable(component.cssProperties)}
`;
}
if (component.cssParts?.length) {
result += `
## CSS Parts
${createPartsTable(component.cssParts)}
`;
}
if (component.animations?.length) {
result += `
## Animations
${createAnimationsTable(component.animations)}
Learn how to [customize animations](/getting-started/customizing#animations).
`;
}
if (component.dependencies?.length) {
result += `
## Dependencies
This component imports the following dependencies.
${createDependenciesList(component.tagName, getAllComponents(metadata))}
`;
}
// Strip whitespace so markdown doesn't process things as code blocks
return result.replace(/^ +| +$/gm, '');
});
next(content);
});
// Wrap tables so we can scroll them horizontally when needed
hook.doneEach(() => {
const content = document.querySelector('.content');
const tables = [...content.querySelectorAll('table')];
tables.forEach(table => {
table.outerHTML = `
${table.outerHTML}
`;
});
});
});
})();