@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
191 lines (171 loc) • 7.17 kB
JavaScript
import * as React from 'react';
import kebabCase from 'kebab-case';
import { useCode } from "../useCode/index.mjs";
import { CodeHighlighterContext } from "../CodeHighlighter/CodeHighlighterContext.mjs";
import { createStackBlitz } from "./createStackBlitz.mjs";
import { createCodeSandbox } from "./createCodeSandbox.mjs";
import { exportVariant } from "./exportVariant.mjs";
import { exportVariantAsCra } from "./exportVariantAsCra.mjs";
import { flattenCodeVariant } from "../pipeline/loadIsomorphicCodeVariant/flattenCodeVariant.mjs";
/**
* Demo templates use the exportVariant/exportVariantAsCra with flattenCodeVariant pattern:
*
* For StackBlitz:
* const { exported: exportedVariant, entrypoint } = exportVariant(variantCode);
* const flattenedFiles = flattenCodeVariant(exportedVariant);
* createStackBlitzDemo({ title, description, flattenedFiles, useTypescript, initialFile: entrypoint })
*
* For CodeSandbox:
* const { exported: craExport, entrypoint } = exportVariantAsCra(variantCode, { title, description, useTypescript });
* const flattenedFiles = flattenCodeVariant(craExport);
* createCodeSandboxDemo({ title, description, flattenedFiles, useTypescript, initialFile: entrypoint })
* createCodeSandboxDemo({ title, description, flattenedFiles, useTypescript })
*/
/**
* Helper to create HTML form element with hidden inputs
*/
export function addHiddenInput(form, name, value) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
form.appendChild(input);
}
/**
* Generic function to create and submit a form for opening online demo platforms
* This function creates HTML elements and should be used in browser contexts
*/
export function openWithForm({
url,
formData,
method = 'POST',
target = '_blank'
}) {
const form = document.createElement('form');
form.method = method;
form.target = target;
form.action = url;
Object.entries(formData).forEach(([name, value]) => {
addHiddenInput(form, name, value);
});
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
// TODO: take initialVariant and initialTransforms as parameters
export function useDemo(contentProps, opts) {
const code = useCode(contentProps, opts);
// Extract export configuration options
const {
export: commonExportConfig = {},
exportStackBlitz: stackBlitzExportConfig = {},
exportCodeSandbox: codeSandboxExportConfig = {}
} = opts || {};
// Get context to access components if available (using React.useContext to avoid import conflicts)
const context = React.useContext(CodeHighlighterContext);
const slug = React.useMemo(() => contentProps.slug || (contentProps.name ? kebabCase(contentProps.name, false) : undefined), [contentProps.slug, contentProps.name]);
// Get the effective components object - context overrides contentProps
const effectiveComponents = React.useMemo(() => {
return context?.components || contentProps.components || {};
}, [context?.components, contentProps.components]);
// Get the component for the current variant
const component = React.useMemo(() => {
return effectiveComponents[code.selectedVariant] || null;
}, [effectiveComponents, code.selectedVariant]);
// Demo-specific ref and focus management. Typed as `HTMLButtonElement` since
// the typical pattern is an invisible focus-target button rendered inside the
// demo. `resetFocus` simply calls `.focus()` on it.
const focusRef = React.useRef(null);
const resetFocus = React.useCallback(() => {
focusRef.current?.focus();
}, []);
// Get the effective code - context overrides contentProps if available
const effectiveCode = React.useMemo(() => {
return context?.code || contentProps.code || {};
}, [context?.code, contentProps.code]);
// Create StackBlitz demo callback
const openStackBlitz = React.useCallback(() => {
// Get the current variant code
const variantCode = effectiveCode[code.selectedVariant];
if (!variantCode || typeof variantCode === 'string') {
console.warn('No valid variant code available for StackBlitz demo');
return;
}
const title = contentProps.name || 'Demo';
const description = `${title} demo`;
// Determine if we should use TypeScript based on whether 'js' transform is NOT applied
// If 'js' transform is applied, it means we're showing the JS version of TS code
const useTypescript = code.selectedTransform !== 'js';
// Merge common export config with StackBlitz-specific config
const mergedConfig = {
...commonExportConfig,
...stackBlitzExportConfig,
variantName: code.selectedVariant,
title,
description,
useTypescript
};
// Use custom export function if provided, otherwise use default exportVariant
const exportFunction = mergedConfig.exportFunction || exportVariant;
const {
exported,
rootFile
} = exportFunction(variantCode, mergedConfig);
// Flatten the variant to get a flat file structure
const flattenedFiles = flattenCodeVariant(exported);
const stackBlitzDemo = createStackBlitz({
title,
description,
flattenedFiles,
rootFile
});
openWithForm(stackBlitzDemo);
}, [effectiveCode, code.selectedVariant, code.selectedTransform, contentProps.name, commonExportConfig, stackBlitzExportConfig]);
// Create CodeSandbox demo callback
const openCodeSandbox = React.useCallback(() => {
// Get the current variant code
const variantCode = effectiveCode[code.selectedVariant];
if (!variantCode || typeof variantCode === 'string') {
console.warn('No valid variant code available for CodeSandbox demo');
return;
}
const title = contentProps.name || 'Demo';
const description = `${title} demo`;
// Determine if we should use TypeScript based on whether 'js' transform is NOT applied
// If 'js' transform is applied, it means we're showing the JS version of TS code
const useTypescript = code.selectedTransform !== 'js';
// Merge common export config with CodeSandbox-specific config
const mergedConfig = {
...commonExportConfig,
...codeSandboxExportConfig,
variantName: code.selectedVariant,
title,
description,
useTypescript
};
// Use custom export function if provided, otherwise use default exportVariantAsCra
const exportFunction = mergedConfig.exportFunction || exportVariantAsCra;
const {
exported: craExport,
rootFile
} = exportFunction(variantCode, mergedConfig);
// Flatten the variant to get a flat file structure
const flattenedFiles = flattenCodeVariant(craExport);
const codeSandboxDemo = createCodeSandbox({
flattenedFiles,
rootFile
});
openWithForm(codeSandboxDemo);
}, [effectiveCode, code.selectedVariant, code.selectedTransform, contentProps.name, commonExportConfig, codeSandboxExportConfig]);
return {
...code,
// Demo-specific additions
component,
focusRef,
resetFocus,
openStackBlitz,
openCodeSandbox,
name: contentProps.name,
slug
};
}