diff --git a/src/components/code-block/code-block.stories.tsx b/src/components/code-block/code-block.stories.tsx index 8a6b6127..49c19aaa 100644 --- a/src/components/code-block/code-block.stories.tsx +++ b/src/components/code-block/code-block.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockHeader } from "."; +import { CodeBlock, CodeBlockBody, CodeBlockContent, CodeBlockCopyButton, CodeBlockHeader, CodeBlockTitle } from "."; const meta = { title: "CodeBlock", @@ -25,13 +25,47 @@ const CodeIcon = () => ( export const WithHeaderHighlightsAndHighlights: Story = { render: () => ( -
+
- hello.js + ngrok-example.js + + + {` +const http = require('http'); +const ngrok = require("@ngrok/ngrok"); + +const server = http.createServer((req, res) => { + res.writeHead(200); + res.end("Hello!"); +}); + +// Consumes authtoken from env automatically +ngrok.listen(server).then(() => { + console.log("url:", server.tunnel.url()); +}); +// really long line here that should wrap around and stuff Officia ipsum sint eu labore esse deserunt aliqua quis irure. + `} + + + +
+ ), +}; + +export const WithHeaderHighlightsAndHighlightsScrollyBoi: Story = { + render: () => ( +
+ + + + ngrok-example.js + + + {` const http = require('http'); @@ -56,11 +90,11 @@ ngrok.listen(server).then(() => { export const WithHeaderAndLineNumbers: Story = { render: () => ( -
+
- hello.js + ngrok-example.js @@ -77,6 +111,7 @@ const server = http.createServer((req, res) => { ngrok.listen(server).then(() => { console.log("url:", server.tunnel.url()); }); +// really long line here that should wrap around and stuff Officia ipsum sint eu labore esse deserunt aliqua quis irure. `} @@ -87,11 +122,11 @@ ngrok.listen(server).then(() => { export const WithHeaderNoLineNumbers: Story = { render: () => ( -
+
- hello.js + ngrok-example.js @@ -118,7 +153,7 @@ ngrok.listen(server).then(() => { export const WithoutHeaderNoLineNumbers: Story = { render: () => ( -
+
@@ -145,7 +180,7 @@ ngrok.listen(server).then(() => { export const WithoutHeaderNoLineNumbersButHighlights: Story = { render: () => ( -
+
diff --git a/src/components/code-block/index.tsx b/src/components/code-block/index.tsx index 0aded13f..85bdbde3 100644 --- a/src/components/code-block/index.tsx +++ b/src/components/code-block/index.tsx @@ -1,9 +1,19 @@ -import { PropsWithChildren, createContext, forwardRef, useContext, useEffect, useState } from "react"; +import { HTMLAttributes, createContext, forwardRef, useContext, useEffect, useState } from "react"; import type { WithStyleProps } from "@/types/with-style-props"; import { cx } from "@/lib/cx"; import { SupportedLanguage } from "./utils/supported-languages"; import { formatLanguageClassName } from "./utils/format-language-classname"; import { LineRange, resolveLineNumbers } from "./utils/line-numbers"; +import { Slot } from "@radix-ui/react-slot"; + +/** + * TODO(cody): + * - implement syntax highlighting w/ prism or highlightjs (spike on both, figure out which is easier/less bs) + * - fix overflow-y-auto on CodeBlockBody + * - fix line numbers, maybe try grid instead of :before and flex? + * - fix line hightlighting + * - fix line wrapping? horizontal scrolling has problems w/ line highlighting :( + */ type CodeBlockContextType = (newCopyText: string) => void; @@ -11,30 +21,25 @@ const CodeBlockContext = createContext(() => {}); const CodeBlockCopyContext = createContext(""); -type CodeBlockProps = PropsWithChildren & WithStyleProps; - -const CodeBlock = forwardRef(({ className, children, style }, ref) => { +const CodeBlock = forwardRef>(({ className, ...props }, ref) => { const [copyText, setCopyText] = useState(""); return ( -
- {children} -
+
); }); CodeBlock.displayName = "CodeBlock"; -type CodeBlockBodyProps = WithStyleProps & PropsWithChildren; - -const CodeBlockBody = forwardRef(({ className, children, style }, ref) => ( -
- - {children} -
+const CodeBlockBody = forwardRef>(({ className, ...props }, ref) => ( +
)); CodeBlockBody.displayName = "CodeBlockBody"; @@ -45,43 +50,38 @@ type CodeBlockContentProps = WithStyleProps & { showLineNumbers?: boolean; }; -const CodeBlockContent = forwardRef( - ({ children, className, highlightLines, language, showLineNumbers, style }, ref) => { - const setCopyText = useContext(CodeBlockContext); +const CodeBlockContent = forwardRef((props, ref) => { + const { children, className, /* highlightLines, */ language = "sh", /* showLineNumbers = false, */ style } = props; + const highlightLines = undefined; // debug only, punting for now + const showLineNumbers = false; // debug only, punting for now - // trim any leading and trailing whitespace/empty lines - const trimmedCode = children?.trim() ?? ""; - const lines = trimmedCode.split("\n"); + const setCopyText = useContext(CodeBlockContext); - const highlightLineNumberSet = resolveLineNumbers(...(highlightLines ?? [])); + // trim any leading and trailing whitespace/empty lines + const trimmedCode = children?.trim() ?? ""; + const lines = trimmedCode.split("\n"); - useEffect(() => { - setCopyText(trimmedCode); - }, [trimmedCode, setCopyText]); + const highlightLineNumberSet = resolveLineNumbers(...(highlightLines ?? [])); - return ( -
-				{showLineNumbers && (
-					
- {lines.map((line, index) => { - const lineNumber = index + 1; - const shouldHighlight = highlightLineNumberSet.has(lineNumber); - - return ( - - {lineNumber} - - ); - })} -
- )} - + useEffect(() => { + setCopyText(trimmedCode); + }, [trimmedCode, setCopyText]); + + return ( +
+			{/* TODO(cody): maybe retry this, but use grid instead? */}
+			{/* {showLineNumbers && (
+				
{lines.map((line, index) => { const lineNumber = index + 1; const shouldHighlight = highlightLineNumberSet.has(lineNumber); @@ -89,36 +89,63 @@ const CodeBlockContent = forwardRef( return ( - {line === "" ? "\n" : line} + {lineNumber} ); })} - -
- ); - }, -); +
+ )} */} + + {lines.map((line, index) => { + const lineNumber = index + 1; + const shouldHighlight = highlightLineNumberSet.has(lineNumber); + + return ( + + {line === "" ? "\n" : line} + + ); + })} + + + ); +}); CodeBlockContent.displayName = "CodeBlockContent"; -const CodeBlockHeader = forwardRef( - ({ children, className, style }, ref) => ( -
- {children} -
- ), -); +const CodeBlockHeader = forwardRef>(({ className, ...props }, ref) => ( +
+)); CodeBlockHeader.displayName = "CodeBlockHeader"; +const CodeBlockTitle = forwardRef & { asChild?: boolean }>( + ({ asChild = false, className, ...props }, ref) => { + const Comp = asChild ? Slot : "h3"; + return ; + }, +); +CodeBlockTitle.displayName = "CodeBlockTitle"; + type CodeBlockCopyButtonProps = WithStyleProps & { onCopy?: (value: string) => void; onCopyError?: (error: unknown) => void; @@ -126,21 +153,21 @@ type CodeBlockCopyButtonProps = WithStyleProps & { const CodeBlockCopyButton = forwardRef( ({ className, onCopy, onCopyError, style }, ref) => { - const ctx = useContext(CodeBlockCopyContext); - const [, setCopied] = useState(false); + const copyText = useContext(CodeBlockCopyContext); + const [, setCopied] = useState(false); // todo: useme return (