如何在Next.js+Sanity中用@portabletext/react实现多样式嵌套列表
实现嵌套列表及双样式切换方案
核心修改思路
要实现嵌套列表并切换两种样式,需要利用@portabletext/react提供的level属性识别嵌套层级,同时通过外部参数控制样式切换。以下是具体修改后的代码和说明:
修改后的RichText组件代码
import Image from 'next/image'; import urlFor from '../lib/urlFor'; import Link from 'next/link'; import { PortableTextReactComponents } from '@portabletext/react'; import CodeBlock from './CodeBlock'; // 改为函数组件,接收样式切换参数 export const RichText = ({ isAlternativeStyle }: { isAlternativeStyle: boolean }): Partial<PortableTextReactComponents> => { // 根据层级和样式类型返回对应的列表类名 const getBulletListClass = (level: number) => { if (isAlternativeStyle) { // 替代样式:调整缩进、符号类型和间距 return `my-3 ml-${8 + level * 6} list-square text-[#333a4a]`; } // 常规样式:按层级递增缩进 return `my-5 ml-${10 + level * 6} list-disc tracking-[0.005em] text-base text-[#333a4a]`; }; const getNumberListClass = (level: number) => { if (isAlternativeStyle) { return `my-3 ml-${8 + level * 6} list-decimal text-[#333a4a]`; } return `my-5 ml-${10 + level * 6} list-decimal tracking-[0.005em] text-base text-[#333a4a]`; }; return { types: { image: ({ value }: any) => { return ( <div> <section className="relative h-[25rem]"> <Image src={urlFor(value).url()} fill alt="内容图片" className="rounded-md" /> </section> <section className="flex flex-col items-center"> <p className="tracking-[0.005em] text-sm text-[#9fa2aa] pt-4"> {value.caption} </p> </section> </div> ); }, code: ({ _, value: { code, language } }: any) => ( <CodeBlock language={language} code={code} /> ), }, list: { bullet: ({ children, level = 0 }: any) => ( <ul className={getBulletListClass(level)}> {children} </ul> ), number: ({ children, level = 0 }: any) => ( <ol className={getNumberListClass(level)}> {children} </ol> ), }, // 自定义列表项样式,适配不同样式需求 listItem: { bullet: ({ children }: any) => ( <li className={isAlternativeStyle ? "py-2" : "py-1"}> {children} </li> ), number: ({ children }: any) => ( <li className={isAlternativeStyle ? "py-2" : "py-1"}> {children} </li> ), }, block: { normal: ({ children }: any) => ( <p className="my-5 tracking-[0.005em] text-base text-[#333a4a] leading-7"> {children} </p> ), h1: ({ children }: any) => ( <h1 className="my-5 text-5xl font-semibold text-fs-black">{children}</h1> ), h2: ({ children }: any) => ( <h2 className="mt-10 mb-5 text-4xl font-medium text-fs-black"> {children} </h2> ), h3: ({ children }: any) => ( <h3 className="mt-14 mb-5 text-3xl font-medium pb-2 border-b-[1px] border-b-[#E6E6E6] text-fs-black"> {children} </h3> ), h4: ({ children }: any) => ( <h4 className="mt-14 mb-5 text-2xl font-medium pb-2 border-b-[1px] border-b-[#E6E6E6] text-fs-black"> {children} </h4> ), h5: ({ children }: any) => ( <h4 className="mt-10 text-xl font-medium text-fs-black">{children}</h4> ), blockquote: ({ children }: any) => ( <blockquote className="py-5 pl-5 my-5 border-l-4 border-l-primary tracking-[0.005em] text-base text-fs-black"> {children} </blockquote> ), }, marks: { link: ({ children, value }: any) => { const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined; return ( <Link href={value.href} rel={rel}> {children} </Link> ); }, }, }; };
关键实现点说明
- 样式切换参数:将RichText改为函数组件,通过
isAlternativeStyle参数控制两种样式的切换 - 嵌套层级处理:利用
list组件接收的level属性,动态计算不同层级的缩进距离,确保嵌套列表显示正确 - 列表项自定义:新增
listItem配置,针对不同样式调整列表项的内边距,优化视觉效果 - 样式类名动态生成:通过
getBulletListClass和getNumberListClass函数,根据层级和样式类型返回对应的Tailwind类名
使用方式
在渲染PortableText组件时,根据传入的条件切换样式:
import { PortableText } from '@portabletext/react'; import { RichText } from './RichText'; // 常规样式 <PortableText value={yourContent} components={RichText({ isAlternativeStyle: false })} /> // 替代样式 <PortableText value={yourContent} components={RichText({ isAlternativeStyle: true })} />
内容的提问来源于stack exchange,提问作者Toss
相关产品推荐
相关产品推荐

