diff --git a/nx-dev/ui-fence/src/lib/fence.tsx b/nx-dev/ui-fence/src/lib/fence.tsx index 8f75a26b1a..43cdc7e079 100644 --- a/nx-dev/ui-fence/src/lib/fence.tsx +++ b/nx-dev/ui-fence/src/lib/fence.tsx @@ -4,6 +4,7 @@ import { ClipboardDocumentIcon, SparklesIcon, } from '@heroicons/react/24/outline'; +import cx from 'classnames'; import { JSX, ReactNode, useEffect, useState } from 'react'; // @ts-ignore import { CopyToClipboard } from 'react-copy-to-clipboard'; @@ -31,6 +32,7 @@ function CodeWrapper(options: { title: string; path: string; language: string; + isWithinTab?: boolean; children: string; // intentionally typed as such }): ({ children }: { children: ReactNode }) => JSX.Element { return ({ children }: { children: ReactNode }) => @@ -49,7 +51,11 @@ function CodeWrapper(options: { title={options.title} /> ) : ( - + ); } @@ -92,6 +98,7 @@ export interface FenceProps { skipRescope?: boolean; selectedLineGroup?: string; onLineGroupSelectionChange?: (selection: string) => void; + isWithinTab?: boolean; } export function Fence({ @@ -107,6 +114,7 @@ export function Fence({ selectedLineGroup, skipRescope, onLineGroupSelectionChange, + isWithinTab, }: FenceProps) { if (highlightLines) { highlightLines = processHighlightLines(highlightLines); @@ -168,7 +176,12 @@ export function Fence({ } return ( -
+
{enableCopy && enableCopy === true && ( @@ -182,7 +195,7 @@ export function Fence({ type="button" className={ 'not-prose flex border border-slate-200 bg-slate-50/50 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:border-slate-700 dark:bg-slate-800/60' + - (highlightOptions && highlightOptions[0] + ((highlightOptions && highlightOptions[0]) || isWithinTab ? '' : ' rounded-tr-lg') } @@ -197,7 +210,7 @@ export function Fence({ )} {highlightOptions && highlightOptions[0] && (
diff --git a/nx-dev/ui-fence/src/lib/fences/code-output.tsx b/nx-dev/ui-fence/src/lib/fences/code-output.tsx index 6a20c8b186..d2c9f9ee70 100644 --- a/nx-dev/ui-fence/src/lib/fences/code-output.tsx +++ b/nx-dev/ui-fence/src/lib/fences/code-output.tsx @@ -1,14 +1,22 @@ import { JSX, ReactNode } from 'react'; +import cx from 'classnames'; export function CodeOutput({ content, fileName, + isWithinTab, }: { content: ReactNode; fileName: string; + isWithinTab?: boolean; }): JSX.Element { return ( -
+
{!!fileName && (
{fileName} diff --git a/nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx b/nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx index f7d2996bed..47c11fe26c 100644 --- a/nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx +++ b/nx-dev/ui-markdoc/src/lib/nodes/fence-wrapper.component.tsx @@ -34,7 +34,7 @@ export function FenceWrapper(props: FenceProps) { }; return ( -
+
); diff --git a/nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx b/nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx index 7843cd2146..fd5d75d548 100644 --- a/nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx +++ b/nx-dev/ui-markdoc/src/lib/tags/tabs.component.tsx @@ -1,12 +1,12 @@ 'use client'; -// TODO@ben: refactor to use HeadlessUI tabs import cx from 'classnames'; -import { +import React, { createContext, ReactNode, useContext, useEffect, useState, + cloneElement, } from 'react'; export const TabContext = createContext(''); @@ -20,7 +20,8 @@ export function Tabs({ labels: string[]; children: ReactNode; }) { - const [currentTab, setCurrentTab] = useState(labels[0]); + const [currentTab, setCurrentTab] = useState(labels[0]); + useEffect(() => { const handleTabSelectedEvent = () => { const selectedTab = localStorage.getItem(SELECTED_TAB_KEY); @@ -28,43 +29,49 @@ export function Tabs({ setCurrentTab(selectedTab); } }; + handleTabSelectedEvent(); window.addEventListener(TAB_SELECTED_EVENT, handleTabSelectedEvent); return () => window.removeEventListener(TAB_SELECTED_EVENT, handleTabSelectedEvent); }, [labels]); + const handleTabClick = (label: string) => { + localStorage.setItem(SELECTED_TAB_KEY, label); + window.dispatchEvent(new Event(TAB_SELECTED_EVENT)); + setCurrentTab(label); + }; + return ( -
-
-
- -
-
+ +
{children} -
+
); } @@ -77,14 +84,23 @@ export function Tab({ children: ReactNode; }) { const currentTab = useContext(TabContext); + const isActive = label === currentTab; - if (label !== currentTab) { - return null; - } + const passPropsToChildren = (children: ReactNode) => { + return React.Children.map(children, (child) => { + if (React.isValidElement(child) && typeof child.type !== 'string') { + return cloneElement(child, { isWithinTab: true }); + } + return child; + }); + }; return ( -
- {children} + ); }