Skip to content

Commit

Permalink
Merge 6bab8c4 into 37cc019
Browse files Browse the repository at this point in the history
  • Loading branch information
zchsh committed Aug 3, 2023
2 parents 37cc019 + 6bab8c4 commit 19f04bc
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-impalas-speak.md
@@ -0,0 +1,5 @@
---
'@hashicorp/react-code-block': minor
---

Implements support for code wrapping, through an options.wrapCode boolean property.
34 changes: 31 additions & 3 deletions packages/code-block/docs.mdx
Expand Up @@ -34,15 +34,43 @@ Longer lines of code may take up more space than the available content width. In
````
```
A line that goes on for a very long time so that it overflows the container in which it is located, which might be a pretty wide container.
This is a second line of code.
```
````

`Result`

<CodeBlock
options={{ showClipboard: true }}
theme="dark"
code={`A line that goes on for a very long time so that it overflows the container in which it is located, which might be a pretty wide container.`}
theme="light"
code={`A line that goes on for a very long time so that it overflows the container in which it is located, which might be a pretty wide container.\nThis is a second line of code.`}
/>

<CodeBlock
options={{ showClipboard: true, lineNumbers: true }}
theme="light"
code={`A line that goes on for a very long time so that it overflows the container in which it is located, which might be a pretty wide container.\nThis is a second line of code.`}
/>

#### Wrap Code

In cases where wrapping code to new lines is preferred over horizontal scrolling, the `options.wrapCode` prop can be set to `true`. Note that this option is not yet supported in MDX contexts.

`Source`

````
```
A line that goes on for a very long time so that it would overflow the container in which it is located, which might be a pretty wide container, but it wraps instead.
This is a second line of code.
```
````

`Result`

<CodeBlock
options={{ showClipboard: true, wrapCode: true, lineNumbers: true }}
theme="light"
code={`A line that goes on for a very long time so that it would overflow the container in which it is located, which might be a pretty wide container, but it wraps instead.\nThis is a second line of code.`}
/>

