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
22 changes: 8 additions & 14 deletions apps/registry/app/components/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ import { notFound } from "next/navigation";
import { QuickAdd } from "@/components/quick-add";
import { StorybookEmbed } from "@/components/storybook-embed";
import componentMetadata from "@/lib/component-metadata.json";
import {
breadcrumbLd,
jsonLdScript,
softwareSourceCodeLd,
} from "@/lib/jsonld";
import { breadcrumbLd, jsonLdScript, softwareSourceCodeLd } from "@/lib/jsonld";
import { generateOGMetadata, generateTwitterMetadata } from "@/lib/og";
import { canonical } from "@/lib/seo";
import {
Expand Down Expand Up @@ -45,13 +41,12 @@ const STORYBOOK_URL =
process.env.NEXT_PUBLIC_STORYBOOK_URL ?? "http://localhost:6006";

export async function generateStaticParams() {
return registry.items
.filter(
(item): item is RegistryComponent => item.type === "registry:component",
)
.map((item) => ({
slug: item.name,
}));
return registry.items.reduce<{ slug: string }[]>((parameters, item) => {
if (item.type === "registry:component") {
parameters.push({ slug: item.name });
}
return parameters;
}, []);
}

function getNpmUrl(packageName: string): string {
Expand Down Expand Up @@ -166,8 +161,7 @@ export default async function ComponentPage(props: Props) {
: []),
] as { id: string; title: string }[];

const SITE_URL =
process.env.NEXT_PUBLIC_SITE_URL ?? "https://ui.vllnt.ai";
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://ui.vllnt.ai";

return (
<>
Expand Down
11 changes: 9 additions & 2 deletions apps/registry/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,15 @@ async function buildLlmsFullTxt(): Promise<string> {
lines.push("```");
lines.push("");

for (const page of DOC_PAGES) {
const body = await readDocPage(page.slug);
const docPages = await Promise.all(
DOC_PAGES.map(async (page) => ({
...page,
body: await readDocPage(page.slug),
})),
);

for (const page of docPages) {
const { body } = page;
if (!body) continue;
lines.push(`## ${page.title}`);
lines.push("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1033,10 +1033,10 @@ function StepNavigationPreview() {
canPrev={step > 1}
currentStep={step}
onNext={() => {
setStep(step + 1);
setStep((currentStep) => currentStep + 1);
}}
onPrev={() => {
setStep(step - 1);
setStep((currentStep) => currentStep - 1);
}}
stepLabel="Step"
totalSteps={5}
Expand Down
19 changes: 12 additions & 7 deletions apps/registry/components/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,18 @@ export function Header() {
{ href: "/components", title: "Components" },
];

const searchItems = registry.items
.filter((item) => item.type === "registry:component")
.map((item) => ({
description: item.description,
id: item.name,
title: item.title,
}));
const searchItems = registry.items.reduce<
{ description?: string; id: string; title: string }[]
>((items, item) => {
if (item.type === "registry:component") {
items.push({
description: item.description,
id: item.name,
title: item.title,
});
}
return items;
}, []);

return (
<NavbarSaas
Expand Down
13 changes: 8 additions & 5 deletions apps/registry/components/storybook-embed/storybook-embed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,22 @@ export function StorybookEmbed({
height = 400,
storyId,
}: StorybookEmbedProps): React.ReactElement {
const [hasManualThemeSelection, setHasManualThemeSelection] =
React.useState(false);
const hasManualThemeSelectionRef = React.useRef(false);
const [previewTheme, setPreviewTheme] = React.useState<null | PreviewTheme>(
null,
);
const resolvedStoryId = storyId ?? toStoryId(componentName);

React.useEffect(() => {
if (hasManualThemeSelection) {
if (hasManualThemeSelectionRef.current) {
return;
}

const updateTheme = () => {
if (hasManualThemeSelectionRef.current) {
return;
}

setPreviewTheme(normalizePreviewTheme(resolveDocumentTheme()));
};

Expand All @@ -180,7 +183,7 @@ export function StorybookEmbed({
observer.disconnect();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Blocking: with the effect dependency changed to [], the observer/media-query listeners remain active after a user manually selects a preview theme. Later site/theme changes can still call setPreviewTheme(...) and overwrite the manual preview choice; the previous state dependency cleaned this up.

mediaQuery.removeEventListener("change", updateTheme);
};
}, [hasManualThemeSelection]);
}, []);

const iframeSource = React.useMemo(() => {
if (previewTheme === null) {
Expand All @@ -194,7 +197,7 @@ export function StorybookEmbed({
<div className={className}>
<PreviewThemeControls
onValueChange={(value) => {
setHasManualThemeSelection(true);
hasManualThemeSelectionRef.current = true;
setPreviewTheme(value);
}}
value={previewTheme}
Expand Down
44 changes: 25 additions & 19 deletions apps/registry/lib/sidebar-sections.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import registryData from "@/registry.json";
import type {
ComponentCategory,
Registry,
RegistryComponent,
} from "@/types/registry";
import type { ComponentCategory, Registry } from "@/types/registry";

const registry = registryData as Registry;

const components = registry.items
.filter(
(item): item is RegistryComponent => item.type === "registry:component",
.reduce<{ category?: ComponentCategory; name: string; title: string }[]>(
(items, item) => {
if (item.type === "registry:component") {
items.push({
category: item.category,
name: item.name,
title: item.title,
});
}
return items;
},
[],
)
.map((item) => ({
category: item.category,
name: item.name,
title: item.title,
}))
.sort((a, b) => a.title.localeCompare(b.title));

const categoryLabels: Record<ComponentCategory, string> = {
Expand Down Expand Up @@ -53,13 +54,18 @@ function groupComponentsByCategory(
return accumulator;
}, new Map<ComponentCategory, { name: string; title: string }[]>());

return categoryOrder
.filter((cat) => grouped.has(cat))
.map((category) => ({
category,
items: grouped.get(category) ?? [],
label: categoryLabels[category],
}));
return categoryOrder.flatMap((category) => {
const categoryItems = grouped.get(category);
return categoryItems
? [
{
category,
items: categoryItems,
label: categoryLabels[category],
},
]
: [];
});
}

const groupedComponents = groupComponentsByCategory(components);
Expand Down
24 changes: 15 additions & 9 deletions apps/registry/lib/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import packageJson from "../../../packages/ui/package.json";
import registry from "../registry.json";

type RegistryItem = {
readonly name: string;
readonly category?: string;
readonly name: string;
};

type Registry = {
readonly generatedAt?: string;
readonly items: readonly RegistryItem[];
readonly version?: string;
readonly generatedAt?: string;
};

const REGISTRY = registry as Registry;
Expand All @@ -33,11 +33,12 @@ export type CategoryStat = {
};

export function getCategoryStats(): readonly CategoryStat[] {
const counts = new Map<string, number>();
for (const item of REGISTRY.items) {
const counts = REGISTRY.items.reduce((accumulator, item) => {
const key = item.category ?? "uncategorized";
counts.set(key, (counts.get(key) ?? 0) + 1);
}
accumulator.set(key, (accumulator.get(key) ?? 0) + 1);
return accumulator;
}, new Map<string, number>());

return [...counts.entries()]
.map(([category, count]) => ({ category, count }))
.sort((a, b) => a.category.localeCompare(b.category));
Expand All @@ -56,7 +57,12 @@ export function getFeaturedComponents(): readonly RegistryItem[] {
"timeline",
"globe-3d",
];
return featuredSlugs
.map((slug) => REGISTRY.items.find((item) => item.name === slug))
.filter((item): item is RegistryItem => item !== undefined);
const registryByName = new Map(
REGISTRY.items.map((item) => [item.name, item] as const),
);

return featuredSlugs.flatMap((slug) => {
const item = registryByName.get(slug);
return item ? [item] : [];
});
}
2 changes: 1 addition & 1 deletion apps/registry/registry/default/auto-reload/auto-reload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ function getCurrencyFormatter(
const key = `${locale}|${currency}`;
let formatter = CURRENCY_FORMATTER_CACHE.get(key);
if (!formatter) {
formatter = new Intl.NumberFormat(locale, {
formatter = Intl.NumberFormat(locale, {
currency,
style: "currency",
});
Expand Down
22 changes: 16 additions & 6 deletions apps/registry/registry/default/carousel/carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";

Expand Down Expand Up @@ -82,6 +83,11 @@ function useCarouselLogic({
setCanScrollPrevious(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
}, []);
const onSelectReference = useRef(onSelect);

useEffect(() => {
onSelectReference.current = onSelect;
}, [onSelect]);

const scrollPrevious = useCallback(() => {
api?.scrollPrev();
Expand Down Expand Up @@ -117,19 +123,23 @@ function useCarouselLogic({
return;
}

api.on("reInit", onSelect);
api.on("select", onSelect);
const notifySelection = (selectedApi: CarouselApi) => {
onSelectReference.current(selectedApi);
};

api.on("reInit", notifySelection);
api.on("select", notifySelection);

const rafId = requestAnimationFrame(() => {
onSelect(api);
notifySelection(api);
});

return () => {
api?.off("select", onSelect);
api?.off("reInit", onSelect);
api?.off("select", notifySelection);
api?.off("reInit", notifySelection);
cancelAnimationFrame(rafId);
};
}, [api, onSelect]);
}, [api]);

return {
api,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { memo, useCallback, useEffect, useRef } from "react";
import { memo, useEffect, useRef } from "react";

import type { ReactNode } from "react";

Expand Down Expand Up @@ -125,8 +125,12 @@ function CompletionDialogImpl({
onConfirm,
title,
}: CompletionDialogProps): React.ReactNode {
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
const keyDownHandlerRef = useRef<(event: KeyboardEvent) => void>(() => {
return;
});

useEffect(() => {
keyDownHandlerRef.current = (event: KeyboardEvent) => {
if (!isOpen) return;
if (event.key === "Escape") {
event.preventDefault();
Expand All @@ -148,17 +152,20 @@ function CompletionDialogImpl({
event.stopPropagation();
onCancel();
}
},
[isOpen, onClose, onConfirm, onCancel, confirmShortcut, cancelShortcut],
);
};
}, [cancelShortcut, confirmShortcut, isOpen, onCancel, onClose, onConfirm]);

useEffect(() => {
if (!isOpen) return;
document.addEventListener("keydown", handleKeyDown, true);
const onDocumentKeyDown = (event: KeyboardEvent) => {
keyDownHandlerRef.current(event);
};

document.addEventListener("keydown", onDocumentKeyDown, true);
return () => {
document.removeEventListener("keydown", handleKeyDown, true);
document.removeEventListener("keydown", onDocumentKeyDown, true);
};
}, [isOpen, handleKeyDown]);
}, [isOpen]);

if (!isOpen) return null;

Expand Down
Loading
Loading