Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Version: 0.1: added line-height 1.5 for readability and spacing between labels a
--p-sm: 0.5rem;
--p-md: 1rem;
--sm: 600px;
--max-content-width: 75rem;
}

body {
Expand All @@ -26,6 +27,19 @@ body {
color 0.5s;
}

main {
max-width: var(--max-content-width);
margin: 0 auto;
width: 100%;
padding: var(--p-md);
}

main.full-width {
max-width: none;
margin: 0;
padding: 0 !important;
}

footer {
bottom: 0;
width: 100%;
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<nav data-component="nav"></nav>
-->
<nav data-component="nav" data-header-bar="true" data-burger-px="600"></nav>
<main data-component="router" data-use-hash style="padding: 1rem; width: 100%">
<main data-component="router" data-use-hash>
<!--components in /pages that will be fetched by router when user clicks on a -->
</main>
</div>
Expand Down
58 changes: 58 additions & 0 deletions js/components/code-viewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// File: js/components/code-viewer.js
// Component for displaying code snippets in multiple languages with a selector

export default (hostComponent) => {
const pres = Array.from(hostComponent.querySelectorAll('pre[data-lang]'));
if (pres.length === 0) return;

const selector = document.createElement('select');
pres.forEach((pre, index) => {
const { lang } = pre.dataset;
const code = pre.querySelector('code');
if (code) {
code.classList.add(`language-${lang}`);
}
const option = document.createElement('option');
option.value = lang;
option.textContent = lang;
selector.appendChild(option);
pre.style.display = index === 0 ? '' : 'none';
});

selector.addEventListener('change', (event) => {
const lang = event.target.value;
pres.forEach((pre) => {
pre.style.display = pre.dataset.lang === lang ? '' : 'none';
});
});

hostComponent.insertBefore(selector, hostComponent.firstChild);

const ensureHighlight = () =>
new Promise((resolve) => {
if (window.hljs) {
resolve();
return;
}
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href =
'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css';
document.head.appendChild(link);
const script = document.createElement('script');
script.src =
'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js';
script.onload = resolve;
document.head.appendChild(script);
});

ensureHighlight().then(() => {
pres.forEach((pre) => {
const code = pre.querySelector('code');
if (code && window.hljs?.highlightElement) {
window.hljs.highlightElement(code);
}
});
});
};

42 changes: 42 additions & 0 deletions js/components/code-viewer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import codeViewer from './code-viewer.js';

beforeEach(() => {
// Mock hljs to avoid loading external scripts during tests
vi.stubGlobal('hljs', {
highlightElement: vi.fn(),
});
});

describe('code-viewer component', () => {
it('renders selector and shows only first snippet', () => {
const host = document.createElement('div');
host.innerHTML = `
<pre data-lang="js"><code>console.log('hi')</code></pre>
<pre data-lang="python"><code>print('hi')</code></pre>
`;
codeViewer(host);
const select = host.querySelector('select');
expect(select).toBeTruthy();
const pres = host.querySelectorAll('pre[data-lang]');
expect(pres[0].style.display).toBe('');
expect(pres[1].style.display).toBe('none');
});

it('switches visible snippet on selector change', () => {
const host = document.createElement('div');
host.innerHTML = `
<pre data-lang="js"><code>console.log('hi')</code></pre>
<pre data-lang="python"><code>print('hi')</code></pre>
`;
codeViewer(host);
const select = host.querySelector('select');
select.value = 'python';
select.dispatchEvent(new Event('change'));
const jsPre = host.querySelector('pre[data-lang="js"]');
const pyPre = host.querySelector('pre[data-lang="python"]');
expect(jsPre.style.display).toBe('none');
expect(pyPre.style.display).toBe('');
});
});

13 changes: 13 additions & 0 deletions js/components/nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export default (hostComponent) => {
flex-direction: column;
gap: 0.4rem;
padding: 10px 10px;
max-width: var(--max-content-width);
margin: 0 auto;
width: 100%;
background-color: var(--nav-background-color);
z-index: 10;
a {
Expand Down Expand Up @@ -79,6 +82,12 @@ export default (hostComponent) => {
}
}
}
nav.overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
}
.burger-button {
position: absolute;
right: 0;
Expand Down Expand Up @@ -191,6 +200,10 @@ export default (hostComponent) => {
<span class="icon">🧮</span>
<span class="text">Web GPU tutorial</span>
</a>
<a href="/developers" title="Developer code examples">
<span class="icon">💻</span>
<span class="text">Developers</span>
</a>
<!-- <a href="/web-gpu" title="Web GPU Scene (wip)">
<span class="icon">🧮</span>
<span class="text">Web GPU</span>
Expand Down
2 changes: 2 additions & 0 deletions js/components/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export default async (hostComponent, baseUrl = config.BASE_URL) => {
*/
const loadRoute = async (url) => {
try {
hostComponent.classList.remove('full-width');
document.querySelector('nav')?.classList.remove('overlay');
const routePath =
url === '/' || url === '' ? `${baseUrl}/routes/index.js` : `${baseUrl}/routes${url}.js`;

Expand Down
14 changes: 14 additions & 0 deletions js/routes/developers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// File: js/routes/developers.js

export default (hostComponent) => {
hostComponent.innerHTML = `
<h1>Developers</h1>
<p>Select a language to view the hello world snippet.</p>
<div data-component="code-viewer">
<pre data-lang="js"><code>console.log('Hello, world!');</code></pre>
<pre data-lang="python"><code>print('Hello, world!')</code></pre>
<pre data-lang="ruby"><code>puts 'Hello, world!'</code></pre>
</div>
`;
};

2 changes: 2 additions & 0 deletions js/routes/heros.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export default (hostComponent) => {
hostComponent.classList.add('full-width');
document.querySelector('nav')?.classList.add('overlay');
// Define HTML structure with data attributes directly in the template
const indexHTML = `
<div data-component="fullscreen-hero"
Expand Down