refactor(theme): introduce CodeBlockContextProvider + split into smaller components (#11062)

* introduce CodeBlockContextProvider

* refactor: apply lint autofix

* add comment

* move wordWrap to context

* Refactor button components

* remove console logs

* Extract more code block components

* Extract CodeBlockLineToken subcomponent

* add TODOs

---------

Co-authored-by: slorber <749374+slorber@users.noreply.github.com>
This commit is contained in:
Sébastien Lorber 2025-04-04 20:11:40 +02:00 committed by GitHub
parent d28210d35b
commit 31b279fea6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 474 additions and 243 deletions

View file

@ -52,12 +52,14 @@ function useTabBecameVisibleCallback(
);
}
export function useCodeWordWrap(): {
export type WordWrap = {
readonly codeBlockRef: RefObject<HTMLPreElement>;
readonly isEnabled: boolean;
readonly isCodeScrollable: boolean;
readonly toggle: () => void;
} {
};
export function useCodeWordWrap(): WordWrap {
const [isEnabled, setIsEnabled] = useState(false);
const [isCodeScrollable, setIsCodeScrollable] = useState<boolean>(false);
const codeBlockRef = useRef<HTMLPreElement>(null);

View file

@ -37,6 +37,8 @@ export {
type CodeBlockMetadata,
createCodeBlockMetadata,
getPrismCssVariables,
CodeBlockContextProvider,
useCodeBlockContext,
} from './utils/codeBlockUtils';
export {DEFAULT_SEARCH_TAG} from './utils/searchUtils';

View file

@ -6,9 +6,12 @@
*/
import type {CSSProperties, ReactNode} from 'react';
import {createContext, useContext, useMemo} from 'react';
import clsx from 'clsx';
import rangeParser from 'parse-numeric-range';
import {ReactContextError} from './reactUtils';
import type {PrismTheme, PrismThemeEntry} from 'prism-react-renderer';
import type {WordWrap} from '../hooks/useCodeWordWrap';
const codeBlockTitleRegex = /title=(?<quote>["'])(?<title>.*?)\1/;
const metastringLinesRangeRegex = /\{(?<range>[\d,-]+)\}/;
@ -322,9 +325,6 @@ export function parseLines(
const newCode = code.replace(/\r?\n$/, '');
// Historical behavior: we try one strategy after the other
// we don't support mixing metastring ranges + magic comments
console.log('params', {params, code});
console.log('from meta', parseCodeLinesFromMetastring(newCode, {...params}));
console.log('from content', parseCodeLinesFromContent(newCode, {...params}));
return (
parseCodeLinesFromMetastring(newCode, {...params}) ??
parseCodeLinesFromContent(newCode, {...params})
@ -460,3 +460,39 @@ export function getPrismCssVariables(prismTheme: PrismTheme): CSSProperties {
});
return properties;
}
type CodeBlockContextValue = {
metadata: CodeBlockMetadata;
wordWrap: WordWrap;
};
const CodeBlockContext = createContext<CodeBlockContextValue | null>(null);
export function CodeBlockContextProvider({
metadata,
wordWrap,
children,
}: {
metadata: CodeBlockMetadata;
wordWrap: WordWrap;
children: ReactNode;
}): ReactNode {
// Should we optimize this in 2 contexts?
// Unlike metadata, wordWrap is stateful and likely to trigger re-renders
const value: CodeBlockContextValue = useMemo(() => {
return {metadata, wordWrap};
}, [metadata, wordWrap]);
return (
<CodeBlockContext.Provider value={value}>
{children}
</CodeBlockContext.Provider>
);
}
export function useCodeBlockContext(): CodeBlockContextValue {
const value = useContext(CodeBlockContext);
if (value === null) {
throw new ReactContextError('CodeBlockContextProvider');
}
return value;
}