/* global Prism */ (() => { const reactVersion = '17.0.2'; let flavor = getFlavor(); let count = 1; // Sync flavor UI on page load setFlavor(getFlavor()); if (!window.$docsify) { throw new Error('Docsify must be loaded before installing this plugin.'); } function convertModuleLinks(html) { const version = sessionStorage.getItem('sl-version'); html = html .replace(/@shoelace-style\/shoelace/g, `https://cdn.skypack.dev/@shoelace-style/shoelace@${version}`) .replace(/from 'react'/g, `from 'https://cdn.skypack.dev/react@${reactVersion}'`) .replace(/from "react"/g, `from "https://cdn.skypack.dev/react@${reactVersion}"`); return html; } function getAdjacentExample(name, pre) { let currentPre = pre.nextElementSibling; while (currentPre?.tagName.toLowerCase() === 'pre') { if (currentPre?.getAttribute('data-lang').split(' ').includes(name)) { return currentPre; } currentPre = currentPre.nextElementSibling; } return null; } function runScript(script) { const newScript = document.createElement('script'); if (script.type === 'module') { newScript.type = 'module'; newScript.textContent = script.innerHTML; } else { newScript.appendChild(document.createTextNode(`(() => { ${script.innerHTML} })();`)); } script.parentNode.replaceChild(newScript, script); } function getFlavor() { return localStorage.getItem('flavor') || 'html'; } function setFlavor(newFlavor) { flavor = ['html', 'react'].includes(newFlavor) ? newFlavor : 'html'; localStorage.setItem('flavor', flavor); // Set the flavor class on the body document.body.classList.toggle('flavor-html', flavor === 'html'); document.body.classList.toggle('flavor-react', flavor === 'react'); } window.$docsify.plugins.push(hook => { // Convert code blocks to previews hook.afterEach((html, next) => { const domParser = new DOMParser(); const doc = domParser.parseFromString(html, 'text/html'); const htmlButton = ` `; const reactButton = ` `; const codePenButton = ` `; [...doc.querySelectorAll('code[class^="lang-"]')].forEach(code => { if (code.classList.contains('preview')) { const isExpanded = code.classList.contains('expanded'); const pre = code.closest('pre'); const sourceGroupId = `code-block-source-group-${count}`; const toggleId = `code-block-toggle-${count}`; const reactPre = getAdjacentExample('react', pre); const hasReact = reactPre !== null; pre.setAttribute('data-lang', pre.getAttribute('data-lang').replace(/ preview$/, '')); pre.setAttribute('aria-labelledby', toggleId); const codeBlock = `
${code.textContent}
${pre.outerHTML}
${ hasReact ? `
${reactPre.outerHTML}
` : '' }
${hasReact ? ` ${htmlButton} ${reactButton} ` : ''} ${!code.classList.contains('no-codepen') ? codePenButton : ''}
`; pre.replaceWith(domParser.parseFromString(codeBlock, 'text/html').body); reactPre?.remove(); count++; } }); // Force the highlighter to run again so JSX fields get highlighted properly requestAnimationFrame(() => Prism.highlightAll()); next(doc.body.innerHTML); }); // After the page is done loading, force scripts in previews to execute hook.doneEach(() => { [...document.querySelectorAll('.code-block__preview script')].map(script => runScript(script)); }); // Horizontal resizing hook.doneEach(() => { [...document.querySelectorAll('.code-block__preview')].forEach(preview => { const resizer = preview.querySelector('.code-block__resizer'); let startX; let startWidth; function dragStart(event) { startX = event.changedTouches ? event.changedTouches[0].pageX : event.clientX; startWidth = parseInt(document.defaultView.getComputedStyle(preview).width, 10); preview.classList.add('code-block__preview--dragging'); event.preventDefault(); document.documentElement.addEventListener('mousemove', dragMove, false); document.documentElement.addEventListener('touchmove', dragMove, false); document.documentElement.addEventListener('mouseup', dragStop, false); document.documentElement.addEventListener('touchend', dragStop, false); } function dragMove(event) { setWidth(startWidth + (event.changedTouches ? event.changedTouches[0].pageX : event.pageX) - startX); } function dragStop() { preview.classList.remove('code-block__preview--dragging'); document.documentElement.removeEventListener('mousemove', dragMove, false); document.documentElement.removeEventListener('touchmove', dragMove, false); document.documentElement.removeEventListener('mouseup', dragStop, false); document.documentElement.removeEventListener('touchend', dragStop, false); } function setWidth(width) { preview.style.width = `${width}px`; } resizer.addEventListener('mousedown', dragStart); resizer.addEventListener('touchstart', dragStart); }, false); }); }); // Toggle source mode document.addEventListener('click', event => { const button = event.target.closest('button'); if (button?.classList.contains('code-block__button--html')) { setFlavor('html'); } else if (button?.classList.contains('code-block__button--react')) { setFlavor('react'); } else { return; } // Update flavor buttons [...document.querySelectorAll('.code-block')].forEach(codeBlock => { codeBlock .querySelector('.code-block__button--html') ?.classList.toggle('code-block__button--selected', flavor === 'html'); codeBlock .querySelector('.code-block__button--react') ?.classList.toggle('code-block__button--selected', flavor === 'react'); }); }); // Expand and collapse code blocks document.addEventListener('click', event => { const toggle = event.target.closest('.code-block__toggle'); if (toggle) { const codeBlock = event.target.closest('.code-block'); codeBlock.classList.toggle('code-block--expanded'); event.target.setAttribute('aria-expanded', codeBlock.classList.contains('code-block--expanded')); } }); // Show pulse when copying document.addEventListener('click', event => { const button = event.target.closest('.docsify-copy-code-button'); if (button) { button.classList.remove('copied'); requestAnimationFrame(() => { button.addEventListener('animationend', () => button.classList.remove('copied'), { once: true }); button.classList.add('copied'); }); } }); // Open in CodePen document.addEventListener('click', event => { const button = event.target.closest('button'); const version = sessionStorage.getItem('sl-version'); if (button?.classList.contains('code-block__button--codepen')) { const codeBlock = button.closest('.code-block'); const htmlExample = codeBlock.querySelector('.code-block__source--html > pre > code')?.textContent; const reactExample = codeBlock.querySelector('.code-block__source--react > pre > code')?.textContent; const isReact = flavor === 'react' && typeof reactExample === 'string'; const theme = localStorage.getItem('theme'); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const isDark = theme === 'dark' || (theme === 'auto' && prefersDark); const editors = isReact ? '0010' : '1000'; let htmlTemplate = ''; let jsTemplate = ''; let cssTemplate = ''; const form = document.createElement('form'); form.action = 'https://codepen.io/pen/define'; form.method = 'POST'; form.target = '_blank'; // HTML templates if (!isReact) { htmlTemplate = `\n` + `\n${htmlExample}`; jsTemplate = ''; } // React templates if (isReact) { htmlTemplate = '
'; jsTemplate = `import React from 'https://cdn.skypack.dev/react@${reactVersion}';\n` + `import ReactDOM from 'https://cdn.skypack.dev/react-dom@${reactVersion}';\n` + `import { setBasePath } from 'https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/utilities/base-path';\n` + `\n` + `// Set the base path for Shoelace assets\n` + `setBasePath('https://cdn.skypack.dev/@shoelace-style/shoelace@${version}/dist/')\n` + `\n${convertModuleLinks(reactExample)}\n` + `\n` + `ReactDOM.render(, document.getElementById('root'));`; } // CSS templates cssTemplate = `@import 'https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@${version}/dist/themes/${ isDark ? 'dark' : 'light' }.css';\n` + '\n' + 'body {\n' + ' font: 16px sans-serif;\n' + ' background-color: var(--sl-color-neutral-0);\n' + ' color: var(--sl-color-neutral-900);\n' + ' padding: 1rem;\n' + '}'; // Docs: https://blog.codepen.io/documentation/prefill/ const data = { title: '', description: '', tags: ['shoelace', 'web components'], editors, head: ``, html_classes: `sl-theme-${isDark ? 'dark' : 'light'}`, css_external: ``, js_external: ``, js_module: true, js_pre_processor: isReact ? 'babel' : 'none', html: htmlTemplate, css: cssTemplate, js: jsTemplate }; const input = document.createElement('input'); input.type = 'hidden'; input.name = 'data'; input.value = JSON.stringify(data); form.append(input); document.body.append(form); form.submit(); form.remove(); } }); })();