Skip to content

Commit 1f61c68

Browse files
committed
feat(demos): integrate interactive examples and static demos into documentation site
- Added `demos:build` script to generate static demos for the documentation site. - Updated `DemoContainer` component for improved tab structure and usability. - Introduced a new `Examples` page in the docs with an iframe for demo previews. - Enhanced fallback in `PostStore` with static data for cases when the API is unavailable.
1 parent ff53f9b commit 1f61c68

6 files changed

Lines changed: 95 additions & 57 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
3737
website/build
3838
website/.docusaurus
3939
website/.cache-loader
40+
website/static/demos

examples/rendering/src/components/DemoContainer.tsx

Lines changed: 41 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,70 +19,58 @@ export function DemoContainer({
1919
codeTabs?: CodeTab[];
2020
}) {
2121
const [activeTab, setActiveTab] = useState(0);
22-
const [showCode, setShowCode] = useState(true);
2322

24-
if (!codeTabs || codeTabs.length === 0) {
25-
return (
26-
<div className="bg-zinc-900 border border-zinc-800 rounded-xl p-6">
23+
const allTabs = [
24+
{label: 'Demo', type: 'demo' as const},
25+
...(codeTabs ?? []).map((t) => ({...t, type: 'code' as const})),
26+
];
27+
28+
return (
29+
<div className="bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden">
30+
{/* Header */}
31+
<div className="px-6 pt-5 pb-0">
2732
<h3 className="text-lg font-semibold text-zinc-100 mb-1">{title}</h3>
2833
{description && (
29-
<p className="text-sm text-zinc-400 mb-4">{description}</p>
34+
<p className="text-sm text-zinc-400 mb-0">{description}</p>
3035
)}
31-
{children}
3236
</div>
33-
);
34-
}
3537

36-
return (
37-
<div className="bg-zinc-900 border border-zinc-800 rounded-xl overflow-hidden">
38-
<div className="flex flex-col lg:flex-row">
39-
{/* Live demo */}
40-
<div className="flex-[3] p-6 min-w-0">
41-
<h3 className="text-lg font-semibold text-zinc-100 mb-1">{title}</h3>
42-
{description && (
43-
<p className="text-sm text-zinc-400 mb-4">{description}</p>
44-
)}
45-
{children}
46-
</div>
38+
{/* Tabs */}
39+
<div className="flex border-b border-zinc-800 px-6 mt-4">
40+
{allTabs.map((tab, i) => (
41+
<button
42+
key={tab.label}
43+
type="button"
44+
onClick={() => setActiveTab(i)}
45+
className={`px-3 py-2 text-xs font-medium transition-colors cursor-pointer -mb-px ${
46+
activeTab === i
47+
? 'text-zinc-100 border-b-2 border-indigo-400'
48+
: 'text-zinc-500 hover:text-zinc-300'
49+
}`}
50+
>
51+
{tab.label}
52+
</button>
53+
))}
54+
</div>
4755

48-
{/* Code panel */}
49-
<div className="flex-[2] border-t lg:border-t-0 lg:border-l border-zinc-800 bg-zinc-950 min-w-0">
50-
<div className="flex items-center justify-between border-b border-zinc-800">
51-
<div className="flex">
52-
{codeTabs.map((tab, i) => (
53-
<button
54-
key={tab.label}
55-
type="button"
56-
onClick={() => setActiveTab(i)}
57-
className={`px-3 py-2 text-xs font-medium transition-colors cursor-pointer ${
58-
activeTab === i
59-
? 'text-zinc-100 bg-zinc-900/50 border-b-2 border-indigo-400'
60-
: 'text-zinc-500 hover:text-zinc-300'
61-
}`}
62-
>
63-
{tab.label}
64-
</button>
65-
))}
66-
</div>
67-
<button
68-
type="button"
69-
onClick={() => setShowCode(!showCode)}
70-
className="px-2 py-1 mr-2 text-xs text-zinc-500 hover:text-zinc-300 cursor-pointer lg:hidden"
71-
>
72-
{showCode ? 'Hide' : 'Show'}
73-
</button>
74-
</div>
75-
{showCode && codeTabs[activeTab] && (
76-
<div className="max-h-[500px] overflow-y-auto">
56+
{/* Content */}
57+
{activeTab === 0 ? (
58+
<div className="p-6">{children}</div>
59+
) : (
60+
(() => {
61+
const codeTab = codeTabs?.[activeTab - 1];
62+
if (!codeTab) return null;
63+
return (
64+
<div className="max-h-[500px] overflow-y-auto bg-zinc-950">
7765
<CodeBlock
78-
code={codeTabs[activeTab].code}
79-
language={codeTabs[activeTab].language ?? 'tsx'}
66+
code={codeTab.code}
67+
language={codeTab.language ?? 'tsx'}
8068
compact={true}
8169
/>
8270
</div>
83-
)}
84-
</div>
85-
</div>
71+
);
72+
})()
73+
)}
8674
</div>
8775
);
8876
}

examples/rendering/src/stores/postStore.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ interface Post {
66
body: string;
77
}
88

9+
const STATIC_POSTS: Post[] = [
10+
{
11+
id: 1,
12+
title: 'Introduction to Classy Store',
13+
body: 'Learn how class-based reactive state management works with ES6 Proxies.',
14+
},
15+
{
16+
id: 2,
17+
title: 'Using Computed Properties',
18+
body: 'Getters on your class become computed values that update automatically.',
19+
},
20+
{
21+
id: 3,
22+
title: 'Async Actions',
23+
body: 'Methods on your store class can be async — loading states are reactive too.',
24+
},
25+
{
26+
id: 4,
27+
title: 'Store Composition',
28+
body: 'Compose multiple stores together for complex application state.',
29+
},
30+
{
31+
id: 5,
32+
title: 'Performance Tips',
33+
body: 'Proxy-based tracking means only the data you read triggers re-renders.',
34+
},
35+
];
36+
937
export class PostStore {
1038
posts: Post[] = [];
1139
loading = false;
@@ -22,8 +50,10 @@ export class PostStore {
2250
const res = await fetch('/api/posts');
2351
if (!res.ok) throw new Error(`HTTP ${res.status}`);
2452
this.posts = await res.json();
25-
} catch (e) {
26-
this.error = e instanceof Error ? e.message : 'Unknown error';
53+
} catch {
54+
// Fallback to static data when the dev server API is unavailable (e.g. static build)
55+
this.posts = STATIC_POSTS;
56+
this.error = null;
2757
} finally {
2858
this.loading = false;
2959
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@
6969
"prerelease:enter": "changeset pre enter",
7070
"prerelease:exit": "changeset pre exit",
7171
"---------- Docs (Docusaurus) ------------------------------------------": "",
72-
"docs:dev": "cd website && bun run start",
73-
"docs:build": "cd website && bun run build",
72+
"demos:build": "cd examples/rendering && bun run build.ts --outdir=../../website/static/demos",
73+
"docs:dev": "bun run demos:build && cd website && bun run start",
74+
"docs:build": "bun run demos:build && cd website && bun run build",
7475
"docs:deploy": "cd website && bun run deploy",
7576
"---------- CI / Helper ------------------------------------------------": "",
7677
"checkall": "bun run lint:fix && bun run test && bun run typecheck",

website/docusaurus.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ const config: Config = {
7171
position: 'left',
7272
label: 'Docs',
7373
},
74+
{
75+
to: '/examples',
76+
label: 'Examples',
77+
position: 'left',
78+
},
7479
{
7580
href: 'https://github.com/codebelt/classy-store',
7681
label: 'GitHub',

website/src/pages/examples.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Layout from '@theme/Layout';
2+
3+
export default function ExamplesPage() {
4+
return (
5+
<Layout title="Examples" description="Interactive classy-store examples">
6+
<iframe
7+
src="/classy-store/demos/index.html"
8+
title="Classy Store Interactive Examples"
9+
style={{width: '100%', height: 'calc(100vh - 60px)', border: 'none'}}
10+
/>
11+
</Layout>
12+
);
13+
}

0 commit comments

Comments
 (0)