Skip to content

Commit 064e68f

Browse files
committed
feat: framework examples
1 parent 7d63392 commit 064e68f

14 files changed

Lines changed: 2032 additions & 48 deletions

.vitepress/config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ export default defineConfig({
8989
{ icon: 'twitter', link: 'https://twitter.com/teamnstudio' },
9090
],
9191

92-
footer: {
93-
// message: 'Released under the Apache-2.0 License.',
94-
copyright: `Copyright © ${new Date().getFullYear()} nStudio`,
95-
},
92+
// footer: {
93+
// // message: 'Released under the Apache-2.0 License.',
94+
// copyright: `Copyright © ${new Date().getFullYear()} nStudio`,
95+
// },
9696

9797
search: {
9898
provider: 'local',
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<script setup lang="ts">
2+
import { ref, computed, watch } from 'vue'
3+
4+
interface Props {
5+
syncKey?: string
6+
}
7+
8+
const props = withDefaults(defineProps<Props>(), {
9+
syncKey: 'framework'
10+
})
11+
12+
const frameworks = ['Angular', 'React', 'Vue', 'Svelte', 'Solid'] as const
13+
type Framework = typeof frameworks[number]
14+
15+
// Use localStorage to persist and sync selection across tabs
16+
const getStoredFramework = (): Framework => {
17+
if (typeof window !== 'undefined') {
18+
const stored = localStorage.getItem(`ncharts-${props.syncKey}`)
19+
if (stored && frameworks.includes(stored as Framework)) {
20+
return stored as Framework
21+
}
22+
}
23+
return 'Angular'
24+
}
25+
26+
const selectedFramework = ref<Framework>(getStoredFramework())
27+
28+
// Watch for changes and persist
29+
watch(selectedFramework, (newVal) => {
30+
if (typeof window !== 'undefined') {
31+
localStorage.setItem(`ncharts-${props.syncKey}`, newVal)
32+
// Dispatch custom event for cross-tab sync
33+
window.dispatchEvent(new CustomEvent('framework-change', { detail: newVal }))
34+
}
35+
})
36+
37+
// Listen for changes from other tabs on the same page
38+
if (typeof window !== 'undefined') {
39+
window.addEventListener('framework-change', ((event: CustomEvent<Framework>) => {
40+
if (event.detail !== selectedFramework.value) {
41+
selectedFramework.value = event.detail
42+
}
43+
}) as EventListener)
44+
}
45+
46+
const selectFramework = (framework: Framework) => {
47+
selectedFramework.value = framework
48+
}
49+
50+
// Expose selected framework for slot content
51+
defineExpose({ selectedFramework })
52+
</script>
53+
54+
<template>
55+
<div class="framework-tabs">
56+
<div class="tab-header">
57+
<button
58+
v-for="framework in frameworks"
59+
:key="framework"
60+
:class="['tab-button', { active: selectedFramework === framework }]"
61+
@click="selectFramework(framework)"
62+
>
63+
{{ framework }}
64+
</button>
65+
</div>
66+
<div class="tab-content">
67+
<div v-show="selectedFramework === 'Angular'">
68+
<slot name="angular" />
69+
</div>
70+
<div v-show="selectedFramework === 'React'">
71+
<slot name="react" />
72+
</div>
73+
<div v-show="selectedFramework === 'Vue'">
74+
<slot name="vue" />
75+
</div>
76+
<div v-show="selectedFramework === 'Svelte'">
77+
<slot name="svelte" />
78+
</div>
79+
<div v-show="selectedFramework === 'Solid'">
80+
<slot name="solid" />
81+
</div>
82+
</div>
83+
</div>
84+
</template>
85+
86+
<style scoped>
87+
.framework-tabs {
88+
margin: 1.5rem 0;
89+
}
90+
91+
.tab-header {
92+
display: flex;
93+
gap: 0;
94+
border-bottom: 1px solid var(--vp-c-divider);
95+
margin-bottom: 0;
96+
}
97+
98+
.tab-button {
99+
padding: 0.625rem 1rem;
100+
border: none;
101+
background: transparent;
102+
color: var(--vp-c-text-2);
103+
cursor: pointer;
104+
font-size: 0.875rem;
105+
font-weight: 500;
106+
border-bottom: 2px solid transparent;
107+
transition: all 0.2s ease;
108+
margin-bottom: -1px;
109+
}
110+
111+
.tab-button:hover {
112+
color: var(--vp-c-brand-1);
113+
}
114+
115+
.tab-button.active {
116+
color: var(--vp-c-brand-1);
117+
border-bottom-color: var(--vp-c-brand-1);
118+
}
119+
120+
.tab-content {
121+
margin-top: 0;
122+
}
123+
124+
.tab-content :deep(div[class*="language-"]) {
125+
margin-top: 0;
126+
border-radius: 0 0 8px 8px;
127+
}
128+
129+
.tab-content :deep(div[class*="language-"]:first-child) {
130+
margin-top: 0;
131+
}
132+
</style>

.vitepress/theme/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import NChartsHome from './NChartsHome.vue';
55
import ChartShowcase from './components/ChartShowcase.vue';
66
import FeatureCard from './components/FeatureCard.vue';
77
import CodePreview from './components/CodePreview.vue';
8+
import FrameworkTabs from './components/FrameworkTabs.vue';
89

910
export default {
1011
extends: DefaultTheme,
@@ -14,5 +15,6 @@ export default {
1415
app.component('ChartShowcase', ChartShowcase);
1516
app.component('FeatureCard', FeatureCard);
1617
app.component('CodePreview', CodePreview);
18+
app.component('FrameworkTabs', FrameworkTabs);
1719
},
1820
};

0 commit comments

Comments
 (0)