@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
109 lines (106 loc) • 4.01 kB
JavaScript
import * as React from 'react';
import { stringOrHastToString } from "../pipeline/hastUtils/index.mjs";
import { useCopier } from "../useCopier/index.mjs";
import { generateVariantMarkdown } from "./generateVariantMarkdown.mjs";
export function collectVariantFiles(selectedVariant, transformedFiles, fallbacks) {
if (!selectedVariant) {
return [];
}
// Resolve per-file DEFLATE dictionaries from both places a fallback can
// arrive (mirrors `resolvedFallbacks` in `useFileNavigation`): the passed
// `fallbacks` (hoisted from a `ContentLoading` component) and the variant's
// own per-file `fallback` fields (kept on `Code` when not stripped — e.g. the
// standalone `useCode`/`useDemo` path with no `CodeHighlighter` context, so
// `context?.fallbacks` is undefined). Without this merge, copy-as-markdown
// throws on a `hastCompressed` source whose dictionary lives on the
// `VariantCode`. The variant copy wins so the full text — the dictionary
// `hastCompressed` needs — is used when a `fallbackCollapsed` block hoisted
// only the visible window.
const resolvedFallbacks = {
...fallbacks
};
if (selectedVariant.fileName && selectedVariant.fallback) {
resolvedFallbacks[selectedVariant.fileName] = selectedVariant.fallback;
}
for (const [name, fileData] of Object.entries(selectedVariant.extraFiles || {})) {
if (typeof fileData === 'object' && fileData?.fallback) {
resolvedFallbacks[name] = fileData.fallback;
}
}
// When a transform has produced files, prefer them so the copied snippet
// matches what the user is currently viewing. Files the transform actually
// rewrote are live HAST (already decoded), but files it left untouched are
// passed through as their ORIGINAL source, which may still be `hastCompressed`
// and needs its dictionary — resolved here by `originalName`.
if (transformedFiles && transformedFiles.files.length > 0) {
return transformedFiles.files.map(file => ({
name: file.name,
source: stringOrHastToString(file.source, resolvedFallbacks[file.originalName])
}));
}
const files = [];
if (selectedVariant.fileName && selectedVariant.source !== undefined) {
files.push({
name: selectedVariant.fileName,
// `resolvedFallbacks` is the active variant's per-file dictionary map, so
// it decodes a `hastCompressed` source in both the hoisted and the
// on-`VariantCode` cases.
source: stringOrHastToString(selectedVariant.source, resolvedFallbacks[selectedVariant.fileName])
});
}
if (selectedVariant.extraFiles) {
for (const [name, fileData] of Object.entries(selectedVariant.extraFiles)) {
if (typeof fileData === 'string') {
files.push({
name,
source: fileData
});
} else if (fileData && typeof fileData === 'object' && fileData.source !== undefined) {
files.push({
name,
source: stringOrHastToString(fileData.source, resolvedFallbacks[name])
});
}
}
}
return files;
}
/**
* Hook for managing copy-to-clipboard functionality
*/
export function useCopyFunctionality({
selectedFile,
selectedVariant,
transformedFiles,
fallbacks,
selectedFileFallback,
title,
copyOpts
}) {
const sourceFileToText = React.useCallback(() => {
if (!selectedFile) {
return undefined;
}
return stringOrHastToString(selectedFile, selectedFileFallback);
}, [selectedFile, selectedFileFallback]);
const variantToMarkdown = React.useCallback(() => {
const files = collectVariantFiles(selectedVariant, transformedFiles, fallbacks);
if (files.length === 0) {
return undefined;
}
return generateVariantMarkdown({
title,
files
});
}, [selectedVariant, transformedFiles, fallbacks, title]);
const {
copy
} = useCopier(sourceFileToText, copyOpts);
const {
copy: copyMarkdown
} = useCopier(variantToMarkdown, copyOpts);
return {
copy,
copyMarkdown
};
}