element used for labels/ids
+ tab: TabElement;
+ // The original top-level node we should render as content (could be a wrapper like
)
+ renderNode: ReactNode;
+ };
+
+ const isTabElement = (node: unknown): node is TabElement => {
+ if (!isValidElement(node as ReactElement)) {
+ return false;
+ }
+ const props = (node as any).props;
+ return typeof props?.label === "string" && props.label.length > 0;
+ };
- const extractTabs = (elements: ReactElement[]) => {
- elements.forEach((element) => {
- if (element.props.mdxType === "CustomContent") {
- if (element.props.children) {
- const childrenArray = Array.isArray(element.props.children)
- ? element.props.children
- : [element.props.children];
- extractTabs(childrenArray);
+ const unwrapToTab = (node: ReactNode): TabElement | null => {
+ // Find the first tab element inside this node (supports wrapper like ).
+ return Children.toArray(node).reduce(
+ (found, child) => {
+ if (found) {
+ return found;
+ }
+ if (!isValidElement(child)) {
+ return null;
}
- } else {
- tabs.push(element);
+ if ((child.props as any)?.mdxType === "CustomContent") {
+ return unwrapToTab(child.props?.children);
+ }
+ if (isTabElement(child)) {
+ return child;
+ }
+ // If it's some other element (e.g. Fragment), keep walking down its children.
+ return unwrapToTab(child.props?.children);
+ },
+ null
+ );
+ };
+
+ const flattenTopLevelNodes = (nodes: ReactNode): ReactNode[] => {
+ // MDX can sometimes pass a single Fragment that wraps multiple tab panes.
+ // We want each tab pane to be a top-level entry, so we manually unwrap Fragments here.
+ return Children.toArray(nodes).reduce((acc, node) => {
+ if (isValidElement(node) && node.type === Fragment) {
+ return acc.concat(Children.toArray(node.props?.children));
}
- });
+ return acc.concat(node);
+ }, []);
};
- extractTabs(children);
+ // Preserve original top-level nodes for rendering (keeps wrappers like intact).
+ const tabEntries = flattenTopLevelNodes(children)
+ .map((node) => {
+ const tab = unwrapToTab(node);
+ return tab ? { tab, renderNode: node } : null;
+ })
+ .filter((entry): entry is TabEntry => entry !== null);
- return tabs;
+ return tabEntries;
}, [children]);
+ const actualTabs = useMemo(() => tabEntries.map((e) => e.tab), [tabEntries]);
+
const defaultValue =
actualTabs[0]?.props?.value || actualTabs[0]?.props.label;
const [activeTab, setActiveTab] = useState(defaultValue);
@@ -52,7 +99,7 @@ export function SimpleTab({
activeTabGroup &&
activeTabGroup !== activeTab &&
actualTabs.some(
- (child) => child.props?.value || child.props.label === activeTabGroup
+ (child) => (child.props?.value || child.props.label) === activeTabGroup
)
) {
setActiveTab(activeTabGroup);
@@ -96,9 +143,8 @@ export function SimpleTab({
);
})}
- {children.map((child, index) => {
- const actualChild = actualTabs[index];
- const id: string = actualChild.props?.value || actualChild.props.label;
+ {tabEntries.map(({ tab, renderNode }) => {
+ const id: string = tab.props?.value || tab.props.label;
return (
- {child}
+ {renderNode}
);
})}
@@ -139,7 +185,7 @@ export const TabContentDetector = ({
renderedTabs,
onRendered,
}: {
- children: ReactElement;
+ children: ReactNode;
id: string;
activeTab: string;
renderedTabs: string[];
diff --git a/src/components/MDXComponents/developer/DevToolCard.tsx b/src/components/MDXComponents/developer/DevToolCard.tsx
index dfbc05eab..96907b926 100644
--- a/src/components/MDXComponents/developer/DevToolCard.tsx
+++ b/src/components/MDXComponents/developer/DevToolCard.tsx
@@ -51,7 +51,7 @@ export function DevToolCard({
"> a.MuiCardActionArea-root:hover, a.MuiButton-text:hover": {
textDecoration: "none",
},
- ".MuiCardActions-root > a.MuiButton-text": {
+ ".MuiCardActions-root > a.MuiButton-text:not(.button)": {
color: "text.secondary",
borderRadius: 0,
},
diff --git a/src/components/MDXContent.tsx b/src/components/MDXContent.tsx
index 87ccfc3ea..ebef8ed93 100644
--- a/src/components/MDXContent.tsx
+++ b/src/components/MDXContent.tsx
@@ -8,11 +8,15 @@ import Box from "@mui/material/Box";
import * as MDXComponents from "components/MDXComponents";
import { CustomNotice } from "components/Card/CustomNotice";
-import { PathConfig, BuildType, CloudPlan } from "shared/interface";
+import {
+ PathConfig,
+ BuildType,
+ CloudPlan,
+ TOCNamespace,
+} from "shared/interface";
import replaceInternalHref from "shared/utils/anchor";
import { Pre } from "components/MDXComponents/Pre";
import { useCustomContent } from "components/MDXComponents/CustomContent";
-import { getPageType } from "shared/utils";
import { H1 } from "./MDXComponents/H1";
export default function MDXContent(props: {
@@ -26,6 +30,7 @@ export default function MDXContent(props: {
buildType: BuildType;
pageUrl: string;
cloudPlan: CloudPlan | null;
+ namespace?: TOCNamespace;
}) {
const {
data,
@@ -38,10 +43,14 @@ export default function MDXContent(props: {
buildType,
pageUrl,
cloudPlan,
+ namespace,
} = props;
- const pageType = getPageType(language, pageUrl);
- const CustomContent = useCustomContent(pageType, cloudPlan, language);
+ const CustomContent = useCustomContent(
+ namespace || TOCNamespace.TiDB,
+ cloudPlan,
+ language
+ );
// const isAutoTranslation = useIsAutoTranslation(pageUrl || "");
React.useEffect(() => {
@@ -67,7 +76,7 @@ export default function MDXContent(props: {
{...props}
/>
),
- [pathConfig, filePath, pageUrl]
+ [pathConfig, filePath, pageUrl, namespace]
);
return (
diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx
index b5afefe8e..608c7e668 100644
--- a/src/components/Search/index.tsx
+++ b/src/components/Search/index.tsx
@@ -31,7 +31,7 @@ const StyledTextField = styled((props: TextFieldProps) => (
},
}));
-const SEARCH_WIDTH = 250;
+const SEARCH_WIDTH = 400;
enum SearchType {
Onsite = "onsite",
diff --git a/src/media/icons/chevron-down.svg b/src/media/icons/chevron-down.svg
new file mode 100644
index 000000000..b912fc636
--- /dev/null
+++ b/src/media/icons/chevron-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/media/icons/cloud-03.svg b/src/media/icons/cloud-03.svg
new file mode 100644
index 000000000..c9d24ae71
--- /dev/null
+++ b/src/media/icons/cloud-03.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/media/icons/layers-three-01.svg b/src/media/icons/layers-three-01.svg
new file mode 100644
index 000000000..97af7b2c1
--- /dev/null
+++ b/src/media/icons/layers-three-01.svg
@@ -0,0 +1,10 @@
+
diff --git a/src/media/logo/tidb-logo-withtext.svg b/src/media/logo/tidb-logo-withtext.svg
index 28bd4072a..1225ad09a 100644
--- a/src/media/logo/tidb-logo-withtext.svg
+++ b/src/media/logo/tidb-logo-withtext.svg
@@ -1,17 +1,17 @@
-