Skip to content
Draft
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
99 changes: 87 additions & 12 deletions demo/app-root.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,101 @@
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
// unsafeHTML is needed to render dynamic custom-element tag names;
// Lit's html`` tag cannot render variable tag names directly.
import { unsafeHTML } from 'lit/directives/unsafe-html.js';

import '@src/elements/ia-button/ia-button-story';
import '@src/labs/ia-snow/ia-snow-story';
import '@src/elements/ia-combo-box/ia-combo-box-story';
import '@src/elements/ia-status-indicator/ia-status-indicator-story';
const storyModules = import.meta.glob(
['../src/elements/**/*-story.ts', '../src/labs/**/*-story.ts'],
{ eager: true }
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa! Nice!


const storyEntries = Object.keys(storyModules)
.map(path => {
const labs = path.includes('/src/labs/');
const parts = path.split('/');
const tag = parts[parts.length - 2];
return { tag, storyTag: `${tag}-story`, id: `elem-${tag}`, labs };
})
.sort((a, b) => a.tag.localeCompare(b.tag));

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; }

private _observer?: IntersectionObserver;

render() {
return html`
<h1>🏛️ Internet Archive Elements ⚛️</h1>
<nav id="ia-sidebar">
<h2>Production-Ready</h2>
${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>`)}
</nav>
<div id="ia-content">
<h1>Internet Archive Elements</h1>
<h2>Production-Ready Elements</h2>
${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>
`)}
</div>
`;
}

<h2>🚀 Production-Ready Elements</h2>
firstUpdated() {
const allIds = ALL_ENTRIES.map(e => e.id);

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

<h2>🧪 Labs Elements</h2>
const visible = new Set<string>();

<ia-snow-story></ia-snow-story>
<ia-button-story></ia-button-story>
`;
// Only anchors in the top 30% of the viewport count as "active".
// The first (topmost) visible anchor wins.
this._observer = new IntersectionObserver(
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));
},
{ rootMargin: '0px 0px -70% 0px' },
);

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' });
}
});
});
}

disconnectedCallback() {
super.disconnectedCallback();
this._observer?.disconnect();
}
}
86 changes: 84 additions & 2 deletions demo/index.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

Expand All @@ -24,7 +24,89 @@ a:hover {
}

body {
margin: 1rem;
margin: 0;
min-width: 320px;
min-height: 100vh;
display: flex;
}

app-root {
display: contents;
}

#ia-sidebar {
width: 200px;
flex-shrink: 0;
position: sticky;
top: 0;
height: 100vh;
border-right: 1px solid #ddd;
padding: 1rem 0;
box-sizing: border-box;
overflow-y: auto;
}

#ia-sidebar h2 {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #767676;
padding: 0 1rem;
margin: 0 0 0.5rem;
}

#ia-sidebar a {
display: block;
font-size: 0.82rem;
font-weight: 400;
color: #444;
text-decoration: none;
padding: 0.3rem 1rem;
border-left: 3px solid transparent;
transition: background 0.1s, color 0.1s, border-color 0.1s;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

#ia-sidebar a:hover {
background: #f0f0f0;
color: #213547;
}

#ia-sidebar a.active {
color: #194880;
border-left-color: #194880;
background: #f0f4f9;
font-weight: 600;
}

#ia-content {
flex: 1;
padding: 0.75rem 1rem;
padding-bottom: 100vh;
min-width: 0;
}

.ia-anchor {
scroll-margin-top: 16px;
}

.ia-anchor > * {
display: block;
margin-bottom: 0.5rem;
}

#ia-content h1 {
font-size: 1.4rem;
margin: 0 0 0.25rem;
display: flex;
align-items: center;
}

#ia-content h2 {
font-size: 1.1rem;
font-weight: 600;
margin: 0.75rem 0 0.25rem;
}
1 change: 0 additions & 1 deletion demo/story-components/story-prop-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export class StoryPropsSettings extends LitElement {
if (!this.propInputData) return nothing;

return html`
<h3>Properties</h3>
<div class="settings-options">
<table>
${this.propInputData.settings.map(
Expand Down
1 change: 0 additions & 1 deletion demo/story-components/story-styles-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export class StoryStylesSettings extends LitElement {
if (!this.styleInputData) return nothing;

return html`
<h3>Styles</h3>
<div class="settings-options">
<table>
${this.styleInputData.settings.map(
Expand Down
Loading
Loading