mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore: www Tabs migration (#47449)
## Problem We're still maintaining the old `Tabs` component which is deprecated. ## Solution Cleanup its usages in `www`: - Remove dead code - Migrate actual tabs to the new Shadcn component - Migrate non tabs to use `Button` instead There should be almost no visual changes (slight background color change on the active button below): - https://zone-www-dot-8z5cao88c-supabase.vercel.app/solutions/agencies (at the bottom) Before: <img width="999" height="412" alt="image" src="https://github.com/user-attachments/assets/97675380-e5fb-4e7e-a39e-93bcc0f2f70f" /> After: <img width="732" height="377" alt="image" src="https://github.com/user-attachments/assets/c4150142-218d-4c32-b8ce-f68f4939706e" /> - https://zone-www-dot-8z5cao88c-supabase.vercel.app/blog/postgres-full-text-search-vs-the-rest#results <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Simplified navigation in API examples and chart tabs for a more consistent browsing experience. * Updated chart tab switching to better match the current UI behavior. * **Refactor** * Removed several carousel and admin access components, along with related styling, reducing unused page elements. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
e92581a3c7
commit
d7543d536f
6 files changed
+28
-390
No files matched your search
@@ -1,18 +0,0 @@
|
||||
import React from 'react'
|
||||
import ImageCarousel from '~/components/Carousels/ImageCarousel'
|
||||
import SectionContainer from '~/components/Layouts/SectionContainer'
|
||||
|
||||
import AdminAccessData from 'data/home/admin-access.json'
|
||||
|
||||
const AdminAccess = () => {
|
||||
return (
|
||||
<SectionContainer>
|
||||
<div className="mb-16">
|
||||
<h2 className="h3">Build your app without leaving the dashboard</h2>
|
||||
</div>
|
||||
<ImageCarousel content={AdminAccessData} altTabView={true} />
|
||||
</SectionContainer>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminAccess
|
||||
@@ -1,7 +0,0 @@
|
||||
/* bright */
|
||||
.gradient-bg {
|
||||
background:
|
||||
linear-gradient(338.96deg, rgba(0, 0, 0, 0.2) -18.99%, rgba(0, 0, 0, 0) 25.34%),
|
||||
linear-gradient(300.08deg, #53ffb1 7.6%, #3ecf8e 64.93%);
|
||||
/* transform: rotate(-89.79deg); */
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
import { CornerRightUp } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useRef, useState } from 'react'
|
||||
import { Button, Tabs } from 'ui'
|
||||
|
||||
// Import Swiper React components
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
|
||||
// Import Swiper styles
|
||||
import 'swiper/css'
|
||||
|
||||
import { useInView } from 'framer-motion'
|
||||
import Image from 'next/image'
|
||||
import TextLink from '../TextLink'
|
||||
import ImageCarouselStyles from './ImageCarousel.module.css'
|
||||
|
||||
interface Content {
|
||||
title: string
|
||||
label?: string
|
||||
img_url?: string
|
||||
text?: string
|
||||
cta?: string
|
||||
url?: string
|
||||
youtube_id?: string
|
||||
}
|
||||
|
||||
interface ImageCarouselProps {
|
||||
content: Content[]
|
||||
footer?: React.ReactNode
|
||||
altTabView?: boolean
|
||||
}
|
||||
|
||||
function ImageCarousel(props: ImageCarouselProps) {
|
||||
const sectionRef = useRef<any>(null)
|
||||
const isInView = useInView(sectionRef, { margin: '75%', once: true })
|
||||
// base path for images
|
||||
const { basePath } = useRouter()
|
||||
|
||||
// store API swiper instance
|
||||
const [imageSwiper, setImageSwiper] = useState(undefined)
|
||||
const [swiperDetails, setSwiperDetails] = useState(undefined)
|
||||
|
||||
const [imageSwiperActiveIndex, setImageSwiperActiveIndex] = useState(0)
|
||||
|
||||
function handleImageSwiperNav(e: number) {
|
||||
setImageSwiperActiveIndex(e)
|
||||
// @ts-ignore
|
||||
imageSwiper.slideTo(e)
|
||||
// @ts-ignore
|
||||
swiperDetails.slideTo(e)
|
||||
}
|
||||
|
||||
const details = (
|
||||
<div className="h-64 bg-white">
|
||||
<p>
|
||||
<span className="mb-8 block text-white">Allow fetch something</span>
|
||||
</p>
|
||||
<p>
|
||||
<p>
|
||||
This would only allow the authenticated user access to a folder that is named after their
|
||||
own account UID. This is useful for things like profile images.
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
<Button variant="outline" size="small" icon={<CornerRightUp />}>
|
||||
View documentation
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12" ref={sectionRef}>
|
||||
<div className="col-span-12 w-full lg:col-span-6">
|
||||
<div className="sbui-tabs--alt col-span-12 lg:col-span-7">
|
||||
<div className={props.altTabView ? 'hidden' : 'block'}>
|
||||
<Tabs
|
||||
scrollable
|
||||
type={props.altTabView ? 'underlined' : 'pills'}
|
||||
// @ts-ignore
|
||||
activeId={imageSwiperActiveIndex.toString()}
|
||||
// @ts-ignore
|
||||
onChange={(id: string) => handleImageSwiperNav(Number(id))}
|
||||
>
|
||||
{props.content.map((content: Content, i) => {
|
||||
return (
|
||||
<Tabs.Panel
|
||||
key={i}
|
||||
label={content.label ? content.label : content.title}
|
||||
id={i.toString()}
|
||||
>
|
||||
<span></span>
|
||||
</Tabs.Panel>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div
|
||||
className={`overflow-hidden rounded-md border border-control bg-border-stronger ${ImageCarouselStyles['gradient-bg']}`}
|
||||
>
|
||||
<Swiper
|
||||
// @ts-ignore
|
||||
onSwiper={setImageSwiper}
|
||||
style={{ zIndex: 0, overflow: 'auto', overflowX: 'hidden' }}
|
||||
initialSlide={0}
|
||||
spaceBetween={0}
|
||||
slidesPerView={1}
|
||||
speed={300}
|
||||
allowTouchMove={false}
|
||||
>
|
||||
{props.content.map((content: Content, i: number) => {
|
||||
return (
|
||||
<SwiperSlide key={i}>
|
||||
{content.img_url && (
|
||||
<Image
|
||||
src={`${basePath}${content.img_url}`}
|
||||
alt={content.title}
|
||||
layout="responsive"
|
||||
width="1460"
|
||||
height="960"
|
||||
/>
|
||||
)}
|
||||
{isInView && content.youtube_id && (
|
||||
<div className="relative w-full" style={{ padding: '56.25% 0 0 0' }}>
|
||||
<iframe
|
||||
title="Demo video showcasing Supabase"
|
||||
className="absolute h-full w-full rounded-b-md"
|
||||
src={`https://www.youtube-nocookie.com/embed/${content.youtube_id}?playlist=${content.youtube_id}&autoplay=1&loop=1&controls=0&modestbranding=1&rel=0&disablekb=1&mute=1&muted=1`}
|
||||
style={{ top: 0, left: 0 }}
|
||||
frameBorder="0"
|
||||
allow="autoplay; modestbranding; encrypted-media"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</SwiperSlide>
|
||||
)
|
||||
})}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 mt-8 lg:col-span-5 lg:col-start-8 lg:mt-0 xl:col-span-5 xl:col-start-8">
|
||||
<div className={`sbui-tabs--underline-alt ${props.altTabView ? 'block' : 'hidden'} mb-3`}>
|
||||
<Tabs
|
||||
scrollable
|
||||
type="underlined"
|
||||
size="small"
|
||||
activeId={imageSwiperActiveIndex.toString()}
|
||||
onChange={(id: string) => handleImageSwiperNav(Number(id))}
|
||||
>
|
||||
{props.content.map((content: Content, i: number) => {
|
||||
return (
|
||||
<Tabs.Panel
|
||||
label={content.label ? content.label : content.title}
|
||||
id={i.toString()}
|
||||
key={i}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
</div>
|
||||
<Swiper
|
||||
// @ts-ignore
|
||||
onSwiper={setSwiperDetails}
|
||||
initialSlide={0}
|
||||
speed={300}
|
||||
allowTouchMove={false}
|
||||
autoHeight={true}
|
||||
>
|
||||
{props.content.map((content, i) => {
|
||||
return (
|
||||
<SwiperSlide key={i} className="py-4">
|
||||
<h4 className="text-foreground mb-4 text-xl">{content.title}</h4>
|
||||
<p className="p text-base">{content.text}</p>
|
||||
{!!content.url && (
|
||||
<TextLink
|
||||
label={content.cta ? content.cta : 'View documentation'}
|
||||
url={content.url}
|
||||
/>
|
||||
)}
|
||||
</SwiperSlide>
|
||||
)
|
||||
})}
|
||||
{props.footer && <div className="my-8">{props.footer}</div>}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ImageCarousel
|
||||
@@ -1,150 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
// Import Swiper React components
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import { Button, Tabs } from 'ui'
|
||||
|
||||
import CodeBlock from '../CodeBlock/CodeBlock'
|
||||
// Import Swiper styles
|
||||
import 'swiper/css'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { Badge } from 'ui'
|
||||
|
||||
interface Content {
|
||||
lang: 'js' | 'py' | 'sql'
|
||||
title: string
|
||||
code: string
|
||||
detail_title?: string
|
||||
detail_text?: string
|
||||
badges_label?: string
|
||||
badges?: string[]
|
||||
url?: string
|
||||
}
|
||||
|
||||
interface SplitCodeBlockCarousel {
|
||||
content: Content[]
|
||||
footer?: React.ReactNode
|
||||
altTabView?: boolean
|
||||
}
|
||||
|
||||
function SplitCodeBlockCarousel(props: SplitCodeBlockCarousel) {
|
||||
// store API swiper instance
|
||||
const [apiSwiper, setApiSwiper] = useState(undefined)
|
||||
const [swiperDetails, setSwiperDetails] = useState(undefined)
|
||||
|
||||
const [apiSwiperActiveIndex, setApiSwiperActiveIndex] = useState(0)
|
||||
|
||||
function handleApiSwiperNavChange(e: number) {
|
||||
setApiSwiperActiveIndex(e)
|
||||
// @ts-ignore
|
||||
apiSwiper.slideTo(e)
|
||||
// @ts-ignore
|
||||
swiperDetails.slideTo(e)
|
||||
}
|
||||
|
||||
const details = (
|
||||
<div>
|
||||
<p>
|
||||
<span className="mb-8 block text-white">Allow fetch something</span>
|
||||
</p>
|
||||
<p>
|
||||
<p>
|
||||
This would only allow the authenticated user access to a folder that is named after their
|
||||
own account UID. This is useful for things like profile images.
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
<Button variant="outline">View documentation</Button>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="sbui-tabs--alt col-span-12 space-y-2 lg:col-span-6 lg:col-start-7">
|
||||
<Tabs
|
||||
scrollable
|
||||
// @ts-ignore
|
||||
activeId={apiSwiperActiveIndex.toString()}
|
||||
// @ts-ignore
|
||||
onChange={(id: string) => handleApiSwiperNavChange(Number(id))}
|
||||
type="pills"
|
||||
>
|
||||
{props.content.map((extension, i) => {
|
||||
return (
|
||||
<Tabs.Panel label={extension.title} id={i.toString()} key={i}>
|
||||
<span></span>
|
||||
</Tabs.Panel>
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
<Swiper
|
||||
// @ts-ignore
|
||||
onSwiper={setApiSwiper}
|
||||
style={{ zIndex: 0 }}
|
||||
initialSlide={apiSwiperActiveIndex}
|
||||
spaceBetween={0}
|
||||
slidesPerView={1}
|
||||
speed={300}
|
||||
autoHeight={true}
|
||||
allowTouchMove={false}
|
||||
>
|
||||
{props.content.map((extension, i) => {
|
||||
return (
|
||||
<SwiperSlide key={i}>
|
||||
<CodeBlock key={i} lang={extension.lang}>
|
||||
{extension.code}
|
||||
</CodeBlock>
|
||||
</SwiperSlide>
|
||||
)
|
||||
})}
|
||||
</Swiper>
|
||||
|
||||
<div className="bg-surface-100 border-default overflow-hidden rounded-md border p-8">
|
||||
<Swiper
|
||||
// @ts-ignore
|
||||
onSwiper={setSwiperDetails}
|
||||
style={{ zIndex: 0 }}
|
||||
initialSlide={0}
|
||||
spaceBetween={0}
|
||||
slidesPerView={1}
|
||||
direction="horizontal"
|
||||
// style={{ overflow: 'hidden' }}
|
||||
speed={300}
|
||||
allowTouchMove={false}
|
||||
>
|
||||
{props.content.map((extension, i) => {
|
||||
return (
|
||||
<SwiperSlide key={i}>
|
||||
<p>
|
||||
<span className="text-foreground mb-4 block text-lg">
|
||||
{extension.detail_title}
|
||||
</span>
|
||||
</p>
|
||||
<p className="p mb-6 block">{extension.detail_text}</p>
|
||||
<div>
|
||||
{extension.url && (
|
||||
<Button asChild variant="default">
|
||||
<Link href={extension.url} as={extension.url} className="ml-px">
|
||||
View documentation
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
<div>
|
||||
<p className="p mr-4">{extension.badges_label}</p>
|
||||
{extension.badges &&
|
||||
extension.badges.map((badge, i) => {
|
||||
return <Badge key={badge}>{badge}</Badge>
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
)
|
||||
})}
|
||||
</Swiper>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SplitCodeBlockCarousel
|
||||
@@ -4,7 +4,11 @@ import { useState } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { Bar, BarChart, Legend, ResponsiveContainer, XAxis, YAxis } from 'recharts'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Tabs } from 'ui'
|
||||
import {
|
||||
Tabs_Shadcn_ as Tabs,
|
||||
TabsList_Shadcn_ as TabsList,
|
||||
TabsTrigger_Shadcn_ as TabsTrigger,
|
||||
} from 'ui'
|
||||
|
||||
import * as data from '@/data/blog/PostgresFTSComparison'
|
||||
|
||||
@@ -53,17 +57,13 @@ const Chart = () => {
|
||||
const [selection, setSelection] = useState('latency')
|
||||
return (
|
||||
<div className={'my-16 flex flex-col'}>
|
||||
<Tabs
|
||||
defaultActiveId={'latency'}
|
||||
type="underlined"
|
||||
size="medium"
|
||||
block
|
||||
onChange={(value: string) => setSelection(value)}
|
||||
>
|
||||
<Tabs.Panel id="latency" label="Latency"></Tabs.Panel>
|
||||
<Tabs.Panel id="results" label="Number of results"></Tabs.Panel>
|
||||
<Tabs.Panel id="avg_latency" label="Average latency"></Tabs.Panel>
|
||||
<Tabs.Panel id="raw_data" label="Raw data"></Tabs.Panel>
|
||||
<Tabs defaultValue="latency" onValueChange={(value: string) => setSelection(value)}>
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="latency">Latency</TabsTrigger>
|
||||
<TabsTrigger value="results">Number of results</TabsTrigger>
|
||||
<TabsTrigger value="avg_latency">Average latency</TabsTrigger>
|
||||
<TabsTrigger value="raw_data">Raw data</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
|
||||
{selection !== 'raw_data' ? (
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// Import Swiper styles if swiper used on page
|
||||
import 'swiper/css'
|
||||
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useState } from 'react'
|
||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import { Button, Tabs } from 'ui'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import CodeBlock, { LANG } from '../CodeBlock/CodeBlock'
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
|
||||
export interface APIExample {
|
||||
lang: LANG
|
||||
@@ -52,18 +53,22 @@ function APISection(props: Props) {
|
||||
{props.footer && <div className="py-8">{props.footer}</div>}
|
||||
</div>
|
||||
<div className="sbui-tabs--alt col-span-12 lg:col-span-7 xl:col-span-6 xl:col-start-7">
|
||||
<Tabs
|
||||
scrollable
|
||||
activeId={apiSwiperActiveIndex.toString()}
|
||||
onChange={(id: string) => handleApiSwiperNavChange(Number(id))}
|
||||
>
|
||||
<div className="flex gap-2 mb-2">
|
||||
{props.content &&
|
||||
props.content.map((content: APIExample, i) => (
|
||||
<Tabs.Panel label={content.title} id={i.toString()} key={i}>
|
||||
<span key={i}></span>
|
||||
</Tabs.Panel>
|
||||
<Button
|
||||
id={i.toString()}
|
||||
variant={apiSwiperActiveIndex === i ? 'default' : 'outline'}
|
||||
className={cn({
|
||||
'text-muted hover:text-foreground': apiSwiperActiveIndex !== i,
|
||||
})}
|
||||
key={i}
|
||||
onClick={() => handleApiSwiperNavChange(i)}
|
||||
>
|
||||
{content.title}
|
||||
</Button>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="overflow-hidden">
|
||||
<Swiper
|
||||
// @ts-ignore
|
||||
|
||||
Reference in new issue
Block a user