diff --git a/src/components/Tutorial.js b/src/components/Tutorial.js index 61c2d8a09..cc739b360 100644 --- a/src/components/Tutorial.js +++ b/src/components/Tutorial.js @@ -7,59 +7,85 @@ import { diffLines } from 'diff'; const Tutorial = ({ children }) => { children = Children.toArray(children); - const initialState = + const initialProjectState = children[0].props.mdxType === 'Project' - ? parseFileInfoFromConfig(children[0]) - : parseFileInfoFromChildren(children); + ? parseProjectStateFromConfig(children[0]) + : parseProjectStateFromChildren(children); - return children - .filter((child) => child.props.mdxType === 'TutorialStep') - .reduce((steps, stepElement, idx, arr) => { + const { elements } = children.reduce( + (memo, child) => { + const { elements, currentProjectState } = memo; + + if (child.props.mdxType === 'Project') { + return memo; + } else if (child.props.mdxType === 'TutorialSection') { + const { steps, currentProjectState: projectState } = gatherSteps( + child, + currentProjectState + ); + + return { + currentProjectState: projectState, + elements: [...elements, cloneElement(child, { children: steps })], + }; + } + + return { elements: [...elements, child], currentProjectState }; + }, + { elements: [], currentProjectState: initialProjectState } + ); + + return elements; +}; + +Tutorial.propTypes = { + children: PropTypes.node, +}; + +const gatherSteps = (parentElement, initialProjectState) => { + return Children.toArray(parentElement.props.children).reduce( + ({ steps, currentProjectState }, stepElement, idx, children) => { + const sharedProps = { stepNumber: idx + 1, totalSteps: children.length }; const codeBlock = Children.toArray(stepElement.props.children).find( (child) => isCodeBlock(child) && !isShellCommand(child) ); if (!codeBlock) { - return [ - ...steps, - cloneElement(stepElement, { index: idx, totalSteps: arr.length }), - ]; + return { + currentProjectState, + steps: [...steps, cloneElement(stepElement, sharedProps)], + }; } - const previousStep = new Map( - steps - .slice(0, idx) - .map((element) => element.props.step) - .reverse() - .find(Boolean) || initialState - ); - const props = parseCodeBlockProps(codeBlock); - const { code: prevCode } = previousStep.get(props.fileName); - - return [ - ...steps, - cloneElement(stepElement, { - codeBlock: { ...props, diff: diffLines(prevCode, props.code) }, - step: previousStep.set(props.fileName, { - code: props.code, - language: props.language, + const { code: prevCode } = currentProjectState.get(props.fileName); + const projectState = clone(currentProjectState).set(props.fileName, { + code: props.code, + language: props.language, + }); + + return { + currentProjectState: projectState, + steps: [ + ...steps, + cloneElement(stepElement, { + ...sharedProps, + codeBlock: { ...props, diff: diffLines(prevCode, props.code) }, + project: projectState, + children: Children.toArray(stepElement.props.children).filter( + (child) => isShellCommand(child) || !isCodeBlock(child) + ), }), - index: idx, - totalSteps: arr.length, - children: Children.toArray(stepElement.props.children).filter( - (child) => isShellCommand(child) || !isCodeBlock(child) - ), - }), - ]; - }, []); + ], + }; + }, + { steps: [], currentProjectState: initialProjectState } + ); }; -Tutorial.propTypes = { - children: PropTypes.node, -}; +const clone = (map) => new Map(map); -const parseFileInfoFromConfig = (configElement) => { +const parseProjectStateFromConfig = (configElement) => { return Children.toArray(configElement.props.children) .filter((child) => isCodeBlock(child) && !isShellCommand(child)) .reduce((map, child) => { @@ -69,7 +95,7 @@ const parseFileInfoFromConfig = (configElement) => { }, new Map()); }; -const parseFileInfoFromChildren = (children) => { +const parseProjectStateFromChildren = (children) => { return children .flatMap((child) => { switch (child.props.mdxType) { diff --git a/src/components/TutorialEditor.js b/src/components/TutorialEditor.js index 4fe809ecb..46121311a 100644 --- a/src/components/TutorialEditor.js +++ b/src/components/TutorialEditor.js @@ -43,7 +43,7 @@ const diffHighlightedLines = (diff) => { return highlightedLines.join(','); }; -const TutorialEditor = ({ codeBlock, files }) => { +const TutorialEditor = ({ codeBlock, project }) => { const [selectedFile, setSelectedFile] = useState(codeBlock.fileName); return ( @@ -56,7 +56,7 @@ const TutorialEditor = ({ codeBlock, files }) => { border-top-right-radius: 0.25rem; `} > - {Array.from(files.keys()).map((fileName) => ( + {Array.from(project.keys()).map((fileName) => (