implement some requested changes

This commit is contained in:
Gimle Larpes 2025-07-04 19:23:46 +02:00
parent 780a3ecaee
commit 4ce825097d
3 changed files with 69 additions and 57 deletions

View file

@ -0,0 +1,37 @@
import { useCallback, useEffect, useRef, useState } from 'react';
/**
* Temporarily sets a boolean state.
*
* @param duration - Duration in milliseconds before resetting (default: 1500)
* @param initial - Initial value (default: false)
*/
export function useTimeoutToggle(duration = 1500, initial = false): [boolean, () => void] {
const [active, setActive] = useState(initial);
const timeoutRef = useRef<number | null>(null);
const clear = () => {
if (timeoutRef.current !== null) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};
const trigger = useCallback(() => {
setActive(!initial);
clear();
timeoutRef.current = window.setTimeout(() => {
setActive(initial);
timeoutRef.current = null;
}, duration);
}, [duration, initial]);
useEffect(
() => () => {
clear();
},
[]
);
return [active, trigger];
}

View file

@ -41,6 +41,7 @@ import {
} from './matrix-to'; } from './matrix-to';
import { onEnterOrSpace } from '../utils/keyboard'; import { onEnterOrSpace } from '../utils/keyboard';
import { copyToClipboard, tryDecodeURIComponent } from '../utils/dom'; import { copyToClipboard, tryDecodeURIComponent } from '../utils/dom';
import { useTimeoutToggle } from '../hooks/useTimeoutToggle';
const ReactPrism = lazy(() => import('./react-prism/ReactPrism')); const ReactPrism = lazy(() => import('./react-prism/ReactPrism'));
@ -204,51 +205,6 @@ export const highlightText = (
); );
}); });
type CodeBlockControlsProps = {
copied: boolean;
onCopy: () => void;
collapsible: boolean;
collapsed: boolean;
onToggle: () => void;
};
function CodeBlockControls({
copied,
onCopy,
collapsible,
collapsed,
onToggle,
}: CodeBlockControlsProps) {
return (
// Needs a better copy icon
<div className={css.CodeBlockControls}>
<IconButton
variant="Secondary"
size="300"
radii="300"
onClick={onCopy}
aria-label="Copy Code Block"
>
<Icon src={copied ? Icons.Check : Icons.File} size="50" />
</IconButton>
{collapsible && (
<IconButton
variant="Secondary"
size="300"
radii="300"
onClick={onToggle}
aria-expanded={!collapsed}
aria-controls="code-block-content"
aria-label={collapsed ? 'Show Full Code Block' : 'Show Code Block Preview'}
style={collapsed ? { visibility: 'visible' } : {}}
>
<Icon src={collapsed ? Icons.ChevronRight : Icons.ChevronBottom} size="50" />
</IconButton>
)}
</div>
);
}
export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) { export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
const LINE_LIMIT = 14; const LINE_LIMIT = 14;
@ -272,7 +228,7 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
return text; return text;
}, []); }, []);
const [copied, setCopied] = useState(false); const [copied, setCopied] = useTimeoutToggle();
const collapsible = useMemo( const collapsible = useMemo(
() => extractTextFromChildren(children).split('\n').length > LINE_LIMIT, () => extractTextFromChildren(children).split('\n').length > LINE_LIMIT,
[children, extractTextFromChildren] [children, extractTextFromChildren]
@ -281,8 +237,8 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
const handleCopy = useCallback(() => { const handleCopy = useCallback(() => {
copyToClipboard(extractTextFromChildren(children)); copyToClipboard(extractTextFromChildren(children));
setCopied(true); setCopied();
}, [children, extractTextFromChildren]); }, [children, extractTextFromChildren, setCopied]);
const toggleCollapse = useCallback(() => { const toggleCollapse = useCallback(() => {
setCollapsed((prev) => !prev); setCollapsed((prev) => !prev);
@ -290,13 +246,32 @@ export function CodeBlock(children: ChildNode[], opts: HTMLReactParserOptions) {
return ( return (
<> <>
<CodeBlockControls <div className={css.CodeBlockControls}>
copied={copied} <IconButton
onCopy={handleCopy} variant="Secondary" // Needs a better copy icon
collapsible={collapsible} size="300"
collapsed={collapsed} radii="300"
onToggle={toggleCollapse} onClick={handleCopy}
/> aria-label="Copy Code Block"
>
<Icon src={copied ? Icons.Check : Icons.File} size="50" />
</IconButton>
{collapsible && (
<IconButton
variant="Secondary"
size="300"
radii="300"
onClick={toggleCollapse}
aria-expanded={!collapsed}
aria-pressed={!collapsed}
aria-controls="code-block-content"
aria-label={collapsed ? 'Show Full Code Block' : 'Show Code Block Preview'}
style={collapsed ? { visibility: 'visible' } : {}}
>
<Icon src={collapsed ? Icons.ChevronBottom : Icons.ChevronTop} size="50" />
</IconButton>
)}
</div>
<Scroll <Scroll
direction={collapsed ? 'Both' : 'Horizontal'} direction={collapsed ? 'Both' : 'Horizontal'}
variant="Secondary" variant="Secondary"

View file

@ -91,12 +91,12 @@ export const CodeBlock = style([
export const CodeBlockInternal = recipe({ export const CodeBlockInternal = recipe({
base: { base: {
padding: `${config.space.S200} ${config.space.S200} 0`, padding: `${config.space.S200} ${config.space.S200} 0`,
minWidth: config.size.ModalDrawerWidth, minWidth: toRem(100),
}, },
variants: { variants: {
collapsed: { collapsed: {
true: { true: {
maxHeight: `calc(${config.size.ModalDrawerWidth} - ${config.lineHeight.T400} / 2)`, maxHeight: `calc(${config.lineHeight.T400} * 9.6)`,
}, },
}, },
}, },