v0.10.9 首页 H8 标题条 + H9 tabs 切换器 1:1:TabsWidget 忠实 DOM/React 切换;widget-tabs 基础块+生成规则+custom_css 三层;section margin/gap 渲染支持;全局 body 行高改无单位 1.5、kit 链接字体 Roboto;H1~H7 全量回归无差异
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* 样式以内联 style 复现视觉效果(背景、内边距、对齐、列宽百分比),
|
||||
* 未识别的 widgetType 优雅降级:渲染 heading_title / editor 文本或忽略。
|
||||
*/
|
||||
import { Component, useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { Component, Fragment, useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { getLatestPosts, getLatestProducts, type LatestItem } from "~/server/data";
|
||||
import TickerBar from "~/components/website/TickerBar";
|
||||
import { InquiryForm } from "~/components/website/InquiryForm";
|
||||
@@ -320,6 +320,9 @@ function SectionNode({ el }: { el: ElElement }) {
|
||||
const width = sizeCss(settings.content_width) || sizeCss((settings as Record<string, unknown>).width) || "1140px";
|
||||
const secPad = paddingCss(settings.padding);
|
||||
if (secPad) style.padding = secPad;
|
||||
// section 外边距(原站 computed 在根元素,如 H8 7df0244 margin-top 4% = 57.4px @1436)
|
||||
const secMargin = paddingCss(settings.margin);
|
||||
if (secMargin) style.margin = secMargin;
|
||||
const inner: CSSProperties = {};
|
||||
if (s(settings.layout) === "full_width") {
|
||||
// 通栏 section:内容不设上限(如首页 hero 轮播)
|
||||
@@ -1091,24 +1094,73 @@ function AccordionWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
}
|
||||
|
||||
/** # 组件 tabs widget:标签页 */
|
||||
/** # 组件 Elementor 原生 tabs widget(H9 热门嵌入式硬件切换器)
|
||||
* DOM 对齐原站(1436 实测 2026-11-01):.elementor-tabs > .elementor-tabs-wrapper(desktop 标题行,
|
||||
* role=tablist,flex 居中) + .elementor-tabs-content-wrapper(mobile 标题 ×N + 内容面板 ×N)。
|
||||
* active 用 elementor-active 类;点击切换(React state,原站为 Elementor JS 行为);
|
||||
* 内容 tab_content 为自包含 HTML(含 <style> 卡片网格样式),dangerouslySetInnerHTML 原样输出。
|
||||
* 基础样式移植 widget-tabs.min.css(app.css「Elementor tabs」块),本节点 custom_css
|
||||
* (.ele-soms-chip-dif 边框/内容覆盖)经 collectCustomCss 自动注入——因此类名必须用
|
||||
* elementor 原名,不得改 el- 前缀。id 基数 2651:原站渲染计数 elementor-tab-title-2651…,
|
||||
* 本地按序复刻(仅影响锚点,无视觉作用)。
|
||||
*/
|
||||
function TabsWidget({ settings }: { settings: Record<string, unknown> }) {
|
||||
const tabs = Array.isArray(settings.tabs) ? (settings.tabs as { tab_title?: string; tab_content?: string }[]) : [];
|
||||
const tabs = Array.isArray(settings.tabs)
|
||||
? (settings.tabs as { _id?: string; tab_title?: string; tab_content?: string }[])
|
||||
: [];
|
||||
const [active, setActive] = useState(0);
|
||||
if (!tabs.length) return null;
|
||||
const align = s(settings.tabs_align_horizontal) || "center";
|
||||
const alignCls =
|
||||
align === "center" ? " elementor-tabs-alignment-center"
|
||||
: align === "end" ? " elementor-tabs-alignment-end"
|
||||
: align === "stretch" ? " elementor-tabs-alignment-stretch"
|
||||
: "";
|
||||
const base = 2651;
|
||||
return (
|
||||
<Tabs defaultValue="tab-0" className="mb-[18px]">
|
||||
<TabsList>
|
||||
<div className={`elementor-widget-tabs elementor-tabs elementor-tabs-view-horizontal${alignCls}`}>
|
||||
<div className="elementor-tabs-wrapper" role="tablist">
|
||||
{tabs.map((t, i) => (
|
||||
<TabsTrigger key={i} value={`tab-${i}`}>
|
||||
<div
|
||||
key={t._id || i}
|
||||
id={`elementor-tab-title-${base + i}`}
|
||||
className={"elementor-tab-title elementor-tab-desktop-title" + (i === active ? " elementor-active" : "")}
|
||||
role="tab"
|
||||
data-tab={i + 1}
|
||||
tabIndex={i === active ? 0 : -1}
|
||||
aria-selected={i === active}
|
||||
onClick={() => setActive(i)}
|
||||
>
|
||||
{s(t.tab_title)}
|
||||
</TabsTrigger>
|
||||
</div>
|
||||
))}
|
||||
</TabsList>
|
||||
{tabs.map((t, i) => (
|
||||
<TabsContent key={i} value={`tab-${i}`}>
|
||||
<div className="content-html" dangerouslySetInnerHTML={{ __html: s(t.tab_content) }} />
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="elementor-tabs-content-wrapper" role="tablist">
|
||||
{tabs.map((t, i) => (
|
||||
<Fragment key={t._id || i}>
|
||||
<div
|
||||
className={"elementor-tab-title elementor-tab-mobile-title" + (i === active ? " elementor-active" : "")}
|
||||
role="tab"
|
||||
data-tab={i + 1}
|
||||
tabIndex={i === active ? 0 : -1}
|
||||
aria-selected={i === active}
|
||||
onClick={() => setActive(i)}
|
||||
>
|
||||
{s(t.tab_title)}
|
||||
</div>
|
||||
<div
|
||||
id={`elementor-tab-content-${base + i}`}
|
||||
className="elementor-tab-content elementor-clearfix"
|
||||
role="tabpanel"
|
||||
data-tab={i + 1}
|
||||
tabIndex={0}
|
||||
style={{ display: i === active ? "block" : "none" }}
|
||||
dangerouslySetInnerHTML={{ __html: s(t.tab_content) }}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ const SETTING_KEYS = new Set([
|
||||
// 列结构
|
||||
"_column_size",
|
||||
"_inline_size",
|
||||
// section 间隙档位(决定子列默认 populated padding:no 0 / default 10 / …)
|
||||
"gap",
|
||||
// 各 widget 数据
|
||||
"heading_title",
|
||||
"title",
|
||||
@@ -79,6 +81,8 @@ const SETTING_KEYS = new Set([
|
||||
"slides",
|
||||
"slides_height",
|
||||
"tabs",
|
||||
"tabs_align_horizontal",
|
||||
"tab_active_color",
|
||||
"video_url",
|
||||
"youtube_url",
|
||||
"map",
|
||||
|
||||
Reference in New Issue
Block a user