Skip to content
Open
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
102 changes: 70 additions & 32 deletions demo/app-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js';

const storyModules = import.meta.glob(
['../src/elements/**/*-story.ts', '../src/labs/**/*-story.ts'],
{ eager: true }
{ eager: true },
);

const storyEntries = Object.keys(storyModules)
.map(path => {
.map((path) => {
const labs = path.includes('/src/labs/');
const parts = path.split('/');
const filename = parts[parts.length - 1]; // e.g. "ia-button-story.ts"
Expand All @@ -19,13 +19,15 @@ const storyEntries = Object.keys(storyModules)
})
.sort((a, b) => a.tag.localeCompare(b.tag));

const productionEntries = storyEntries.filter(e => !e.labs);
const labsEntries = storyEntries.filter(e => e.labs);
const productionEntries = storyEntries.filter((e) => !e.labs);
const labsEntries = storyEntries.filter((e) => e.labs);
const ALL_ENTRIES = [...productionEntries, ...labsEntries];

@customElement('app-root')
export class AppRoot extends LitElement {
createRenderRoot() { return this; }
createRenderRoot() {
return this;
}

private _observer?: IntersectionObserver;
private _abortController = new AbortController();
Expand All @@ -34,65 +36,101 @@ export class AppRoot extends LitElement {
return html`
<nav id="ia-sidebar">
<h2>Production-Ready</h2>
${productionEntries.map(e => html`<a href="#${e.id}">&lt;${e.tag}&gt;</a>`)}
${productionEntries.map(
(e) => html`<a href="#${e.id}">&lt;${e.tag}&gt;</a>`,
)}
<h2>Labs 🧪</h2>
${labsEntries.map(e => html`<a href="#${e.id}">&lt;${e.tag}&gt;</a>`)}
${labsEntries.map((e) => html`<a href="#${e.id}">&lt;${e.tag}&gt;</a>`)}
</nav>
<div id="ia-content">
<h1>Internet Archive Elements</h1>

<fieldset>
<legend>Settings</legend>
<label for="darkModeToggle">Dark Mode</label>
<input
id="darkModeToggle"
type="checkbox"
switch
@change=${this.toggleDarkMode}
/>
</fieldset>

<h2>Production-Ready Elements</h2>
${productionEntries.map(e => html`
<div id="${e.id}" class="ia-anchor">
${unsafeHTML(`<${e.storyTag}></${e.storyTag}>`)}
</div>
`)}
${productionEntries.map(
(e) => html`
<div id="${e.id}" class="ia-anchor">
${unsafeHTML(`<${e.storyTag}></${e.storyTag}>`)}
</div>
`,
)}
<h2>Labs Elements</h2>
${labsEntries.map(e => html`
<div id="${e.id}" class="ia-anchor">
${unsafeHTML(`<${e.storyTag}></${e.storyTag}>`)}
</div>
`)}
${labsEntries.map(
(e) => html`
<div id="${e.id}" class="ia-anchor">
${unsafeHTML(`<${e.storyTag}></${e.storyTag}>`)}
</div>
`,
)}
</div>
`;
}

private toggleDarkMode(event: Event) {
const checkbox = event.target as HTMLInputElement;
if (checkbox.checked) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}

firstUpdated() {
const allIds = ALL_ENTRIES.map(e => e.id);
const allIds = ALL_ENTRIES.map((e) => e.id);

const links = Object.fromEntries(
allIds.map(id => [id, this.querySelector(`#ia-sidebar a[href="#${id}"]`)])
allIds.map((id) => [
id,
this.querySelector(`#ia-sidebar a[href="#${id}"]`),
]),
);

const visible = new Set<string>();

// Only anchors in the top 30% of the viewport count as "active".
// The first (topmost) visible anchor wins.
this._observer = new IntersectionObserver(
entries => {
(entries) => {
for (const entry of entries) {
if (entry.isIntersecting) visible.add(entry.target.id);
else visible.delete(entry.target.id);
}
const activeId = allIds.find(id => visible.has(id)) ?? allIds[0];
allIds.forEach(id => links[id]?.classList.toggle('active', id === activeId));
const activeId = allIds.find((id) => visible.has(id)) ?? allIds[0];
allIds.forEach((id) =>
links[id]?.classList.toggle('active', id === activeId),
);
},
{ rootMargin: '0px 0px -70% 0px' },
);

allIds.forEach(id => {
allIds.forEach((id) => {
const el = document.getElementById(id);
if (el) this._observer!.observe(el);
});

allIds.forEach(id => {
links[id]?.addEventListener('click', (e: Event) => {
e.preventDefault();
const el = document.getElementById(id);
if (el) {
const top = el.getBoundingClientRect().top + window.scrollY;
window.scrollTo({ top: Math.max(0, top - 16), behavior: 'smooth' });
}
}, { signal: this._abortController.signal });
allIds.forEach((id) => {
links[id]?.addEventListener(
'click',
(e: Event) => {
e.preventDefault();
const el = document.getElementById(id);
if (el) {
const top = el.getBoundingClientRect().top + window.scrollY;
window.scrollTo({ top: Math.max(0, top - 16), behavior: 'smooth' });
}
},
{ signal: this._abortController.signal },
);
});
}

Expand Down
16 changes: 15 additions & 1 deletion demo/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,23 @@
-moz-osx-font-smoothing: grayscale;
}

:root.dark {
color-scheme: dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

--ia-theme-primary-background-color: #242424;
--ia-theme-secondary-background-color: #1e1e1e;
--ia-theme-primary-text-color: rgba(255, 255, 255, 0.87);
--ia-theme-secondary-text-color: #a0a0a0;
--ia-theme-link-color: #4b64ff;
--ia-theme-primary-cta-fill: #4b64ff;
--ia-theme-primary-cta-text-color: #ffffff;
}

a {
font-weight: 500;
color: #646cff;
color: var(--ia-theme-link-color);
text-decoration: inherit;
}

Expand Down
8 changes: 8 additions & 0 deletions demo/story-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ export class StoryTemplate extends LitElement {
padding: 0.5em;
}

.disclosure-arrow {
width: 12px;
height: 12px;
transform: rotate(-90deg);
transition: transform 0.2s ease-in-out;
filter: invert(0.5);
}

.slot-container.hidden {
display: none;
}
Expand Down
Loading