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
47 changes: 47 additions & 0 deletions .github/workflows/site.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Site

on:
workflow_dispatch:
push:
branches:
- master
- next
paths:
- ".github/workflows/site.yml"
- "apps/site/**"
- "docs/**"
- "packages/**"
- "package-lock.json"
- "package.json"

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci --ignore-scripts

- name: Install Pro docs package
env:
CHART_KIT_PRO_NPM_TOKEN: ${{ secrets.CHART_KIT_PRO_NPM_TOKEN }}
run: |
if [ -n "$CHART_KIT_PRO_NPM_TOKEN" ]; then
npm config set //registry.npmjs.org/:_authToken "$CHART_KIT_PRO_NPM_TOKEN"
npm install --no-save --package-lock=false --ignore-scripts @chart-kit/pro
echo "CHART_KIT_PRO_DOCS=true" >> "$GITHUB_ENV"
else
echo "CHART_KIT_PRO_NPM_TOKEN is not set; building with local Pro docs stubs."
fi

- name: Build site
run: npm run site:build
49 changes: 37 additions & 12 deletions apps/site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { chartKitMarkdownPatches } from "./src/lib/starlight-markdown-patches.mj
import tailwindcss from "@tailwindcss/vite";

const repositoryUrl = "https://github.com/indiespirit/react-native-chart-kit";
const docsSlug = (slug) => `docs/react-native/${slug}`;
const packageSource = (packagePath) =>
fileURLToPath(new URL(`../../packages/${packagePath}`, import.meta.url));
const nodeModuleSource = (packagePath) =>
Expand All @@ -24,6 +25,15 @@ const expoVectorIconsStub = localSource(
const svgTransformParserStub = localSource(
"./src/previews/svgTransformParserStub.ts"
);
const useRealProCharts = process.env.CHART_KIT_PRO_DOCS === "true";
const chartKitProAliases = useRealProCharts
? []
: [
{
find: /^@chart-kit\/pro$/,
replacement: localSource("./src/previews/proStub.tsx")
}
];

const chartKitPreviewWebAliases = () => ({
name: "chart-kit-preview-web-aliases",
Expand Down Expand Up @@ -77,32 +87,45 @@ export default defineConfig({
sidebar: [
{
label: "Start",
items: [{ slug: "docs/getting-started/installation" }]
items: [
{ slug: docsSlug("getting-started/installation") },
{ slug: docsSlug("getting-started/contributing") }
]
},
{
label: "Charts",
items: [
{ slug: "docs/charts/line-and-area" },
{ slug: "docs/charts/bar" },
{ slug: "docs/charts/pie-and-donut" },
{ slug: "docs/charts/progress" },
{ slug: "docs/charts/contribution-heatmap" }
{ slug: docsSlug("charts/line") },
{ slug: docsSlug("charts/area") },
{ slug: docsSlug("charts/bar") },
{ slug: docsSlug("charts/pie") },
{ slug: docsSlug("charts/donut") },
{ slug: docsSlug("charts/progress") },
{ slug: docsSlug("charts/contribution-heatmap") }
]
},
{
label: "Pro Charts",
items: [
{ slug: docsSlug("charts/candlebar") },
{ slug: docsSlug("charts/radar") },
{ slug: docsSlug("charts/combo") }
]
},
{
label: "Guides",
items: [
{ slug: "docs/charts/themes" },
{ slug: "docs/charts/accessibility" },
{ slug: "docs/troubleshooting" },
{ slug: "docs/recipes" }
{ slug: docsSlug("charts/themes") },
{ slug: docsSlug("charts/accessibility") },
{ slug: docsSlug("troubleshooting") },
{ slug: docsSlug("recipes") }
]
},
{
label: "Migration",
items: [
{ slug: "docs/migration/from-v1" },
{ slug: "docs/migration/prop-mapping" }
{ slug: docsSlug("migration/from-v1") },
{ slug: docsSlug("migration/prop-mapping") }
]
}
],
Expand All @@ -119,6 +142,7 @@ export default defineConfig({
"react-native",
"react-native-chart-kit",
"react-native-chart-kit/v2",
"@chart-kit/pro",
"react-native-gesture-handler",
"react-native-svg"
]
Expand All @@ -141,6 +165,7 @@ export default defineConfig({
find: /^@chart-kit\/svg-renderer$/,
replacement: packageSource("svg-renderer/src/index.ts")
},
...chartKitProAliases,
{
find: /^react-native$/,
replacement: reactNativeWebStub
Expand Down
1 change: 1 addition & 0 deletions apps/site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"dependencies": {
"@tailwindcss/vite": "^4.3.0",
"lucide-astro": "^0.556.0",
"react-live": "^4.1.8",
"tailwindcss": "^4.3.0"
}
}
218 changes: 218 additions & 0 deletions apps/site/src/chart-theme-controls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import {
applyChartThemePreset,
chartThemeOptions,
getCurrentChartThemePreset,
isChartThemePreset
} from "./previews/chartTheme";

type ColorTheme = "dark" | "light";

const controlSelector = "[data-chart-theme-control]";
const docsThemeToggleSelector = "[data-docs-theme-toggle]";
const selectSelector = "[data-chart-theme-select]";
const starlightThemeStorageKey = "starlight-theme";
const themeColor = {
dark: "#06070a",
light: "#f7f8fb"
};

const isColorTheme = (value: string | null): value is ColorTheme =>
value === "dark" || value === "light";

const getCurrentColorTheme = (): ColorTheme => {
try {
const stored = localStorage.getItem(starlightThemeStorageKey);

if (isColorTheme(stored)) {
return stored;
}
} catch {
// Storage can be blocked in restricted browser contexts.
}

return document.documentElement.dataset.theme === "light" ? "light" : "dark";
};

const syncStarlightThemeSelects = (theme: ColorTheme) => {
document
.querySelectorAll<HTMLSelectElement>("starlight-theme-select select")
.forEach((select) => {
if (select.value !== theme) {
select.value = theme;
}
});
};

const syncDocsThemeToggles = (theme: ColorTheme) => {
const nextTheme = theme === "dark" ? "light" : "dark";

document
.querySelectorAll<HTMLButtonElement>(docsThemeToggleSelector)
.forEach((button) => {
button.dataset.themeCurrent = theme;
button.dataset.themeNext = nextTheme;
button.setAttribute("aria-label", `Switch to ${nextTheme} mode`);
button.setAttribute("title", `Switch to ${nextTheme} mode`);
});
};

const updateThemeColor = (theme: ColorTheme) => {
const meta = document.querySelector<HTMLMetaElement>(
'meta[name="theme-color"]'
);

if (meta) {
meta.content = themeColor[theme];
}
};

const applyColorTheme = (theme: ColorTheme) => {
document.documentElement.dataset.theme = theme;
document.documentElement.style.colorScheme = theme;

try {
localStorage.setItem(starlightThemeStorageKey, theme);
} catch {
// Persisting the preference is best-effort.
}

syncStarlightThemeSelects(theme);
syncDocsThemeToggles(theme);
updateThemeColor(theme);
window.StarlightThemeProvider?.updatePickers(theme);
};

const syncChartThemeControls = () => {
const theme = getCurrentChartThemePreset();

document
.querySelectorAll<HTMLSelectElement>(selectSelector)
.forEach((select) => {
if (select.value !== theme) {
select.value = theme;
}
});
};

const createChartThemeControl = () => {
const label = document.createElement("label");
label.className = "chartkit-chart-theme-control";
label.dataset.chartThemeControl = "true";

const text = document.createElement("span");
text.className = "chartkit-chart-theme-control__label";
text.textContent = "Chart theme";

const select = document.createElement("select");
select.className = "chartkit-chart-theme-control__select";
select.dataset.chartThemeSelect = "true";
select.setAttribute("aria-label", "Chart theme");

chartThemeOptions.forEach((option) => {
const item = document.createElement("option");
item.value = option.value;
item.textContent = option.label;
select.append(item);
});

label.append(text, select);

return label;
};

const createDocsThemeToggle = () => {
const button = document.createElement("button");
button.className = "chartkit-docs-theme-toggle";
button.dataset.docsThemeToggle = "true";
button.type = "button";
button.innerHTML = `
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path>
</svg>
`;

return button;
};

const enhanceThemeSelectors = () => {
document
.querySelectorAll<HTMLElement>("starlight-theme-select")
.forEach((themeSelect) => {
const previousElement = themeSelect.previousElementSibling;

if (!previousElement?.matches(controlSelector)) {
themeSelect.before(createChartThemeControl());
}

if (themeSelect.dataset.chartkitIconToggle !== "true") {
themeSelect.dataset.chartkitIconToggle = "true";
themeSelect.append(createDocsThemeToggle());
}
});

syncChartThemeControls();
syncDocsThemeToggles(getCurrentColorTheme());
};

const handleChartThemeChange = (event: Event) => {
const select = (event.target as Element | null)?.closest<HTMLSelectElement>(
selectSelector
);

if (!select || !isChartThemePreset(select.value)) {
return;
}

applyChartThemePreset(select.value);
syncChartThemeControls();
};

const handleDocsThemeClick = (event: Event) => {
const button = (event.target as Element | null)?.closest<HTMLButtonElement>(
docsThemeToggleSelector
);

if (!button) {
return;
}

const nextTheme = getCurrentColorTheme() === "dark" ? "light" : "dark";
applyColorTheme(nextTheme);
};

const bootChartThemeControls = () => {
enhanceThemeSelectors();
applyChartThemePreset(getCurrentChartThemePreset(), { dispatch: false });
applyColorTheme(getCurrentColorTheme());

if (!window.__chartkitChartThemeControlBound) {
window.__chartkitChartThemeControlBound = true;
document.addEventListener("change", handleChartThemeChange);
document.addEventListener("click", handleDocsThemeClick);
}
};

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", bootChartThemeControls, {
once: true
});
} else {
bootChartThemeControls();
}

customElements.whenDefined("starlight-theme-select").then(() => {
bootChartThemeControls();
});

document.addEventListener("astro:page-load", bootChartThemeControls);

declare global {
interface Window {
StarlightThemeProvider?: {
updatePickers: (theme?: string) => void;
};
__chartkitChartThemeControlBound?: boolean;
}
}

export {};
2 changes: 1 addition & 1 deletion apps/site/src/components/ChartKitFeatures.astro
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const features = [
interactive.
</p>
<a
href="/docs/getting-started/installation"
href="/docs/react-native/getting-started/installation"
class="mt-8 inline-flex h-10 items-center justify-center rounded-full border border-white/15 px-5 text-sm font-semibold tracking-[-0.01em] text-white/78 transition-colors hover:border-white/28 hover:text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-white/45 [html[data-theme='light']_&]:border-black/15 [html[data-theme='light']_&]:text-black/70 [html[data-theme='light']_&]:hover:border-black/28 [html[data-theme='light']_&]:hover:text-black [html[data-theme='light']_&]:focus-visible:outline-black/40"
>
Read docs
Expand Down
Loading