v0.10.21: 产品归档页 C1-C4(原站 /shop/ 模板 1398 + WPC 筛选 + WooCommerce 网格)
- 导入 archive 模板 post 1398(slug=shop-archive),/collection/ 用 ElementorRenderer 直出 hero/两列外壳,按 widget id 注入 ShopFilters/ShopProducts;新增 server function 解决客户端导航时 loader 打不开 db 的问题 - C1 hero:面包屑 Home / Shop(BreadcrumbWidget + vars.breadcrumb 上下文) - C2 筛选:12 组属性(term taxonomy 顺序,scripts/import-attribute-term-order.ts); 长期列表 5 项 + See more,Battery/Operating/Install 默认折叠;URL query 驱动, SoM 筛选实测 22 条;顶部 Filters 标题/已选 chips/Clear all - C3 网格:WooCommerce 归档 DOM(结果计数、orderby、24 卡 4 列、卡片 1:1 图/标题/ Read more);默认排序导入 products.meta.menu_order(scripts/import-product-menu-order.ts), 前三个商品与原站一致(DSOM-040R/110R/130N) - C4 分页:页码/箭头、当前页样式、page2 25–48 条验证 - 修复 Tailwind .columns-4 与 WooCommerce 网格类冲突;app.css 新增 shop 归档 scoped 样式 - 1436 实测:hero 52.8、shop 节 2906.7、筛选区 2798.7、网格 2523.3、卡 257.7×409、 分页 y2855,与原站逐项一致(±6px 内);bun test 11 pass;版本 0.10.21
This commit is contained in:
@@ -273,24 +273,46 @@ const PostsDataContext = createContext<Record<string, PostCardItem[]>>({});
|
||||
/** # 成员变量 WordPress 短代码渲染上下文(widget id → 预渲染节点;页脚 menu-2/gtranslate) **/
|
||||
const ShortcodesContext = createContext<Record<string, ReactNode>>({});
|
||||
|
||||
/** # 类型 渲染器动态数据(面包屑等 WP 动态 widget 的 SSR 上下文) */
|
||||
export type RendererVars = {
|
||||
/** # 成员变量 面包屑路径(末项无 url 表示当前页) **/
|
||||
breadcrumb?: { label: string; url?: string }[];
|
||||
};
|
||||
|
||||
/** # 成员变量 动态数据上下文(woocommerce-breadcrumb 等 widget 读取) **/
|
||||
const VarsContext = createContext<RendererVars>({});
|
||||
|
||||
/** # 组件 Elementor 渲染器入口
|
||||
* 参数 shortcodes 短代码 widget 的预渲染内容(键为 widget id;缺省渲染为空)
|
||||
* 参数 vars 动态数据(面包屑路径等)
|
||||
*/
|
||||
export function ElementorRenderer({ doc, postsData, shortcodes }: { doc: ElDocument; postsData?: Record<string, PostCardItem[]>; shortcodes?: Record<string, ReactNode> }) {
|
||||
export function ElementorRenderer({
|
||||
doc,
|
||||
postsData,
|
||||
shortcodes,
|
||||
vars,
|
||||
}: {
|
||||
doc: ElDocument;
|
||||
postsData?: Record<string, PostCardItem[]>;
|
||||
shortcodes?: Record<string, ReactNode>;
|
||||
vars?: RendererVars;
|
||||
}) {
|
||||
const elements = normalizeDoc(doc);
|
||||
if (!elements.length) return null;
|
||||
const css = collectCustomCss(elements) + collectResponsiveCss(elements);
|
||||
return (
|
||||
<PostsDataContext.Provider value={postsData || {}}>
|
||||
<ShortcodesContext.Provider value={shortcodes || {}}>
|
||||
<div className="content-html">
|
||||
{css ? <style dangerouslySetInnerHTML={{ __html: css }} /> : null}
|
||||
<VarsContext.Provider value={vars || {}}>
|
||||
<PostsDataContext.Provider value={postsData || {}}>
|
||||
<ShortcodesContext.Provider value={shortcodes || {}}>
|
||||
<div className="content-html">
|
||||
{css ? <style dangerouslySetInnerHTML={{ __html: css }} /> : null}
|
||||
{elements.map((el, i) => (
|
||||
<ElementNode key={el.id || i} el={el} />
|
||||
))}
|
||||
</div>
|
||||
</ShortcodesContext.Provider>
|
||||
</PostsDataContext.Provider>
|
||||
</ShortcodesContext.Provider>
|
||||
</PostsDataContext.Provider>
|
||||
</VarsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -644,6 +666,9 @@ function WidgetNodeInner({ el }: { el: ElElement }) {
|
||||
const w = s(el.widgetType);
|
||||
const settings = resolveGlobals(el.settings || {});
|
||||
const shortcodes = useContext(ShortcodesContext);
|
||||
// 任意 widget 可由调用方按 id 注入实现(WooCommerce 归档/WPC 筛选等动态插件 widget)
|
||||
const injected = el.id ? shortcodes[el.id] : undefined;
|
||||
if (injected !== undefined) return <>{injected}</>;
|
||||
const unknownFallback = () => {
|
||||
const title = s(settings.heading_title) || s(settings.title);
|
||||
const text = s(settings.editor) || s(settings.html) || s(settings.text);
|
||||
@@ -658,6 +683,8 @@ function WidgetNodeInner({ el }: { el: ElElement }) {
|
||||
switch (w) {
|
||||
case "heading":
|
||||
return <HeadingWidget settings={settings} />;
|
||||
case "woocommerce-breadcrumb":
|
||||
return <BreadcrumbWidget />;
|
||||
case "text-editor":
|
||||
return <TextEditorWidget settings={settings} />;
|
||||
case "image":
|
||||
@@ -746,6 +773,23 @@ function WidgetNodeInner({ el }: { el: ElElement }) {
|
||||
}
|
||||
}
|
||||
|
||||
/** # 组件 woocommerce-breadcrumb widget:面包屑(路径由 vars.breadcrumb 注入;
|
||||
* 原站归档页模板 1398 的 hero 即此 widget,末项为当前页、前面项带链接) */
|
||||
function BreadcrumbWidget() {
|
||||
const vars = useContext(VarsContext);
|
||||
const trail = vars.breadcrumb?.length ? vars.breadcrumb : [{ label: "Home", url: "/" }, { label: "Shop" }];
|
||||
return (
|
||||
<nav className="woocommerce-breadcrumb" aria-label="Breadcrumb">
|
||||
{trail.map((n, i) => (
|
||||
<Fragment key={`${n.label}-${i}`}>
|
||||
{i > 0 ? <span className="ds-crumb-sep" dangerouslySetInnerHTML={{ __html: " / " }} /> : null}
|
||||
{n.url && i < trail.length - 1 ? <a href={n.url}>{n.label}</a> : <span>{n.label}</span>}
|
||||
</Fragment>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
/** # 组件 heading widget:标题(可带 link,is_external → target=_blank;title 支持 HTML) */
|
||||
function HeadingWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
// 标签合法域同原站 Utils::validate_html_tag(h1..h6 / div / span / p);默认 h2
|
||||
|
||||
Reference in New Issue
Block a user