UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

191 lines (171 loc) 7.17 kB
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 }; }