mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
edge functions cards
This commit is contained in:
1 parent
70b0d66ccf
commit
1a0304f324
5 files changed
+89
-105
No files matched your search
@@ -67,11 +67,11 @@ const LEVELS_BG = [
|
||||
]
|
||||
|
||||
const LEVELS_TEXT = [
|
||||
'hsl(var(--brand-400))',
|
||||
'hsl(var(--brand-500))',
|
||||
'hsl(var(--brand-600))',
|
||||
'hsl(var(--brand-default))',
|
||||
'hsl(var(--brand-default))',
|
||||
'hsl(var(--brand-600))',
|
||||
'hsl(var(--brand-500))',
|
||||
]
|
||||
|
||||
function makeGrid(): { level: number; isText: boolean }[][] {
|
||||
|
||||
@@ -36,74 +36,64 @@ export function ExamplesSection() {
|
||||
|
||||
{/* Cards */}
|
||||
<div className="mx-auto max-w-[var(--container-max-w,75rem)] px-6 w-full">
|
||||
<div className="border border-border rounded-md overflow-clip">
|
||||
{/* Featured row - 2 large cells */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 border-b border-border">
|
||||
{featuredExamples.map((example, i) => {
|
||||
const tag = example.tags?.[0]
|
||||
const icon = tag ? EXAMPLE_ICON[tag] : undefined
|
||||
return (
|
||||
<Link
|
||||
key={example.title}
|
||||
href={example.repo_url ?? '#'}
|
||||
target="_blank"
|
||||
className={`group flex flex-col px-6 py-8 hover:bg-surface-75/50 transition-colors ${
|
||||
i < featuredExamples.length - 1
|
||||
? 'border-b md:border-b-0 md:border-r border-border'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
{icon && (
|
||||
<img
|
||||
src={icon.src}
|
||||
alt=""
|
||||
className={`${icon.className} object-contain${icon.invert ? ' dark:invert' : ''}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="text-foreground text-sm font-medium">{example.title}</h4>
|
||||
<p className="text-foreground-lighter text-sm mt-1 line-clamp-2">
|
||||
{example.description}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* Featured row - 2 large cells */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{featuredExamples.map((example) => {
|
||||
const tag = example.tags?.[0]
|
||||
const icon = tag ? EXAMPLE_ICON[tag] : undefined
|
||||
return (
|
||||
<Link
|
||||
key={example.title}
|
||||
href={example.repo_url ?? '#'}
|
||||
target="_blank"
|
||||
className="group flex flex-col bg-surface-75 border border-border rounded-lg px-6 py-8 "
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
{icon && (
|
||||
<img
|
||||
src={icon.src}
|
||||
alt=""
|
||||
className={`${icon.className} object-contain${icon.invert ? ' dark:invert' : ''}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="text-foreground text-sm font-medium">{example.title}</h4>
|
||||
<p className="text-foreground-lighter text-sm mt-1 line-clamp-2">
|
||||
{example.description}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Grid row - 4 smaller cells */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{gridExamples.map((example, i) => {
|
||||
const tag = example.tags?.[0]
|
||||
const icon = tag ? EXAMPLE_ICON[tag] : undefined
|
||||
return (
|
||||
<Link
|
||||
key={example.title}
|
||||
href={example.repo_url ?? '#'}
|
||||
target="_blank"
|
||||
className={`group flex flex-col px-6 py-8 hover:bg-surface-75/50 transition-colors ${
|
||||
i < gridExamples.length - 1
|
||||
? 'border-b sm:border-b-0 sm:border-r border-border'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
{icon && (
|
||||
<img
|
||||
src={icon.src}
|
||||
alt=""
|
||||
className={`${icon.className} object-contain${icon.invert ? ' dark:invert' : ''}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="text-foreground text-sm font-medium">{example.title}</h4>
|
||||
<p className="text-foreground-lighter text-sm mt-1 line-clamp-2">
|
||||
{example.description}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* Grid row - 4 smaller cells */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mt-3">
|
||||
{gridExamples.map((example) => {
|
||||
const tag = example.tags?.[0]
|
||||
const icon = tag ? EXAMPLE_ICON[tag] : undefined
|
||||
return (
|
||||
<Link
|
||||
key={example.title}
|
||||
href={example.repo_url ?? '#'}
|
||||
target="_blank"
|
||||
className="group flex flex-col bg-surface-75 border border-border rounded-lg px-6 py-10 "
|
||||
>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
{icon && (
|
||||
<img
|
||||
src={icon.src}
|
||||
alt=""
|
||||
className={`${icon.className} object-contain${icon.invert ? ' dark:invert' : ''}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<h4 className="text-foreground text-sm font-medium">{example.title}</h4>
|
||||
<p className="text-foreground-lighter text-sm mt-1 line-clamp-2">
|
||||
{example.description}
|
||||
</p>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
const LocalDXImage = dynamic(() => import('~/components/Products/Functions/LocalDXImage'))
|
||||
const ParityImage = dynamic(() => import('~/components/Products/Functions/ParityImage'))
|
||||
@@ -15,14 +14,14 @@ function EcosystemCard() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-col border-b border-border', 'col-span-1 md:row-span-2 !border-b-0')}
|
||||
className="flex flex-col bg-surface-75 border border-border rounded-lg overflow-hidden col-span-1 md:row-span-2"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="flex-1 relative overflow-hidden min-h-[320px] md:min-h-0">
|
||||
<NpmEcosystem isHovered={isHovered} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-6 py-5 border-t border-border bg-surface-200">
|
||||
<div className="flex flex-col gap-1 px-6 py-5">
|
||||
<h4 className="text-foreground text-sm font-medium">Use any NPM module</h4>
|
||||
<p className="text-foreground-lighter text-sm">
|
||||
Tap into the 2+ million modules in the Deno and NPM ecosystem
|
||||
@@ -32,25 +31,19 @@ function EcosystemCard() {
|
||||
)
|
||||
}
|
||||
|
||||
const GRID_CLASS: Record<string, string> = {
|
||||
localDX: 'col-span-1 md:border-r',
|
||||
parity: 'col-span-1 xl:border-r',
|
||||
ci: 'col-span-1 md:col-span-2 xl:border-r !border-b-0',
|
||||
}
|
||||
|
||||
function ParityCard() {
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-col border-b border-border', GRID_CLASS['parity'])}
|
||||
className="flex flex-col bg-surface-75 border border-border rounded-lg overflow-hidden "
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="aspect-[3/2] w-full flex items-center justify-center overflow-hidden">
|
||||
<ParityImage isHovered={isHovered} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-6 py-5 border-t border-border bg-surface-200">
|
||||
<div className="flex flex-col gap-1 px-6 py-5">
|
||||
<h4 className="text-foreground text-sm font-medium">Dev and Prod parity</h4>
|
||||
<p className="text-foreground-lighter text-sm">
|
||||
The open source{' '}
|
||||
@@ -73,14 +66,20 @@ function CICard() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-col border-b border-border', GRID_CLASS['ci'])}
|
||||
className="flex flex-col bg-surface-75 border border-border rounded-lg overflow-hidden md:col-span-2"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="flex-1 flex items-center justify-center overflow-hidden pl-2 pt-8 md:pt-12">
|
||||
<div
|
||||
className="flex-1 flex items-center justify-center overflow-hidden pl-2 pt-8 md:pt-12"
|
||||
style={{
|
||||
maskImage: 'linear-gradient(to bottom, black 95%, transparent 100%)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, black 95%, transparent 100%)',
|
||||
}}
|
||||
>
|
||||
<CI isHovered={isHovered} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-6 py-5 border-t border-border bg-surface-200">
|
||||
<div className="flex flex-col gap-1 px-6 py-5">
|
||||
<h4 className="text-foreground text-sm font-medium">Continuous Integration</h4>
|
||||
<p className="text-foreground-lighter text-sm">
|
||||
Use the{' '}
|
||||
@@ -103,14 +102,14 @@ function LocalDXCard() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-col border-b border-border', GRID_CLASS['localDX'])}
|
||||
className="flex flex-col bg-surface-75 border border-border rounded-lg overflow-hidden "
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="aspect-[3/2] w-full flex items-center justify-center overflow-hidden">
|
||||
<LocalDXImage isHovered={isHovered} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 px-6 py-5 border-t border-border bg-surface-200">
|
||||
<div className="flex flex-col gap-1 px-6 py-5">
|
||||
<h4 className="text-foreground text-sm font-medium">First-class local dev experience</h4>
|
||||
<p className="text-foreground-lighter text-sm">
|
||||
Write code with hot code reloading, a fast Language server for autocompletion, type
|
||||
@@ -148,13 +147,11 @@ export function LocalDXSection() {
|
||||
|
||||
{/* Bento grid */}
|
||||
<div className="mx-auto max-w-[var(--container-max-w,75rem)] px-6 w-full">
|
||||
<div className="border border-border rounded-md overflow-clip">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 grid-flow-dense">
|
||||
<LocalDXCard />
|
||||
<ParityCard />
|
||||
<CICard />
|
||||
<EcosystemCard />
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 grid-flow-dense gap-3">
|
||||
<LocalDXCard />
|
||||
<ParityCard />
|
||||
<CICard />
|
||||
<EcosystemCard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,12 +40,12 @@ function ObservabilityCard({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('flex flex-col', className)}
|
||||
className={cn('flex flex-col bg-surface-75 border border-border rounded-lg overflow-hidden ', className)}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="relative flex-1 min-h-[285px] overflow-hidden">{card.render(isHovered)}</div>
|
||||
<div className="flex flex-col gap-1 px-6 py-5 border-t border-border bg-surface-200">
|
||||
<div className="flex flex-col gap-1 px-6 py-5">
|
||||
<h4 className="text-foreground text-sm font-medium">{card.label}</h4>
|
||||
<p className="text-foreground-lighter text-sm">{card.paragraph}</p>
|
||||
</div>
|
||||
@@ -72,16 +72,13 @@ export function ObservabilitySection() {
|
||||
|
||||
{/* 3-column grid */}
|
||||
<div className="mx-auto max-w-[var(--container-max-w,75rem)] px-6 w-full">
|
||||
<div className="border border-border rounded-md overflow-clip">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3">
|
||||
{cards.map((card, i) => (
|
||||
<ObservabilityCard
|
||||
key={card.id}
|
||||
card={card}
|
||||
className={cn(i < cards.length - 1 && 'md:border-r border-border')}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
||||
{cards.map((card) => (
|
||||
<ObservabilityCard
|
||||
key={card.id}
|
||||
card={card}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -199,7 +199,7 @@ const NpmEcosystem = ({ isHovered = false }: { isHovered?: boolean }) => {
|
||||
<div
|
||||
className="absolute inset-0 z-10"
|
||||
style={{
|
||||
background: `radial-gradient(70% 50% at 50% 50%, transparent, hsl(var(--background-default)))`,
|
||||
background: `radial-gradient(70% 50% at 50% 50%, transparent, hsl(var(--background-surface-75)))`,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
Reference in new issue
Block a user