Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 78 additions & 6 deletions src/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,93 @@ import ReactMarkdown, { type Options } from 'react-markdown';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
import { Tabs } from 'design-system/tabs';

const useTab = (children: ReactNode) => {
const [activeTab, setActiveTab] = React.useState<number>(0);

const content = React.useMemo(() => children?.toString() || ``, [children]);
const hasTabs = content.includes(`@@@`);

const parseTabContent = React.useCallback((content: string): string[] => {
const panels = content.split(`@@@`);
return panels[0].trim() === `` ? panels.slice(1) : panels;
}, []);

const tabs = React.useMemo(
() => (hasTabs ? parseTabContent(content) : []),
[hasTabs, content, parseTabContent],
);

return {
activeTab,
setActiveTab,
hasTabs,
content,
tabs,
};
};

type TabCodeProps = {
tabs: string[];
activeTab: number;
setActiveTab: React.Dispatch<React.SetStateAction<number>>;
};

const TabCode = ({ tabs, activeTab, setActiveTab }: TabCodeProps) => {
return (
<>
<Tabs fit>
{tabs.map((_, index) => (
<Tabs.Item
active={activeTab === index}
key={index}
onClick={() => setActiveTab(index)}
>
{`Tab ${index + 1}`}
</Tabs.Item>
))}
</Tabs>
{tabs.map((tab, index) => (
<code
key={index}
ref={(el) => {
if (index === activeTab && el) {
highlightElement(el);
}
}}
className={c(`language-javascript`, index !== activeTab && `hidden`)}
>
{tab}
</code>
))}
</>
);
};

const Code = ({
children,
}: DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>) => {
const ref = React.useRef<HTMLElement | null>(null);
const { hasTabs, tabs, activeTab, setActiveTab } = useTab(children);

React.useLayoutEffect(() => {
const container = ref.current;
container && highlightElement(container);
}, [children]);
if (container && !hasTabs) {
highlightElement(container);
}
}, [children, hasTabs]);

if (!hasTabs) {
return (
<code ref={ref} className="language-javascript">
{children}
</code>
);
}

return (
<code ref={ref} className="language-javascript">
{children}
</code>
<TabCode tabs={tabs} activeTab={activeTab} setActiveTab={setActiveTab} />
);
};

Expand All @@ -42,7 +114,7 @@ const SnippetCopyButton = ({ children }: { children: ReactNode }) => {

return (
<Button
className="absolute right-3 top-3 opacity-0 group-hover:opacity-100 transition-opacity"
className="absolute right-3 top-3 opacity-0 group-hover:opacity-100 transition-opacity z-10"
i={2}
s={1}
title="Copy snippet"
Expand Down
18 changes: 17 additions & 1 deletion src/features/creator/components/creator-toolbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
BiMath,
BiSolidQuoteAltLeft,
BiStrikethrough,
BiWindow,
} from 'react-icons/bi';

type CreatorToolboxSyntaxKey =
Expand All @@ -26,7 +27,8 @@ type CreatorToolboxSyntaxKey =
| `link`
| `ol`
| `ul`
| `todo`;
| `todo`
| `tab`;

type CreatorToolboxProps = {
creator: HTMLTextAreaElement | null;
Expand Down Expand Up @@ -72,6 +74,11 @@ const CreatorToolbox = ({ creator, onClick }: CreatorToolboxProps) => {
todo: () => {
onClick(`- [x] a\n- [ ] b\n- [x] c`);
},
tab: () => {
onClick(
`\`\`\`\n@@@\n// Tab name: Tab 1\n\n@@@\n// Tab name: Empty Tab\n\n@@@\n// Tab name: Tab 3\nconsole.log("Hello world");\n\`\`\``,
);
},
};

operationsMap[syntaxKey]();
Expand Down Expand Up @@ -133,6 +140,15 @@ const CreatorToolbox = ({ creator, onClick }: CreatorToolboxProps) => {
>
<BiCode size={20} />
</Button>
<Button
s="auto"
className="p-1"
i={1}
title="Tab Separator"
onClick={insertMarkdownSyntax(`tab`)}
>
<BiWindow size={20} />
</Button>
<Button
s="auto"
className="p-1"
Expand Down
Loading