Some changes ported from the work on the makerkit.dev website related… (#89)
* Some changes ported from the work on the makerkit.dev website related to the marketing sections of the kit, such as documentation * Added slight background hue to make darker theme better looking * Support more complex configurations for documentation navigations. * Do not fetch content from Keystatic when non-needed * Add cursor pointers in dropdown * Updated packages
This commit is contained in:
committed by
GitHub
parent
a682b991f3
commit
079a8f857a
@@ -27,13 +27,13 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.468.0",
|
||||
"next": "15.0.4",
|
||||
"react": "19.0.0",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
||||
@@ -392,32 +392,36 @@ function PlanIntervalSwitcher(
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<div className={'flex'}>
|
||||
<div className={'flex gap-x-1 rounded-full border p-1.5'}>
|
||||
{props.intervals.map((plan, index) => {
|
||||
const selected = plan === props.interval;
|
||||
|
||||
const className = cn(
|
||||
'animate-in fade-in !outline-none transition-all focus:!ring-0',
|
||||
'animate-in fade-in rounded-full !outline-none transition-all focus:!ring-0',
|
||||
{
|
||||
'rounded-r-none border-r-transparent': index === 0,
|
||||
'rounded-l-none': index === props.intervals.length - 1,
|
||||
['hover:text-primary text-muted-foreground border']: !selected,
|
||||
['hover:text-initial hover:bg-background cursor-default font-semibold']:
|
||||
selected,
|
||||
'border-r-transparent': index === 0,
|
||||
['hover:text-primary text-muted-foreground']: !selected,
|
||||
['cursor-default font-semibold']: selected,
|
||||
['hover:bg-initial']: !selected
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={plan}
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
variant={selected ? 'default' : 'ghost'}
|
||||
className={className}
|
||||
onClick={() => props.setInterval(plan)}
|
||||
>
|
||||
<span className={'flex items-center space-x-1'}>
|
||||
<If condition={selected}>
|
||||
<CheckCircle className={'animate-in fade-in zoom-in-90 h-4'} />
|
||||
</If>
|
||||
<span className={'flex items-center'}>
|
||||
<CheckCircle
|
||||
className={cn('animate-in fade-in zoom-in-95 h-3.5', {
|
||||
hidden: !selected,
|
||||
'slide-in-from-left-4': index === 0,
|
||||
'slide-in-from-right-4': index === props.intervals.length - 1,
|
||||
})}
|
||||
/>
|
||||
|
||||
<span className={'capitalize'}>
|
||||
<Trans i18nKey={`common:billingInterval.${plan}`} />
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"./components": "./src/components/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lemonsqueezy/lemonsqueezy.js": "3.3.1"
|
||||
"@lemonsqueezy/lemonsqueezy.js": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/billing": "workspace:*",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Cms, CmsClient } from '@kit/cms-types';
|
||||
|
||||
import { createKeystaticReader } from './create-reader';
|
||||
import { PostEntryProps } from './keystatic.config';
|
||||
import { DocumentationEntryProps, PostEntryProps } from './keystatic.config';
|
||||
import { renderMarkdoc } from './markdoc';
|
||||
|
||||
export function createKeystaticClient() {
|
||||
@@ -19,11 +19,13 @@ class KeystaticClient implements CmsClient {
|
||||
throw new Error(`Collection ${collection} not found`);
|
||||
}
|
||||
|
||||
const docs = await reader.collections[collection].all();
|
||||
const fetchContent = options.content ?? true;
|
||||
|
||||
const startOffset = options?.offset ?? 0;
|
||||
const endOffset = startOffset + (options?.limit ?? 10);
|
||||
|
||||
const docs = await reader.collections[collection].all();
|
||||
|
||||
const filtered = docs
|
||||
.filter((item) => {
|
||||
const status = options?.status ?? 'published';
|
||||
@@ -80,72 +82,95 @@ class KeystaticClient implements CmsClient {
|
||||
return right - left;
|
||||
});
|
||||
|
||||
function processItems(items: typeof docs) {
|
||||
const result: typeof docs = [...items];
|
||||
function processItems(items: typeof filtered) {
|
||||
const slugSet = new Set(items.map((item) => item.slug));
|
||||
const indexFileCache = new Map<string, boolean>();
|
||||
const parentCache = new Map<string, string | null>();
|
||||
|
||||
const indexFiles = items.filter((item) => {
|
||||
const parts = item.slug.split('/');
|
||||
const isIndexFile = (slug: string): boolean => {
|
||||
if (indexFileCache.has(slug)) {
|
||||
return indexFileCache.get(slug)!;
|
||||
}
|
||||
|
||||
return (
|
||||
parts.length > 1 &&
|
||||
parts[parts.length - 1] === parts[parts.length - 2]
|
||||
);
|
||||
});
|
||||
const parts = slug.split('/');
|
||||
|
||||
const result =
|
||||
parts.length === 1 ||
|
||||
(parts.length >= 2 &&
|
||||
parts[parts.length - 1] === parts[parts.length - 2]);
|
||||
|
||||
indexFileCache.set(slug, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
const findClosestValidParent = (pathParts: string[]): string | null => {
|
||||
const path = pathParts.join('/');
|
||||
|
||||
if (parentCache.has(path)) {
|
||||
return parentCache.get(path)!;
|
||||
}
|
||||
|
||||
function findParentIndex(pathParts: string[]): string | null {
|
||||
// Try each level up from the current path until we find an index file
|
||||
for (let i = pathParts.length - 1; i > 0; i--) {
|
||||
const currentPath = pathParts.slice(0, i).join('/');
|
||||
const parentParts = pathParts.slice(0, i);
|
||||
const lastPart = parentParts[parentParts.length - 1];
|
||||
|
||||
const possibleParent = indexFiles.find((indexFile) => {
|
||||
const indexParts = indexFile.slug.split('/');
|
||||
const indexFolderPath = indexParts.slice(0, -1).join('/');
|
||||
if (!lastPart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return indexFolderPath === currentPath;
|
||||
});
|
||||
const possibleIndexParent = parentParts.concat(lastPart).join('/');
|
||||
|
||||
if (possibleParent) {
|
||||
return possibleParent.slug;
|
||||
if (slugSet.has(possibleIndexParent)) {
|
||||
parentCache.set(path, possibleIndexParent);
|
||||
return possibleIndexParent;
|
||||
}
|
||||
|
||||
const regularParent = parentParts.join('/');
|
||||
|
||||
if (slugSet.has(regularParent)) {
|
||||
parentCache.set(path, regularParent);
|
||||
return regularParent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
result.forEach((item) => {
|
||||
// never override the parent if it's already set in the config
|
||||
if (item.entry.parent) {
|
||||
return;
|
||||
parentCache.set(path, null);
|
||||
return null;
|
||||
};
|
||||
|
||||
const results = new Array(items.length) as typeof items;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isIndexFile(item.slug)) {
|
||||
item.entry.parent = null;
|
||||
results[i] = item;
|
||||
continue;
|
||||
}
|
||||
|
||||
const pathParts = item.slug.split('/');
|
||||
const parentParts = pathParts.slice(0, -1);
|
||||
const lastPart = parentParts[parentParts.length - 1]!;
|
||||
const possibleIndexParent = parentParts.concat(lastPart).join('/');
|
||||
|
||||
// Skip if this is a root level index file (e.g., "authentication/authentication")
|
||||
if (pathParts.length === 2 && pathParts[0] === pathParts[1]) {
|
||||
item.entry.parent = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if current item is an index file
|
||||
const isIndexFile =
|
||||
pathParts[pathParts.length - 1] === pathParts[pathParts.length - 2];
|
||||
|
||||
if (isIndexFile) {
|
||||
// For index files, find parent in the level above
|
||||
const parentPath = pathParts.slice(0, -2);
|
||||
if (parentPath.length > 0) {
|
||||
item.entry.parent = findParentIndex(
|
||||
parentPath.concat(parentPath[parentPath.length - 1]!),
|
||||
);
|
||||
} else {
|
||||
item.entry.parent = null;
|
||||
}
|
||||
if (slugSet.has(possibleIndexParent)) {
|
||||
item.entry.parent = possibleIndexParent;
|
||||
} else {
|
||||
// For regular files, find parent in the current folder
|
||||
item.entry.parent = findParentIndex(pathParts);
|
||||
const regularParent = parentParts.join('/');
|
||||
if (slugSet.has(regularParent)) {
|
||||
item.entry.parent = regularParent;
|
||||
} else {
|
||||
item.entry.parent = findClosestValidParent(pathParts);
|
||||
}
|
||||
}
|
||||
});
|
||||
results[i] = item;
|
||||
}
|
||||
|
||||
return result;
|
||||
return results;
|
||||
}
|
||||
|
||||
const itemsWithParents = processItems(filtered);
|
||||
@@ -156,7 +181,25 @@ class KeystaticClient implements CmsClient {
|
||||
.sort((a, b) => {
|
||||
return (a.entry.order ?? 0) - (b.entry.order ?? 0);
|
||||
})
|
||||
.map((item) => this.mapPost(item)),
|
||||
.map((item) => {
|
||||
if (collection === 'documentation') {
|
||||
return this.mapDocumentationPost(
|
||||
item as {
|
||||
entry: DocumentationEntryProps;
|
||||
slug: string;
|
||||
},
|
||||
{ fetchContent },
|
||||
);
|
||||
}
|
||||
|
||||
return this.mapPost(
|
||||
item as {
|
||||
entry: PostEntryProps;
|
||||
slug: string;
|
||||
},
|
||||
{ fetchContent },
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -192,13 +235,7 @@ class KeystaticClient implements CmsClient {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const allPosts = await reader.collections[collection].all();
|
||||
|
||||
const children = allPosts.filter(
|
||||
(item) => item.entry.parent === params.slug,
|
||||
);
|
||||
|
||||
return this.mapPost({ entry: doc, slug: params.slug }, children);
|
||||
return this.mapPost({ entry: doc as PostEntryProps, slug: params.slug });
|
||||
}
|
||||
|
||||
async getCategories() {
|
||||
@@ -217,18 +254,79 @@ class KeystaticClient implements CmsClient {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private async mapPost<
|
||||
private async mapDocumentationPost<
|
||||
Type extends {
|
||||
entry: PostEntryProps;
|
||||
entry: DocumentationEntryProps;
|
||||
slug: string;
|
||||
},
|
||||
>(item: Type, children: Type[] = []): Promise<Cms.ContentItem> {
|
||||
>(
|
||||
item: Type,
|
||||
params: {
|
||||
fetchContent: boolean;
|
||||
} = {
|
||||
fetchContent: true,
|
||||
},
|
||||
): Promise<Cms.ContentItem> {
|
||||
const publishedAt = item.entry.publishedAt
|
||||
? new Date(item.entry.publishedAt)
|
||||
: new Date();
|
||||
|
||||
const content = await item.entry.content();
|
||||
const html = await renderMarkdoc(content.node);
|
||||
const html = params.fetchContent ? await renderMarkdoc(content.node) : [];
|
||||
|
||||
return {
|
||||
id: item.slug,
|
||||
title: item.entry.title,
|
||||
label: item.entry.label,
|
||||
url: item.slug,
|
||||
slug: item.slug,
|
||||
description: item.entry.description,
|
||||
publishedAt: publishedAt.toISOString(),
|
||||
content: html as string,
|
||||
image: item.entry.image ?? undefined,
|
||||
status: item.entry.status,
|
||||
collapsible: item.entry.collapsible,
|
||||
collapsed: item.entry.collapsed,
|
||||
categories:
|
||||
(item.entry.categories ?? []).map((item) => {
|
||||
return {
|
||||
id: item,
|
||||
name: item,
|
||||
slug: item,
|
||||
};
|
||||
}) ?? [],
|
||||
tags: (item.entry.tags ?? []).map((item) => {
|
||||
return {
|
||||
id: item,
|
||||
name: item,
|
||||
slug: item,
|
||||
};
|
||||
}),
|
||||
parentId: item.entry.parent ?? undefined,
|
||||
order: item.entry.order ?? 1,
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
|
||||
private async mapPost<
|
||||
Type extends {
|
||||
entry: PostEntryProps;
|
||||
slug: string;
|
||||
},
|
||||
>(
|
||||
item: Type,
|
||||
params: {
|
||||
fetchContent: boolean;
|
||||
} = {
|
||||
fetchContent: true,
|
||||
},
|
||||
): Promise<Cms.ContentItem> {
|
||||
const publishedAt = item.entry.publishedAt
|
||||
? new Date(item.entry.publishedAt)
|
||||
: new Date();
|
||||
|
||||
const content = await item.entry.content();
|
||||
const html = params.fetchContent ? await renderMarkdoc(content.node) : [];
|
||||
|
||||
return {
|
||||
id: item.slug,
|
||||
@@ -242,14 +340,14 @@ class KeystaticClient implements CmsClient {
|
||||
image: item.entry.image ?? undefined,
|
||||
status: item.entry.status,
|
||||
categories:
|
||||
item.entry.categories.map((item) => {
|
||||
(item.entry.categories ?? []).map((item) => {
|
||||
return {
|
||||
id: item,
|
||||
name: item,
|
||||
slug: item,
|
||||
};
|
||||
}) ?? [],
|
||||
tags: item.entry.tags.map((item) => {
|
||||
tags: (item.entry.tags ?? []).map((item) => {
|
||||
return {
|
||||
id: item,
|
||||
name: item,
|
||||
@@ -258,9 +356,7 @@ class KeystaticClient implements CmsClient {
|
||||
}),
|
||||
parentId: item.entry.parent ?? undefined,
|
||||
order: item.entry.order ?? 1,
|
||||
children: await Promise.all(
|
||||
children.map((child) => this.mapPost(child, [])),
|
||||
),
|
||||
children: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ export type PostEntryProps = Entry<
|
||||
(typeof keyStaticConfig)['collections']['posts']
|
||||
>;
|
||||
|
||||
export type DocumentationEntryProps = Entry<
|
||||
(typeof keyStaticConfig)['collections']['documentation']
|
||||
>;
|
||||
|
||||
function createKeyStaticConfig(path = '') {
|
||||
if (path && !path.endsWith('/')) {
|
||||
path += '/';
|
||||
@@ -135,6 +139,14 @@ function getKeystaticCollections(path: string) {
|
||||
{ label: 'Pending', value: 'pending' },
|
||||
],
|
||||
}),
|
||||
collapsible: fields.checkbox({
|
||||
label: 'Collapsible',
|
||||
defaultValue: false,
|
||||
}),
|
||||
collapsed: fields.checkbox({
|
||||
label: 'Collapsed',
|
||||
defaultValue: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@ export namespace Cms {
|
||||
order: number;
|
||||
children: ContentItem[];
|
||||
parentId: string | undefined;
|
||||
collapsible?: boolean;
|
||||
collapsed?: boolean;
|
||||
}
|
||||
|
||||
export type ContentItemStatus = 'draft' | 'published' | 'review' | 'pending';
|
||||
@@ -38,6 +40,7 @@ export namespace Cms {
|
||||
offset?: number;
|
||||
categories?: string[];
|
||||
tags?: string[];
|
||||
content?: boolean;
|
||||
parentIds?: string[];
|
||||
language?: string | undefined;
|
||||
sortDirection?: 'asc' | 'desc';
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/team-accounts": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-email/components": "0.0.29"
|
||||
"@react-email/components": "0.0.30"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
@@ -43,9 +43,9 @@
|
||||
"next-themes": "0.4.4",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"sonner": "^1.7.0",
|
||||
"sonner": "^1.7.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
|
||||
@@ -148,7 +148,7 @@ export function PersonalAccountDropdown({
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
className={'s-full flex items-center space-x-2'}
|
||||
className={'s-full flex items-center space-x-2 cursor-pointer'}
|
||||
href={paths.home}
|
||||
>
|
||||
<Home className={'h-5'} />
|
||||
@@ -162,7 +162,7 @@ export function PersonalAccountDropdown({
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link className={'s-full flex items-center space-x-2'} href={'/docs'}>
|
||||
<Link className={'s-full flex items-center space-x-2 cursor-pointer'} href={'/docs'}>
|
||||
<MessageCircleQuestion className={'h-5'} />
|
||||
|
||||
<span>
|
||||
@@ -176,7 +176,7 @@ export function PersonalAccountDropdown({
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
className={'s-full flex items-center space-x-2'}
|
||||
className={'s-full flex items-center space-x-2 cursor-pointer'}
|
||||
href={'/admin'}
|
||||
>
|
||||
<Shield className={'h-5'} />
|
||||
|
||||
@@ -21,15 +21,15 @@
|
||||
"@kit/ui": "workspace:*",
|
||||
"@makerkit/data-loader-supabase-core": "^0.0.8",
|
||||
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"next": "15.0.4",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"exports": {
|
||||
|
||||
@@ -28,14 +28,14 @@
|
||||
"@kit/ui": "workspace:*",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"next": "15.0.4",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"sonner": "^1.7.0",
|
||||
"sonner": "^1.7.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"lucide-react": "^0.468.0",
|
||||
"react": "19.0.0",
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||
@@ -43,9 +43,9 @@
|
||||
"next": "15.0.4",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"sonner": "^1.7.0",
|
||||
"sonner": "^1.7.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@kit/shared": "workspace:*",
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"next": "15.0.4",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"i18next": "24.0.5",
|
||||
"i18next-browser-languagedetector": "8.0.0",
|
||||
"i18next-browser-languagedetector": "8.0.1",
|
||||
"i18next-resources-to-backend": "^1.2.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@kit/supabase": "workspace:*",
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"next": "15.0.4",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@supabase/ssr": "^0.5.2",
|
||||
"@supabase/supabase-js": "^2.47.1",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@supabase/supabase-js": "^2.47.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"next": "15.0.4",
|
||||
"react": "19.0.0",
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"@kit/tailwind-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@tanstack/react-query": "5.62.2",
|
||||
"@tanstack/react-query": "5.62.3",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@types/react": "npm:types-react@19.0.0-rc.1",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
|
||||
@@ -54,9 +54,9 @@
|
||||
"next-themes": "0.4.4",
|
||||
"prettier": "^3.4.2",
|
||||
"react-day-picker": "^8.10.1",
|
||||
"react-hook-form": "^7.53.2",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-i18next": "^15.1.3",
|
||||
"sonner": "^1.7.0",
|
||||
"sonner": "^1.7.1",
|
||||
"tailwindcss": "3.4.16",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.7.2",
|
||||
@@ -106,6 +106,7 @@
|
||||
"./chart": "./src/shadcn/chart.tsx",
|
||||
"./skeleton": "./src/shadcn/skeleton.tsx",
|
||||
"./shadcn-sidebar": "./src/shadcn/sidebar.tsx",
|
||||
"./collapsible": "./src/shadcn/collapsible.tsx",
|
||||
"./utils": "./src/lib/utils/index.ts",
|
||||
"./if": "./src/makerkit/if.tsx",
|
||||
"./trans": "./src/makerkit/trans.tsx",
|
||||
|
||||
@@ -30,7 +30,7 @@ export const FeatureCard = forwardRef<HTMLDivElement, FeatureCardProps>(
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl font-semibold">{label}</CardTitle>
|
||||
<CardDescription className="max-w-xs text-sm font-semibold tracking-tight text-current">
|
||||
<CardDescription className="max-w-xs text-sm font-semibold tracking-tight text-muted-foreground">
|
||||
{description}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -10,7 +10,7 @@ export const FeatureGrid = forwardRef<
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'grid w-full grid-cols-1 gap-6 space-y-0 lg:grid-cols-3',
|
||||
'grid w-full grid-cols-1 gap-4 space-y-0 lg:grid-cols-3',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -20,7 +20,7 @@ export const FeatureShowcase = forwardRef<HTMLDivElement, FeatureShowcaseProps>(
|
||||
>
|
||||
<div className="flex w-full max-w-5xl flex-col space-y-4">
|
||||
{icon && <div className="flex">{icon}</div>}
|
||||
<h3 className="text-3xl font-normal tracking-tighter xl:text-5xl">
|
||||
<h3 className="text-3xl font-normal tracking-tight xl:text-5xl">
|
||||
{heading}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ export const Header = forwardRef<HTMLDivElement, HeaderProps>(
|
||||
>
|
||||
<div className="container">
|
||||
<div className="grid h-14 grid-cols-3 items-center">
|
||||
<div>{logo}</div>
|
||||
<div className={'mx-auto lg:mx-0'}>{logo}</div>
|
||||
<div className="order-first md:order-none">{navigation}</div>
|
||||
<div className="flex items-center justify-end space-x-1">
|
||||
{actions}
|
||||
|
||||
@@ -16,7 +16,7 @@ export const HeroTitle = forwardRef<
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'hero-title flex flex-col space-y-1 text-center font-sans text-4xl font-semibold tracking-tighter dark:text-white sm:text-6xl lg:max-w-5xl lg:text-7xl xl:text-[5.125rem]',
|
||||
'hero-title flex flex-col text-center font-sans text-4xl font-semibold tracking-tighter dark:text-white sm:text-6xl lg:max-w-5xl lg:text-7xl xl:text-[4.85rem]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Heading } from '../../shadcn/heading';
|
||||
import { HeroTitle } from './hero-title';
|
||||
|
||||
interface HeroProps {
|
||||
@@ -53,12 +52,11 @@ export function Hero({
|
||||
|
||||
{subtitle && (
|
||||
<div className="flex max-w-2xl flex-col space-y-1">
|
||||
<Heading
|
||||
level={3}
|
||||
className="p-0 text-center font-sans text-base font-normal"
|
||||
<h3
|
||||
className="p-0 text-center text-xl font-sans font-normal text-muted-foreground tracking-tight"
|
||||
>
|
||||
{subtitle}
|
||||
</Heading>
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -78,8 +76,11 @@ export function Hero({
|
||||
|
||||
{image && (
|
||||
<div
|
||||
className={cn('mx-auto flex max-w-[85rem] justify-center py-8', {
|
||||
['delay-300 duration-1000 animate-in fade-in zoom-in-95 slide-in-from-top-32 fill-mode-both']:
|
||||
style={{
|
||||
MozAnimationDuration: '100ms',
|
||||
}}
|
||||
className={cn('mx-auto container flex justify-center py-8', {
|
||||
['delay-1000 duration-1000 animate-in fade-in zoom-in-95 slide-in-from-top-32 fill-mode-both']:
|
||||
animate,
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { forwardRef } from 'react';
|
||||
import { Slot, Slottable } from '@radix-ui/react-slot';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
import { GradientSecondaryText } from './gradient-secondary-text';
|
||||
|
||||
export const Pill = forwardRef<
|
||||
HTMLHeadingElement,
|
||||
@@ -33,7 +32,7 @@ export const Pill = forwardRef<
|
||||
</span>
|
||||
)}
|
||||
<Slottable>
|
||||
<GradientSecondaryText>{props.children}</GradientSecondaryText>
|
||||
<span className={'text-secondary-foreground'}>{props.children}</span>
|
||||
</Slottable>
|
||||
</Comp>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ export const SecondaryHero = forwardRef<HTMLDivElement, SecondaryHeroProps>(
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-col items-center space-y-4 text-center',
|
||||
'flex flex-col items-center space-y-6 text-center',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -30,12 +30,11 @@ export const SecondaryHero = forwardRef<HTMLDivElement, SecondaryHeroProps>(
|
||||
{heading}
|
||||
</Heading>
|
||||
|
||||
<Heading
|
||||
level={3}
|
||||
className="font-sans font-normal tracking-tight text-muted-foreground"
|
||||
<h3
|
||||
className="font-sans font-normal text-xl tracking-tight text-muted-foreground"
|
||||
>
|
||||
{subheading}
|
||||
</Heading>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
|
||||
@@ -74,7 +74,7 @@ export function SubMenuModeToggle() {
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
className={cn('flex items-center space-x-2', {
|
||||
className={cn('flex items-center space-x-2 cursor-pointer', {
|
||||
'bg-muted': isSelected,
|
||||
})}
|
||||
key={mode}
|
||||
|
||||
Reference in New Issue
Block a user