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
1 change: 1 addition & 0 deletions examples/advanced-blog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-slot": "^1.0.2",
"@vercel/og": "^0.6.1",
"change-case": "^5.4.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
Expand Down
11 changes: 7 additions & 4 deletions examples/advanced-blog/src/app/(web)/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import DocHero from '@/components/doc-hero'
import MDXComponent from '@/components/mdx/mdx-component'
import MDXServer from '@/lib/mdx-server'
import { absoluteUrl, ogUrl } from '@/lib/utils'
import { sentenceCase } from 'change-case'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import { OstDocument } from 'outstatic'
Expand All @@ -23,8 +24,8 @@ export async function generateMetadata(params: Params): Promise<Metadata> {

if (!doc) {
return {
title: `All ${moreDocs.collection}`,
description: `All ${moreDocs.collection}`
title: `All ${sentenceCase(moreDocs.collection)}`,
description: `All ${sentenceCase(moreDocs.collection)}`
}
}

Expand Down Expand Up @@ -59,13 +60,15 @@ export default async function Document(params: Params) {

if (!doc) {
const { docs, collection } = moreDocs

return (
<div className="pt-24 mb-16 animate-fade-up opacity-0">
<div className="md:pt-24 mb-16 animate-fade-up opacity-0">
{docs.length > 0 && (
<ContentGrid
title={`All ${collection}`}
items={docs}
collection={collection}
search
/>
)}
</div>
Expand All @@ -74,7 +77,7 @@ export default async function Document(params: Params) {

if (doc.collection === 'pages') {
return (
<article className="mb-32 py-24">
<article className="mb-32 md:py-24">
<div className="prose md:prose-xl prose-outstatic animate-fade-up opacity-0">
<MDXComponent content={doc.content} />
</div>
Expand Down
43 changes: 40 additions & 3 deletions examples/advanced-blog/src/components/content-grid.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
"use client";

import { useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";
import { ArrowRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import type { OstDocument } from "outstatic";
import Search from "./search";
import metadb from "@/../outstatic/content/metadata.json";
import { Suspense } from "react";

type Item = {
tags?: { value: string; label: string }[];
Expand All @@ -11,9 +17,10 @@ type Item = {
type Props = {
collection: string;
title?: string;
items: Item[];
items: Item[] | typeof metadb.metadata;
priority?: boolean;
viewAll?: boolean;
search?: boolean;
};

const ContentGrid = ({
Expand All @@ -22,7 +29,26 @@ const ContentGrid = ({
collection,
priority = false,
viewAll = false,
search = false,
}: Props) => {
const searchParams = useSearchParams();

const searchQuery = searchParams.get("q")?.toLowerCase();
const posts = metadb.metadata;
const searchResults: typeof metadb.metadata = [];

if (searchQuery) {
posts.forEach((post) => {
if (post.status === "published" && post.collection === collection) {
if (
post?.title?.toLowerCase().includes(searchQuery) ||
post?.description?.toLowerCase().includes(searchQuery)
) {
searchResults.push(post);
}
}
});
}
return (
<section id={collection} className="mb-24">
<div className="flex gap-4 md:gap-6 items-end">
Expand All @@ -37,8 +63,11 @@ const ContentGrid = ({
</Button>
) : null}
</div>

<div className="my-8">{search ? <Search /> : null}</div>

<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 sm:gap-x-6 lg:gap-x-8 gap-y-5 sm:gap-y-6 lg:gap-y-8 mt-4 md:mt-8">
{items.map((item, id) => (
{(searchResults.length > 0 ? searchResults : items).map((item, id) => (
<Link key={item.slug} href={`/${collection}/${item.slug}`}>
<div className="cursor-pointer border rounded-md md:w-full scale-100 hover:scale-[1.02] active:scale-[0.97] motion-safe:transform-gpu transition duration-100 motion-reduce:hover:scale-100 hover:shadow overflow-hidden h-full">
<Image
Expand Down Expand Up @@ -86,4 +115,12 @@ const ContentGrid = ({
);
};

export default ContentGrid;
function ContentGridWrapper(props: Props) {
return (
<Suspense>
<ContentGrid {...props} />
</Suspense>
);
}

export default ContentGridWrapper;
61 changes: 31 additions & 30 deletions examples/advanced-blog/src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import Link from "next/link";
import { getCollections, load } from "outstatic/server";
import { MobileMenu } from "./mobile-menu";
import { ThemeToggle } from "./theme-toggle";
import { buttonVariants } from "./ui/button";
import Link from 'next/link'
import { getCollections, load } from 'outstatic/server'
import { MobileMenu } from './mobile-menu'
import { ThemeToggle } from './theme-toggle'
import { buttonVariants } from './ui/button'
import { sentenceCase } from 'change-case'

export type MenuProps = {
pages: {
title: string;
slug: string;
}[];
collections: string[];
};
title: string
slug: string
}[]
collections: string[]
}

const Header = async () => {
const data = await getData();
const { pages, collections } = data;
const data = await getData()
const { pages, collections } = data

return (
<header className="py-4 fixed bottom-0 border-t md:bottom-auto md:top-0 w-full z-20 border-b bg-background">
Expand All @@ -31,8 +32,8 @@ const Header = async () => {
<Link
href={`/${slug}`}
className={
buttonVariants({ variant: "ghost", size: "sm" }) +
" capitalize"
buttonVariants({ variant: 'ghost', size: 'sm' }) +
' capitalize'
}
>
{title}
Expand All @@ -44,11 +45,11 @@ const Header = async () => {
<Link
href={`/${collection}`}
className={
buttonVariants({ variant: "ghost", size: "sm" }) +
" capitalize"
buttonVariants({ variant: 'ghost', size: 'sm' }) +
' capitalize'
}
>
{collection}
{sentenceCase(collection)}
</Link>
</li>
))}
Expand All @@ -57,32 +58,32 @@ const Header = async () => {
<MobileMenu pages={pages} collections={collections} />
</nav>
</header>
);
};
)
}

async function getData() {
const db = await load();
const db = await load()

// get all pages
const pages = await db
.find(
{
collection: "pages",
slug: { $nin: ["home"] },
status: "published",
collection: 'pages',
slug: { $nin: ['home'] },
status: 'published'
},
["title", "slug"]
['title', 'slug']
)
.toArray();
.toArray()

const collections = getCollections().filter(
(collection) => collection !== "pages"
);
(collection) => collection !== 'pages'
)

return {
pages,
collections,
} as MenuProps;
collections
} as MenuProps
}

export default Header;
export default Header
30 changes: 30 additions & 0 deletions examples/advanced-blog/src/components/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { Input } from "./ui/input";

export default function Search() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();

const handleSearch = (term: string) => {
const params = new URLSearchParams(searchParams);
if (term) {
params.set("q", term);
} else {
params.delete("q");
}

replace(`${pathname}?${params.toString()}`);
};

return (
<Input
placeholder="Search..."
onChange={(event) => handleSearch(event.target.value)}
defaultValue={searchParams.get("q"?.toString()) || ""}
/>
);
}
25 changes: 25 additions & 0 deletions examples/advanced-blog/src/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from "react"

import { cn } from "@/lib/utils"

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"

export { Input }
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.1.13",
"turbo": "latest"
}
},
"packageManager": "[email protected]+sha512.73a29afa36a0d092ece5271de5177ecbf8318d454ecd701343131b8ebc0c1a91c487da46ab77c8e596d6acf1461e3594ced4becedf8921b074fbd8653ed7051c"
}
Loading