Browse Source

Sandpack: upgrade dependencies and adds ReactDevtools (#4161)

* upgrade sandpack dependencies

* update csb button

* remove unnecessary ref

* add react dev tools

* remove duplicated/unnecessary element

* fix: codemirror gutter

* fix codemirror gutter

* make devtools optional

* bump sandpack version

* fix: add website theme to the react devtools

* fix: react devtools ui tweaks

* devtools: custom theme

* refactor: reset sandbox

* fix sandpack

* fix: devtools style

* fix: tweaks on mobile version

* fix: soft refresh when module is loaded

* bump sandpack version

* update color

* remove demo

* remove inspect button

* bring back key logic

* refactor: icon align

* update sandpack

* logic to keep the content synced
main
Danilo Woznica 3 years ago
committed by GitHub
parent
commit
d80ef566a3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      beta/package.json
  2. 6
      beta/src/components/Icon/IconNewPage.tsx
  3. 7
      beta/src/components/MDX/APIAnatomy.tsx
  4. 12
      beta/src/components/MDX/CodeBlock/CodeBlock.tsx
  5. 23
      beta/src/components/MDX/Sandpack/CustomPreset.tsx
  6. 27
      beta/src/components/MDX/Sandpack/NavigationBar.tsx
  7. 31
      beta/src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx
  8. 25
      beta/src/components/MDX/Sandpack/index.tsx
  9. 59
      beta/src/styles/sandpack.css
  10. 1337
      beta/yarn.lock

6
beta/package.json

@ -22,7 +22,7 @@
"check-all": "npm-run-all prettier lint:fix tsc" "check-all": "npm-run-all prettier lint:fix tsc"
}, },
"dependencies": { "dependencies": {
"@codesandbox/sandpack-react": "^0.1.20", "@codesandbox/sandpack-react": "0.13.6-experimental.0",
"@docsearch/css": "3.0.0-alpha.41", "@docsearch/css": "3.0.0-alpha.41",
"@docsearch/react": "3.0.0-alpha.41", "@docsearch/react": "3.0.0-alpha.41",
"@headlessui/react": "^1.3.0", "@headlessui/react": "^1.3.0",
@ -34,9 +34,9 @@
"github-slugger": "^1.3.0", "github-slugger": "^1.3.0",
"next": "^12.0.5", "next": "^12.0.5",
"parse-numeric-range": "^1.2.0", "parse-numeric-range": "^1.2.0",
"react": "18.0.0-alpha-930c9e7ee-20211015", "react": "experimental",
"react-collapsed": "3.1.0", "react-collapsed": "3.1.0",
"react-dom": "18.0.0-alpha-930c9e7ee-20211015", "react-dom": "experimental",
"scroll-into-view-if-needed": "^2.2.25" "scroll-into-view-if-needed": "^2.2.25"
}, },
"devDependencies": { "devDependencies": {

6
beta/src/components/Icon/IconNewPage.tsx

@ -5,14 +5,14 @@
import * as React from 'react'; import * as React from 'react';
export const IconNewPage = React.memo<JSX.IntrinsicElements['svg']>( export const IconNewPage = React.memo<JSX.IntrinsicElements['svg']>(
function IconNewPage({className}) { function IconNewPage(props) {
return ( return (
<svg <svg
className={className}
width="0.72em" width="0.72em"
height="0.72em" height="0.72em"
viewBox="0 0 13 13" viewBox="0 0 13 13"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg"
{...props}>
<path <path
d="M4.72038 2.94824V4.28158H1.38704V11.6149H8.72038V8.28158H10.0537V12.2816C10.0537 12.4584 9.98347 12.628 9.85845 12.753C9.73343 12.878 9.56386 12.9482 9.38704 12.9482H0.720378C0.543567 12.9482 0.373997 12.878 0.248973 12.753C0.123949 12.628 0.0537109 12.4584 0.0537109 12.2816V3.61491C0.0537109 3.4381 0.123949 3.26853 0.248973 3.1435C0.373997 3.01848 0.543567 2.94824 0.720378 2.94824H4.72038ZM12.0537 0.948242V6.28158H10.7204V3.22358L5.52504 8.41958L4.58238 7.47691L9.77704 2.28158H6.72038V0.948242H12.0537Z" d="M4.72038 2.94824V4.28158H1.38704V11.6149H8.72038V8.28158H10.0537V12.2816C10.0537 12.4584 9.98347 12.628 9.85845 12.753C9.73343 12.878 9.56386 12.9482 9.38704 12.9482H0.720378C0.543567 12.9482 0.373997 12.878 0.248973 12.753C0.123949 12.628 0.0537109 12.4584 0.0537109 12.2816V3.61491C0.0537109 3.4381 0.123949 3.26853 0.248973 3.1435C0.373997 3.01848 0.543567 2.94824 0.720378 2.94824H4.72038ZM12.0537 0.948242V6.28158H10.7204V3.22358L5.52504 8.41958L4.58238 7.47691L9.77704 2.28158H6.72038V0.948242H12.0537Z"
fill="currentColor" fill="currentColor"

7
beta/src/components/MDX/APIAnatomy.tsx

@ -42,7 +42,6 @@ const colors = [
export function APIAnatomy({children}: APIAnatomyProps) { export function APIAnatomy({children}: APIAnatomyProps) {
const [activeStep, setActiveStep] = React.useState<number | null>(null); const [activeStep, setActiveStep] = React.useState<number | null>(null);
const ref = React.useRef<HTMLDivElement>();
const {steps, code} = React.Children.toArray(children).reduce( const {steps, code} = React.Children.toArray(children).reduce(
(acc: AnatomyContent, child) => { (acc: AnatomyContent, child) => {
@ -60,11 +59,7 @@ export function APIAnatomy({children}: APIAnatomyProps) {
break; break;
case 'pre': case 'pre':
acc.code = ( acc.code = (
<CodeBlock <CodeBlock {...child.props.children.props} noMargin={true} />
ref={ref}
{...child.props.children.props}
noMargin={true}
/>
); );
break; break;
} }

12
beta/src/components/MDX/CodeBlock/CodeBlock.tsx

@ -2,7 +2,6 @@
* Copyright (c) Facebook, Inc. and its affiliates. * Copyright (c) Facebook, Inc. and its affiliates.
*/ */
import * as React from 'react';
import cn from 'classnames'; import cn from 'classnames';
import { import {
ClasserProvider, ClasserProvider,
@ -21,8 +20,7 @@ interface InlineHiglight {
endColumn: number; endColumn: number;
} }
const CodeBlock = React.forwardRef(function CodeBlock( const CodeBlock = function CodeBlock({
{
children, children,
className = 'language-js', className = 'language-js',
metastring, metastring,
@ -34,9 +32,7 @@ const CodeBlock = React.forwardRef(function CodeBlock(
metastring: string; metastring: string;
noMargin?: boolean; noMargin?: boolean;
noMarkers?: boolean; noMarkers?: boolean;
}, }) {
ref?: React.Ref<HTMLDivElement>
) {
const getDecoratedLineInfo = () => { const getDecoratedLineInfo = () => {
if (!metastring) { if (!metastring) {
return []; return [];
@ -95,7 +91,7 @@ const CodeBlock = React.forwardRef(function CodeBlock(
'sp-cm': styles.codeViewer, 'sp-cm': styles.codeViewer,
}}> }}>
<SandpackCodeViewer <SandpackCodeViewer
ref={ref} key={children.trimEnd()}
showLineNumbers={false} showLineNumbers={false}
decorators={decorators} decorators={decorators}
/> />
@ -104,7 +100,7 @@ const CodeBlock = React.forwardRef(function CodeBlock(
</SandpackProvider> </SandpackProvider>
</div> </div>
); );
}); };
export default CodeBlock; export default CodeBlock;

23
beta/src/components/MDX/Sandpack/CustomPreset.tsx

@ -10,9 +10,12 @@ import {
useActiveCode, useActiveCode,
SandpackCodeEditor, SandpackCodeEditor,
SandpackThemeProvider, SandpackThemeProvider,
SandpackReactDevTools,
} from '@codesandbox/sandpack-react'; } from '@codesandbox/sandpack-react';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import cn from 'classnames';
import {IconChevron} from 'components/Icon/IconChevron'; import {IconChevron} from 'components/Icon/IconChevron';
import {NavigationBar} from './NavigationBar'; import {NavigationBar} from './NavigationBar';
import {Preview} from './Preview'; import {Preview} from './Preview';
@ -20,10 +23,14 @@ import {CustomTheme} from './Themes';
export function CustomPreset({ export function CustomPreset({
isSingleFile, isSingleFile,
onReset, showDevTools,
onDevToolsLoad,
devToolsLoaded,
}: { }: {
isSingleFile: boolean; isSingleFile: boolean;
onReset: () => void; showDevTools: boolean;
devToolsLoaded: boolean;
onDevToolsLoad: () => void;
}) { }) {
const lineCountRef = React.useRef<{[key: string]: number}>({}); const lineCountRef = React.useRef<{[key: string]: number}>({});
const containerRef = React.useRef<HTMLDivElement>(null); const containerRef = React.useRef<HTMLDivElement>(null);
@ -50,11 +57,14 @@ export function CustomPreset({
<div <div
className="shadow-lg dark:shadow-lg-dark rounded-lg" className="shadow-lg dark:shadow-lg-dark rounded-lg"
ref={containerRef}> ref={containerRef}>
<NavigationBar showDownload={isSingleFile} onReset={onReset} /> <NavigationBar showDownload={isSingleFile} />
<SandpackThemeProvider theme={CustomTheme}> <SandpackThemeProvider theme={CustomTheme}>
<div <div
ref={sandpack.lazyAnchorRef} ref={sandpack.lazyAnchorRef}
className="sp-layout rounded-t-none" className={cn(
'sp-layout sp-custom-layout',
showDevTools && devToolsLoaded && 'sp-layout-devtools'
)}
style={{ style={{
// Prevent it from collapsing below the initial (non-loaded) height. // Prevent it from collapsing below the initial (non-loaded) height.
// There has to be some better way to do this... // There has to be some better way to do this...
@ -77,6 +87,7 @@ export function CustomPreset({
maxHeight: isExpanded ? '' : 406, maxHeight: isExpanded ? '' : 406,
}} }}
/> />
{isExpandable && ( {isExpandable && (
<button <button
translate="yes" translate="yes"
@ -104,6 +115,10 @@ export function CustomPreset({
</button> </button>
)} )}
</div> </div>
{showDevTools && (
<SandpackReactDevTools onLoadModule={onDevToolsLoad} />
)}
</SandpackThemeProvider> </SandpackThemeProvider>
</div> </div>
</> </>

27
beta/src/components/MDX/Sandpack/NavigationBar.tsx

@ -3,22 +3,22 @@
*/ */
import * as React from 'react'; import * as React from 'react';
import {FileTabs, useSandpack} from '@codesandbox/sandpack-react'; import {
FileTabs,
useSandpack,
useSandpackNavigation,
} from '@codesandbox/sandpack-react';
import {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton'; import {OpenInCodeSandboxButton} from './OpenInCodeSandboxButton';
import {ResetButton} from './ResetButton'; import {ResetButton} from './ResetButton';
import {DownloadButton} from './DownloadButton'; import {DownloadButton} from './DownloadButton';
import {FilesDropdown} from './FilesDropdown'; import {FilesDropdown} from './FilesDropdown';
export function NavigationBar({ export function NavigationBar({showDownload}: {showDownload: boolean}) {
showDownload,
onReset,
}: {
showDownload: boolean;
onReset: () => void;
}) {
const {sandpack} = useSandpack(); const {sandpack} = useSandpack();
const [dropdownActive, setDropdownActive] = React.useState(false); const [dropdownActive, setDropdownActive] = React.useState(false);
const {openPaths} = sandpack; const {openPaths, clients} = sandpack;
const clientId = Object.keys(clients)[0];
const {refresh} = useSandpackNavigation(clientId);
const resizeHandler = React.useCallback(() => { const resizeHandler = React.useCallback(() => {
const width = window.innerWidth || document.documentElement.clientWidth; const width = window.innerWidth || document.documentElement.clientWidth;
@ -41,6 +41,11 @@ export function NavigationBar({
return; return;
}, [openPaths.length, resizeHandler]); }, [openPaths.length, resizeHandler]);
const handleReset = () => {
sandpack.resetAllFiles();
refresh();
};
return ( return (
<div className="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg rounded-b-none"> <div className="bg-wash dark:bg-card-dark flex justify-between items-center relative z-10 border-b border-border dark:border-border-dark rounded-t-lg rounded-b-none">
<div className="px-4 lg:px-6"> <div className="px-4 lg:px-6">
@ -50,8 +55,8 @@ export function NavigationBar({
className="px-3 flex items-center justify-end flex-grow text-right" className="px-3 flex items-center justify-end flex-grow text-right"
translate="yes"> translate="yes">
{showDownload && <DownloadButton />} {showDownload && <DownloadButton />}
<ResetButton onReset={onReset} /> <ResetButton onReset={handleReset} />
<OpenInCodeSandboxButton className="ml-2 md:ml-4" /> <OpenInCodeSandboxButton />
</div> </div>
</div> </div>
); );

31
beta/src/components/MDX/Sandpack/OpenInCodeSandboxButton.tsx

@ -3,29 +3,20 @@
*/ */
import * as React from 'react'; import * as React from 'react';
import {useCodeSandboxLink} from '@codesandbox/sandpack-react'; import {UnstyledOpenInCodeSandboxButton} from '@codesandbox/sandpack-react';
import cn from 'classnames';
import {IconNewPage} from '../../Icon/IconNewPage'; import {IconNewPage} from '../../Icon/IconNewPage';
export const OpenInCodeSandboxButton = ({className}: {className?: string}) => { export const OpenInCodeSandboxButton = () => {
const url = useCodeSandboxLink();
return ( return (
<a <UnstyledOpenInCodeSandboxButton
className={cn( className="text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1 ml-3 md:ml-1"
'text-sm text-primary dark:text-primary-dark inline-flex items-center hover:text-link duration-100 ease-in transition mx-1',
className
)}
href={url}
rel="noreferrer noopener"
target="_blank"
title="Open in CodeSandbox"> title="Open in CodeSandbox">
<span className="hidden md:inline"> <IconNewPage
<IconNewPage className="inline mb-0.5 text-base" /> Fork className="inline mb-0.5 ml-1 mr-1 relative top-px"
</span> width=".8em"
<span className="inline md:hidden"> height=".8em"
<IconNewPage className="inline mb-0.5 text-base" /> Fork />
</span> <span className="hidden md:block">Fork</span>
</a> </UnstyledOpenInCodeSandboxButton>
); );
}; };

25
beta/src/components/MDX/Sandpack/index.tsx

@ -15,6 +15,7 @@ type SandpackProps = {
children: React.ReactChildren; children: React.ReactChildren;
autorun?: boolean; autorun?: boolean;
setup?: SandpackSetup; setup?: SandpackSetup;
showDevTools?: boolean;
}; };
const sandboxStyle = ` const sandboxStyle = `
@ -64,8 +65,8 @@ ul {
`.trim(); `.trim();
function Sandpack(props: SandpackProps) { function Sandpack(props: SandpackProps) {
let {children, setup, autorun = true} = props; let {children, setup, autorun = true, showDevTools = false} = props;
let [resetKey, setResetKey] = React.useState(0); const [devToolsLoaded, setDevToolsLoaded] = React.useState(false);
let codeSnippets = React.Children.toArray(children) as React.ReactElement[]; let codeSnippets = React.Children.toArray(children) as React.ReactElement[];
let isSingleFile = true; let isSingleFile = true;
@ -121,29 +122,17 @@ function Sandpack(props: SandpackProps) {
hidden: true, hidden: true,
}; };
let key = String(resetKey);
if (process.env.NODE_ENV !== 'production') {
// Remount on any source change in development.
key +=
'-' +
JSON.stringify({
...props,
children: files,
});
}
return ( return (
<div className="my-8" translate="no"> <div className="sandpack-container my-8" translate="no">
<SandpackProvider <SandpackProvider
key={key}
template="react" template="react"
customSetup={{...setup, files: files}} customSetup={{...setup, files: files}}
autorun={autorun}> autorun={autorun}>
<CustomPreset <CustomPreset
isSingleFile={isSingleFile} isSingleFile={isSingleFile}
onReset={() => { showDevTools={showDevTools}
setResetKey((k) => k + 1); onDevToolsLoad={() => setDevToolsLoaded(true)}
}} devToolsLoaded={devToolsLoaded}
/> />
</SandpackProvider> </SandpackProvider>
</div> </div>

59
beta/src/styles/sandpack.css

@ -112,10 +112,6 @@ html.dark .sp-tabs .sp-tab-button[data-active='true'] {
* *
* If you know a better way to keep them from diverging, send a PR. * If you know a better way to keep them from diverging, send a PR.
*/ */
.sp-layout {
border-bottom-left-radius: 0.5rem !important;
border-bottom-right-radius: 0.5rem !important;
}
.sp-stack { .sp-stack {
height: initial !important; height: initial !important;
width: fit-content !important; width: fit-content !important;
@ -150,11 +146,17 @@ html.dark .sp-tabs .sp-tab-button[data-active='true'] {
background-color: var(--sp-colors-bg-default); background-color: var(--sp-colors-bg-default);
z-index: 1; z-index: 1;
} }
.sp-layout { .sp-wrapper .sp-custom-layout {
overflow: initial !important; overflow: initial;
border: 0px solid transparent !important; border: 0px solid transparent;
border-top-left-radius: 0px !important; border-top-left-radius: 0px;
border-top-right-radius: 0px !important; border-top-right-radius: 0px;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}
.sp-wrapper .sp-layout-devtools {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
} }
html.dark .sp-layout > :not(:first-child) { html.dark .sp-layout > :not(:first-child) {
border-color: #343a46; border-color: #343a46;
@ -172,6 +174,45 @@ html.dark .sp-loading {
} }
} }
/* Devtools */
.sp-devtools {
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
overflow: hidden;
}
.sp-devtools > div {
--color-background: var(--sp-colors-bg-default) !important;
--color-background-inactive: var(--sp-colors-fg-inactive) !important;
--color-background-selected: #087ea4 !important;
--color-background-hover: transparent !important;
--color-modal-background: #ffffffd2 !important;
--color-tab-selected-border: #087ea4 !important;
--color-component-name: var(--theme-definition) !important;
--color-attribute-name: var(--theme-property) !important;
--color-attribute-value: var(--theme-string) !important;
--color-attribute-editable-value: var(--theme-property) !important;
--color-attribute-name-not-editable: var(--sp-colors-fg-default) !important;
--color-button-background-focus: var(--sp-colors-fg-inactive) !important;
--color-button-active: #087ea4 !important;
--color-button-background: transparent !important;
--color-button: var(--sp-colors-fg-default) !important;
--color-button-hover: var(--sp-colors-fg-active) !important;
--color-border: var(--sp-colors-fg-inactive) !important;
--color-text: rgb(35, 39, 47) !important;
}
html.dark .sp-devtools > div {
--color-text: var(--sp-colors-fg-default) !important;
--color-modal-background: #16181de0 !important;
}
.sp-devtools table td {
border: 1px solid var(--sp-colors-fg-inactive);
}
/* Make focus rings work */ /* Make focus rings work */
.sp-tab-button { .sp-tab-button {
transition: none !important; transition: none !important;

1337
beta/yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save