@mui/internal-docs-infra
Version:
MUI Infra - internal documentation creation tools.
232 lines • 11 kB
text/typescript
import * as React from 'react';
import type { ContentProps, SourceEnhancers } from "../CodeHighlighter/types.mjs";
import { type UseCopierOpts } from "../useCopier/index.mjs";
export type UseCodeOpts = {
preClassName?: string;
copy?: UseCopierOpts;
githubUrlPrefix?: string;
initialVariant?: string;
initialTransform?: string;
/**
* Controls hash removal behavior when user interacts with file tabs:
* - 'remove-hash': Remove entire hash (default)
* - 'remove-filename': Remove only filename, keep variant in hash
*/
fileHashMode?: 'remove-hash' | 'remove-filename';
/**
* Controls when to save hash variant to localStorage:
* - 'on-load': Save immediately when page loads with hash
* - 'on-interaction': Save only when user clicks a tab (default)
* - 'never': Never save hash variant to localStorage
*/
saveHashVariantToLocalStorage?: 'on-load' | 'on-interaction' | 'never';
/**
* Array of enhancer functions to apply to parsed HAST sources.
* Enhancers receive the HAST root, comments extracted from source, and filename.
* Runs asynchronously when code changes.
*/
sourceEnhancers?: SourceEnhancers;
/**
* Disables editing of the code block even when a CodeControllerContext is present.
*/
disabled?: boolean;
/**
* Called when the code block is asked to expand its collapsed window — most
* importantly from the editor itself, when the caret navigates past the
* visible region (e.g. `ArrowUp` at the top of a collapsed block). Fires
* synchronously, *before* the expansion re-renders, so a host can capture the
* still-collapsed layout and engage a scroll anchor (e.g. `useCodeWindow`'s
* `anchorScroll('expand')`) — matching the timing of a click on the expand
* toggle. Without this, keyboard-driven expansion would jump the viewport
* instead of smoothly anchoring it.
*/
onExpand?: () => void;
/**
* Delay in milliseconds between a transform change and the actual swap
* of the rendered file tree to the new transform. `selectedTransform`
* still updates synchronously so UI controls reflect the change
* immediately — whether triggered by a user click in this demo or
* received as an external broadcast from a peer demo. While the swap
* is pending the rendered `<pre>` element receives a `data-transforming`
* attribute so consumer CSS can run an exit animation — most notably
* expanding `.collapse` placeholders back to their original height —
* before the new tree replaces them. When omitted or `0`, the new
* transform commits synchronously (default behavior).
*/
transformDelay?: number;
/**
* Delay in milliseconds between a variant change and the actual
* swap of the rendered file tree to the new variant. `selectedVariant`
* still updates synchronously so UI controls (tabs, dropdowns)
* reflect the change immediately — whether triggered by a user
* click in this demo or received as an external broadcast from a
* peer demo. While the swap is pending the rendered `<pre>` element
* receives a `data-transforming` attribute, and `<Pre>` appends a
* bridge `<span class="collapse">` to the shorter of the two
* variants' rendered tree so consumer CSS can animate between the
* two heights before the swap commits. When omitted or `0`, the
* new variant commits synchronously (default behavior).
*/
variantSwapDelay?: number;
/**
* Controls which transforms are treated as layout-affecting (phase 1,
* coordinated barrier) versus non-layout (phase 2, deferred). All
* options consult the precomputed `hasCollapse` /
* `hasCollapseInFocus` flags on each transform manifest entry — no
* tree walking happens at runtime.
*
* - `'all'` — Phase 1 if *any* file (main or `extraFiles`) in the
* selected variant has `hasCollapse: true`. Most conservative;
* matches the historical pre-`transformLayoutShift` behavior.
* - `'selected'` (default) — Phase 1 only when the currently
* rendered file's transform has `hasCollapse: true`. Avoids
* coordinating swaps that wouldn't visibly shift the rendered
* pre.
* - `'focus'` — Like `'selected'`, but while the surrounding code
* block is *collapsed* (un-expanded), use `hasCollapseInFocus`
* instead of `hasCollapse`. A `.collapse` placeholder outside
* the initially-visible region (the lines covered by
* `data-frame-type` ∈ `'highlighted' | 'focus' | 'padding-top' |
* 'padding-bottom'`) won't trigger the coordinated barrier
* because the user can't see the resulting layout shift. Falls
* back to `'selected'`-style behavior when expanded.
*/
transformLayoutShift?: 'all' | 'selected' | 'focus';
/**
* When `true`, throws synchronously during render if any transform
* on any variant has `hasCollapseInFocus: true` — i.e. its
* `.collapse` placeholder lands inside the focus region that is
* visible while the surrounding code block is un-expanded. The
* thrown error names the offending variant/file/transform so the
* demo author can narrow the `@focus` (or `@padding`) markers, or
* shrink the transform's edit range, until the placeholder lands
* outside the initially-visible window. Pair with
* `transformLayoutShift: 'focus'` to guarantee no coordinated
* barrier swaps fire while the block is collapsed.
*/
strictCollapseInFocus?: boolean;
/**
* Controls which variant swaps are treated as layout-affecting
* (phase 1, coordinated barrier) versus non-layout (phase 2,
* deferred). The check consults `totalLines` / `focusedLines`
* metadata precomputed by the pipeline — no tree walking happens
* at runtime.
*
* - `'all'` — Phase 1 when the sum of `totalLines` across every
* file (main + `extraFiles`) differs between the from-variant
* and the to-variant. Useful when the rendering surface shows
* all files simultaneously.
* - `'selected'` (default) — Phase 1 when the currently selected
* file's `totalLines` differs between the two variants (or
* the file is missing from one side). Avoids coordinating
* swaps that wouldn't visibly shift the rendered pre.
* - `'focus'` — Like `'selected'`, but while the surrounding
* code block is *collapsed* (un-expanded), compare
* `focusedLines` (the size of the visible window when
* collapsed) instead of `totalLines`. Recommended for demos
* that use `@focus` / `@padding` markers to collapse to a
* specific region.
*/
variantLayoutShift?: 'all' | 'selected' | 'focus';
/**
* When `true`, throws synchronously during render if any two
* variants declare a file with the same name but a different
* `focusedLines` count. Pair with `variantLayoutShift: 'focus'`
* to guarantee no coordinated barrier swaps fire while the block
* is collapsed: when every shared file's focused window matches
* across variants, switching variants can never shift the
* collapsed pre's height. The thrown error names the offending
* variants / file so the demo author can align the
* `@focus` / `@padding` markers.
*/
strictMatchingVariantFocusedLines?: boolean;
};
type UserProps<T extends {} = {}> = T & {
name?: string;
slug?: string;
};
export interface UseCodeResult<T extends {} = {}> {
variants: string[];
selectedVariant: string;
selectVariant: (variant: string | null) => void;
files: Array<{
name: string;
slug?: string;
component: React.ReactNode;
}>;
selectedFile: React.ReactNode;
selectedFileLines: number;
selectedFileName: string | undefined;
/**
* URL of the currently selected file, derived from the selected variant's
* `url`, the file's name, and its `relativeUrl` (when set). `undefined` when
* the variant has no `url` or the URL cannot be resolved.
*/
selectedFileUrl: string | undefined;
/**
* Slug for the currently selected file. Always derived from the canonical
* (original) file name — transforms are a view preference and do not
* produce separate slugs. Useful for building permalinks (e.g. `#${slug}`)
* that survive transform changes.
*/
selectedFileSlug: string | undefined;
selectFileName: (fileName: string) => void;
allFilesSlugs: Array<{
fileName: string;
slug: string;
variantName: string;
}>;
expanded: boolean;
expand: () => void;
setExpanded: (expanded: boolean) => void;
copy: (event: React.MouseEvent<Element>) => Promise<void>;
/**
* Copies all files in the current variant to the clipboard as a Markdown
* snippet (heading + per-file fenced code blocks).
*/
copyMarkdown: (event: React.MouseEvent<Element>) => Promise<void>;
availableTransforms: string[];
selectedTransform: string | null | undefined;
selectTransform: (transformName: string | null) => void;
/**
* Target of an in-flight transform swap that is still waiting on
* slow peers to catch up. `undefined` when no swap is pending or
* shortly after one commits. Otherwise mirrors the shape of
* `selectedTransform`: `null` for a pending swap back to the
* un-transformed original, or the transform name for a pending
* swap to that transform. Consumers can check
* `pendingTransform !== undefined` to render a generic loading
* indicator, or read the value to render something like
* `` `Switching to ${pendingTransform ?? 'original'}…` ``. Only
* populated on the demo that originated the change — peer demos
* receiving the broadcast keep this `undefined` so the indicator
* stays anchored to the demo the user interacted with.
*/
pendingTransform: string | null | undefined;
/**
* Replace the source of the currently selected file (or `fileName` when
* provided) in the controlled code. Internal hooks may pass additional
* arguments (caret position, pre-parsed HAST) that are not part of the
* public contract.
*/
setSource?: (source: string, fileName?: string) => void;
/**
* Clears the entire controlled code state back to `undefined`, discarding
* user edits across **all variants and files** owned by the surrounding
* `CodeControllerContext` (not just the currently selected file or
* variant). Only available when a `CodeControllerContext` with `setCode`
* is in scope and editing is not disabled.
*/
reset?: () => void;
/**
* Re-fetches the block's data on the client by re-running the full variant
* loader, then swaps in the fresh result while keeping the current highlighted
* output visible until the new tree lands (stale-while-revalidate). Invalidates
* the pre-parsed HAST cache. `undefined` (or a no-op) for a block with no `url`
* to re-fetch from, or with no `CodeProvider` in scope.
*/
refresh?: () => void;
userProps: UserProps<T>;
}
export declare function useCode<T extends {} = {}>(contentProps: ContentProps<T>, opts?: UseCodeOpts): UseCodeResult<T>;
export {};