| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- "use client";
- import { useState } from "react";
- import { Badge } from "@/components/ui/badge";
- import { examples, allTags, getGitHubUrl, type Example } from "@/lib/examples";
- import { cn } from "@/lib/utils";
- function ExampleCard({ example }: { example: Example }) {
- return (
- <div className="group flex flex-col rounded-xl border border-border bg-card text-card-foreground overflow-hidden transition-colors hover:border-foreground/25">
- <div className="flex flex-1 flex-col gap-3 p-5">
- <h3 className="font-semibold leading-none">{example.title}</h3>
- <p className="text-sm text-muted-foreground leading-relaxed">
- {example.description}
- </p>
- <div className="flex flex-wrap gap-1.5">
- {example.tags.map((tag) => (
- <Badge key={tag} variant="secondary" className="text-[11px]">
- {tag}
- </Badge>
- ))}
- </div>
- <div className="mt-auto flex items-center gap-3 pt-2">
- {example.demoUrl && (
- <a
- href={example.demoUrl}
- target="_blank"
- rel="noopener noreferrer"
- className="inline-flex items-center gap-1.5 text-sm text-foreground hover:text-primary transition-colors"
- >
- <svg
- xmlns="http://www.w3.org/2000/svg"
- width="14"
- height="14"
- viewBox="0 0 24 24"
- fill="none"
- stroke="currentColor"
- strokeWidth="2"
- strokeLinecap="round"
- strokeLinejoin="round"
- >
- <path d="M15 3h6v6" />
- <path d="M10 14 21 3" />
- <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
- </svg>
- Live Demo
- </a>
- )}
- <a
- href={getGitHubUrl(example)}
- target="_blank"
- rel="noopener noreferrer"
- className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
- >
- <svg
- viewBox="0 0 16 16"
- className="h-3.5 w-3.5"
- fill="currentColor"
- aria-hidden="true"
- >
- <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
- </svg>
- Source
- </a>
- </div>
- </div>
- </div>
- );
- }
- export default function ExamplesPage() {
- const [activeTag, setActiveTag] = useState<string | null>(null);
- const filtered = activeTag
- ? examples.filter((e) => e.tags.includes(activeTag))
- : examples;
- return (
- <section className="mx-auto max-w-6xl px-6 py-16">
- <div className="mb-10">
- <h1 className="text-3xl font-bold tracking-tight sm:text-4xl">
- Examples
- </h1>
- <p className="mt-3 text-lg text-muted-foreground">
- Explore json-render across frameworks, renderers, and use cases.
- </p>
- </div>
- <div className="mb-8 flex flex-wrap gap-2">
- <button
- onClick={() => setActiveTag(null)}
- className={cn(
- "rounded-full border px-3 py-1 text-xs font-medium transition-colors",
- activeTag === null
- ? "border-foreground bg-foreground text-background"
- : "border-border text-muted-foreground hover:text-foreground hover:border-foreground/50",
- )}
- >
- All
- </button>
- {allTags.map((tag) => (
- <button
- key={tag}
- onClick={() => setActiveTag(activeTag === tag ? null : tag)}
- className={cn(
- "rounded-full border px-3 py-1 text-xs font-medium transition-colors",
- activeTag === tag
- ? "border-foreground bg-foreground text-background"
- : "border-border text-muted-foreground hover:text-foreground hover:border-foreground/50",
- )}
- >
- {tag}
- </button>
- ))}
- </div>
- <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
- {filtered.map((example) => (
- <ExampleCard key={example.slug} example={example} />
- ))}
- </div>
- {filtered.length === 0 && (
- <p className="py-12 text-center text-muted-foreground">
- No examples match the selected filter.
- </p>
- )}
- </section>
- );
- }
|