From 4bad9714faa6f7944555f82fd37e480117d34b81 Mon Sep 17 00:00:00 2001 From: javayhu Date: Mon, 25 Aug 2025 23:43:45 +0800 Subject: [PATCH] refactor: remove BlockCategory pages, and BlockPreview components --- .../(marketing)/blocks/[category]/layout.tsx | 16 - .../(marketing)/blocks/[category]/page.tsx | 54 --- src/components/tailark/block-preview.tsx | 368 ------------------ 3 files changed, 438 deletions(-) delete mode 100644 src/app/[locale]/(marketing)/blocks/[category]/layout.tsx delete mode 100644 src/app/[locale]/(marketing)/blocks/[category]/page.tsx delete mode 100644 src/components/tailark/block-preview.tsx diff --git a/src/app/[locale]/(marketing)/blocks/[category]/layout.tsx b/src/app/[locale]/(marketing)/blocks/[category]/layout.tsx deleted file mode 100644 index c988f6f..0000000 --- a/src/app/[locale]/(marketing)/blocks/[category]/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { categories } from '@/components/tailark/blocks'; -import BlocksNav from '@/components/tailark/blocks-nav'; -import type { PropsWithChildren } from 'react'; - -/** - * The locale inconsistency issue has been fixed in the BlocksNav component - */ -export default function BlockCategoryLayout({ children }: PropsWithChildren) { - return ( - <> - - -
{children}
- - ); -} diff --git a/src/app/[locale]/(marketing)/blocks/[category]/page.tsx b/src/app/[locale]/(marketing)/blocks/[category]/page.tsx deleted file mode 100644 index cd893d1..0000000 --- a/src/app/[locale]/(marketing)/blocks/[category]/page.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import BlockPreview from '@/components/tailark/block-preview'; -import { blocks, categories } from '@/components/tailark/blocks'; -import { constructMetadata } from '@/lib/metadata'; -import { getUrlWithLocale } from '@/lib/urls/urls'; -import type { Metadata } from 'next'; -import type { Locale } from 'next-intl'; -import { getTranslations } from 'next-intl/server'; -import { notFound } from 'next/navigation'; - -export const dynamic = 'force-static'; -export const revalidate = 3600; - -export async function generateStaticParams() { - return categories.map((category) => ({ - category: category, - })); -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ locale: Locale; category: string }>; -}): Promise { - const { locale, category } = await params; - const t = await getTranslations({ locale, namespace: 'Metadata' }); - return constructMetadata({ - title: category + ' | ' + t('title'), - description: t('description'), - canonicalUrl: getUrlWithLocale(`/blocks/${category}`, locale), - }); -} - -interface BlockCategoryPageProps { - params: Promise<{ category: string }>; -} - -export default async function BlockCategoryPage({ - params, -}: BlockCategoryPageProps) { - const { category } = await params; - const categoryBlocks = blocks.filter((b) => b.category === category); - - if (categoryBlocks.length === 0) { - notFound(); - } - - return ( - <> - {categoryBlocks.map((block, index) => ( - - ))} - - ); -} diff --git a/src/components/tailark/block-preview.tsx b/src/components/tailark/block-preview.tsx deleted file mode 100644 index 797e49e..0000000 --- a/src/components/tailark/block-preview.tsx +++ /dev/null @@ -1,368 +0,0 @@ -'use client'; - -import { Button } from '@/components/ui/button'; -import { Separator } from '@/components/ui/separator'; -import { useCopyToClipboard } from '@/hooks/use-clipboard'; -import { isUrlCached } from '@/lib/serviceWorker'; -import { cn } from '@/lib/utils'; -import * as RadioGroup from '@radix-ui/react-radio-group'; -import { Check, Code2, Copy, Eye, Maximize, Terminal } from 'lucide-react'; -import Link from 'next/link'; -import type React from 'react'; -import { useEffect, useRef, useState } from 'react'; -import { - Panel, - PanelGroup, - PanelResizeHandle, - type ImperativePanelGroupHandle, -} from 'react-resizable-panels'; -import { useMedia } from 'use-media'; - -export interface BlockPreviewProps { - code?: string; - preview: string; - title: string; - category: string; - previewOnly?: boolean; -} - -const radioItem = - 'rounded-(--radius) duration-200 flex items-center justify-center h-8 px-2.5 gap-2 transition-[color] data-[state=checked]:bg-muted'; - -const DEFAULTSIZE = 100; -const SMSIZE = 30; -const MDSIZE = 62; -const LGSIZE = 82; - -const getCacheKey = (src: string) => `iframe-cache-${src}`; - -const titleToNumber = (title: string): number => { - const titles = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty"]; - return titles.indexOf(title.toLowerCase()) + 1; -}; - -export const BlockPreview: React.FC = ({ - code, - preview, - title, - category, - previewOnly, -}) => { - const [width, setWidth] = useState(DEFAULTSIZE); - const [mode, setMode] = useState<'preview' | 'code'>('preview'); - const [iframeHeight, setIframeHeight] = useState(0); - const [shouldLoadIframe, setShouldLoadIframe] = useState(false); - const [cachedHeight, setCachedHeight] = useState(null); - const [isIframeCached, setIsIframeCached] = useState(false); - - const terminalCode = `pnpm dlx shadcn@canary add https://nsui.irung.me/r/${category}-${titleToNumber(title)}.json`; - const { copied, copy } = useCopyToClipboard({ code: code as string, title, category, eventName: 'block_copy' }) - const { copied: cliCopied, copy: cliCopy } = useCopyToClipboard({ code: terminalCode, title, category, eventName: 'block_cli_copy' }) - - const ref = useRef(null); - const isLarge = useMedia('(min-width: 1024px)'); - - const iframeRef = useRef(null); - const observer = useRef(null); - const blockRef = useRef(null); - - useEffect(() => { - observer.current = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting) { - setShouldLoadIframe(true); - observer.current?.disconnect(); - } - }, - { threshold: 0.1 } - ); - - if (blockRef.current) { - observer.current.observe(blockRef.current); - } - - return () => { - observer.current?.disconnect(); - }; - }, []); - - useEffect(() => { - const checkCache = async () => { - try { - const isCached = await isUrlCached(preview); - setIsIframeCached(isCached); - if (isCached) { - setShouldLoadIframe(true); - } - } catch (error) { - console.error('Error checking cache status:', error); - } - }; - - checkCache(); - - try { - const cacheKey = getCacheKey(preview); - const cached = localStorage.getItem(cacheKey); - if (cached) { - const { height, timestamp } = JSON.parse(cached); - const now = Date.now(); - if (now - timestamp < 24 * 60 * 60 * 1000) { - setCachedHeight(height); - setIframeHeight(height); - } - } - } catch (error) { - console.error('Error retrieving cache:', error); - } - }, [preview]); - - useEffect(() => { - const iframe = iframeRef.current; - if (!iframe || !shouldLoadIframe) return; - - const handleLoad = () => { - try { - const contentHeight = iframe.contentWindow!.document.body.scrollHeight; - setIframeHeight(contentHeight); - - const cacheKey = getCacheKey(preview); - const cacheValue = JSON.stringify({ - height: contentHeight, - timestamp: Date.now(), - }); - localStorage.setItem(cacheKey, cacheValue); - } catch (e) { - console.error('Error accessing iframe content:', e); - } - }; - - iframe.addEventListener('load', handleLoad); - return () => { - iframe.removeEventListener('load', handleLoad); - }; - }, [shouldLoadIframe, preview]); - - useEffect(() => { - if (!blockRef.current || shouldLoadIframe) return; - - const linkElement = document.createElement('link'); - linkElement.rel = 'preload'; - linkElement.href = preview; - linkElement.as = 'document'; - - if ( - !document.head.querySelector(`link[rel="preload"][href="${preview}"]`) - ) { - document.head.appendChild(linkElement); - } - - return () => { - const existingLink = document.head.querySelector( - `link[rel="preload"][href="${preview}"]` - ); - if (existingLink) { - document.head.removeChild(existingLink); - } - }; - }, [preview, shouldLoadIframe]); - - return ( -
-
-
-
-
-
- -
-
- {code && ( - <> - - setMode('preview')} - aria-label="Block preview" - value="100" - checked={mode == 'preview'} - className={radioItem} - > - - Preview - - - setMode('code')} - aria-label="Code" - value="0" - checked={mode == 'code'} - className={radioItem} - > - - Code - - - - - - )} - {previewOnly && ( - <> - {' '} - {title} - {' '} - - )} - {/* */} - - - {width < MDSIZE - ? 'Mobile' - : width < LGSIZE - ? 'Tablet' - : 'Desktop'} - {' '} -
- -
- {code && ( - <> - - - {/* */} - - - - - )} - {!code && ( - - {/* pnpm dlx shadcn@canary add */}{category}-{titleToNumber(title)} - - )} -
-
-
- -
-
-
-
-
- -
-
- - { - setWidth(Number(size)); - }} - defaultSize={DEFAULTSIZE} - minSize={SMSIZE} - className="h-fit border-x" - > -
- {shouldLoadIframe ? ( -