#### Syntax Highlighting
Expand Down Expand Up @@ -219,7 +247,7 @@ function hello() {
options={{ lineNumbers: true, showClipboard: true }}
code={`<span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token string">'bar'</span>
<span class="token keyword">function</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
{/* */} <span class="token keyword control-flow">return</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">0.5</span> <span class="token operator">?</span> <span class="token string">'Hello'</span> <span class="token operator">:</span> <span class="token string">'Bonjour'</span>
<span class="token keyword control-flow">return</span> <span class="token known-class-name class-name">Math</span><span class="token punctuation">.</span><span class="token method function property-access">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">0.5</span> <span class="token operator">?</span> <span class="token string">'Hello'</span> <span class="token operator">:</span> <span class="token string">'Bonjour'</span>
<span class="token punctuation">}</span>`}
/>

Expand Down
4 changes: 4 additions & 0 deletions packages/code-block/index.tsx
Expand Up @@ -27,6 +27,7 @@ export interface CodeBlockOptions {
showWindowBar?: boolean
filename?: string
heading?: string
wrapCode?: boolean
}

export interface CodeBlockProps {
Expand All @@ -52,6 +53,7 @@ function CodeBlock({
lineNumbers: false,
showClipboard: false,
showWindowBar: false,
wrapCode: false,
},
}: CodeBlockProps) {
const copyRef = useRef<HTMLPreElement>()
Expand All @@ -76,6 +78,7 @@ function CodeBlock({
lineNumbers,
showClipboard,
showWindowBar,
wrapCode,
} = options
if (showWindowBar) {
console.warn(
Expand Down Expand Up @@ -119,6 +122,7 @@ function CodeBlock({
highlight={highlight}
lineNumbers={lineNumbers}
hasFloatingCopyButton={hasFloatingCopyButton}
wrapCode={wrapCode}
/>
{hasFloatingCopyButton ? (
<div className={s.copyButtonContainer}>
Expand Down
100 changes: 85 additions & 15 deletions packages/code-block/partials/code-lines/index.js
Expand Up @@ -16,6 +16,7 @@ function CodeLines({
lineNumbers,
highlight,
hasFloatingCopyButton,
wrapCode,
}) {
const linesOfCode = useMemo(() => {
const isHtmlString = typeof code === 'string'
Expand All @@ -28,7 +29,7 @@ function CodeLines({
return (
<pre className={classNames(s.pre, `language-${language}`)}>
<code className={classNames(s.code, `language-${language}`)}>
{lineNumbers ? (
{lineNumbers && !wrapCode ? (
<span className={s.numbersColumn}>
{linesOfCode.map((_lineChildren, stableIdx) => {
const number = stableIdx + 1
Expand All @@ -44,46 +45,113 @@ function CodeLines({
lineCount={lineCount}
isHighlighted={isHighlighted}
isNotHighlighted={isNotHighlighted}
wrapCode={false}
/>
)
})}
</span>
) : null}
<span className={classNames(s.linesColumn, s.styledScrollbars)}>
<span className={s.linesWrapper}>
<span
className={classNames(s.linesColumn, s.styledScrollbars, {
[s.wrapCode]: wrapCode,
})}
>
{wrapCode ? (
<LineSpacer
lineCount={lineCount}
hasFloatingCopyButton={hasFloatingCopyButton}
/>
) : null}
<span
className={classNames(s.linesWrapper, { [s.wrapCode]: wrapCode })}
>
{linesOfCode.map((lineChildren, stableIdx) => {
const number = stableIdx + 1
const isHighlighted = highlightedLines.indexOf(number) !== -1
const isNotHighlighted = highlightedLines.length && !isHighlighted
return (
<LineOfCode
// This array is stable, so we can use index as key
// eslint-disable-next-line react/no-array-index-key
key={stableIdx}
isHighlighted={isHighlighted}
isNotHighlighted={isNotHighlighted}
hasFloatingCopyButton={hasFloatingCopyButton}
>
{lineChildren}
{'\n'}
</LineOfCode>
// This array is stable, so we can use index as key
// eslint-disable-next-line react/no-array-index-key
<div className={s.lineWrapper} key={stableIdx}>
{lineNumbers && wrapCode ? (
<LineNumber
// This array is stable, so we can use index as key
// eslint-disable-next-line react/no-array-index-key
key={stableIdx}
number={number}
lineCount={lineCount}
isHighlighted={isHighlighted}
isNotHighlighted={isNotHighlighted}
wrapCode={true}
/>
) : null}
<LineOfCode
isHighlighted={isHighlighted}
isNotHighlighted={isNotHighlighted}
hasFloatingCopyButton={hasFloatingCopyButton}
wrapCode={wrapCode}
>
{lineChildren}
{'\n'}
</LineOfCode>
</div>
)
})}
</span>
{wrapCode ? (
<LineSpacer
lineCount={lineCount}
hasFloatingCopyButton={hasFloatingCopyButton}
/>
) : null}
</span>
</code>
</pre>
)
}

function LineNumber({ number, isHighlighted, isNotHighlighted, lineCount }) {
function LineSpacer({ lineCount, hasFloatingCopyButton }) {
return (
<div
className={classNames(s.linesSpacer, {
[s.wrapCode]: true,
})}
>
<LineNumber
number=""
lineCount={lineCount}
isHighlighted={false}
isNotHighlighted={true}
wrapCode={true}
/>
<LineOfCode
isHighlighted={false}
isNotHighlighted={true}
hasFloatingCopyButton={hasFloatingCopyButton}
wrapCode={true}
>
{''}
{'\n'}
</LineOfCode>
</div>
)
}

function LineNumber({
number,
isHighlighted,
isNotHighlighted,
lineCount,
wrapCode,
}) {
const padLevel = Math.max(lineCount.toString().length, 1)
const paddedNumber = number.toString().padEnd(padLevel)
return (
<span
className={classNames(s.LineNumber, {
[s.isHighlighted]: isHighlighted,
[s.isNotHighlighted]: isNotHighlighted,
[s.wrapCode]: wrapCode,
})}
dangerouslySetInnerHTML={{ __html: paddedNumber }}
/>
Expand All @@ -95,13 +163,15 @@ function LineOfCode({
isHighlighted,
isNotHighlighted,
hasFloatingCopyButton,
wrapCode,
}) {
return (
<span
className={classNames(s.LineOfCode, {
[s.isHighlighted]: isHighlighted,
[s.isNotHighlighted]: isNotHighlighted,
[s.hasFloatingCopyButton]: hasFloatingCopyButton,
[s.wrapCode]: wrapCode,
})}
>
{children}
Expand Down
43 changes: 43 additions & 0 deletions packages/code-block/partials/code-lines/style.module.css
Expand Up @@ -54,6 +54,10 @@ pre.pre {
*/
overflow-x: auto;
overflow-y: hidden;

&.wrapCode {
padding: 0;
}
}

.styledScrollbars {
Expand Down Expand Up @@ -82,6 +86,10 @@ pre.pre {
min-width: 100%;
flex-direction: column;
flex-shrink: 0;

&.wrapCode {
width: 100%;
}
}

.numbersColumn {
Expand All @@ -100,6 +108,10 @@ pre.pre {
padding: 0 var(--code-padding);
color: var(--text-color-faded);

&.wrapCode {
border-right: 1px solid var(--divider-line-color);
}

&.isHighlighted {
background: var(--background-highlighted-line);
color: var(--text-color-base);
Expand All @@ -121,6 +133,12 @@ pre.pre {
min-width: max-content;
white-space: pre;

&.wrapCode {
white-space: pre-wrap;
overflow-wrap: break-word;
min-width: 0;
}

&.hasFloatingCopyButton {
/* Adds right padding so that the floating copy button
does not obscure content at the end of the line */
Expand All @@ -143,3 +161,28 @@ pre.pre {
}
}
}

/*
Additions to make code-wrap work as expected
TODO: refactor this whole component so there's less conditional stuff going on.
*/

.lineWrapper {
display: flex;
flex-wrap: nowrap;
}

/* adds space at the top and bottom of the block,
while supporting a continuous border for line numbers */
.linesSpacer {
display: flex;
flex-wrap: nowrap;
height: var(--code-padding);

&.wrapCode {
display: flex;
}
}

0 comments on commit 19f04bc

Please sign in to comment.