Skip to content

Commit

Permalink
fix(open in codesandbox button): optimize for long sandoxes (#120)
Browse files Browse the repository at this point in the history
* fix(open in csb): support long strings

* Update website/docs/docs/advanced-usage/components.md

Co-authored-by: Sanne <[email protected]>

* Update website/docs/docs/advanced-usage/components.md

Co-authored-by: Sanne <[email protected]>

Co-authored-by: Sanne <[email protected]>
  • Loading branch information
danilowoz and sannek authored Dec 8, 2021
1 parent ff40f97 commit 3ad5261
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
SandpackProvider,
SandpackThemeProvider,
SandpackCodeEditor,
} from "../../";

import { OpenInCodeSandboxButton, UnstyledOpenInCodeSandboxButton } from ".";

export default {
title: "components/OpenInCodeSandboxButton",
component: OpenInCodeSandboxButton,
};

export const Main = (): JSX.Element => (
<SandpackProvider>
<SandpackThemeProvider>
<SandpackCodeEditor />
<OpenInCodeSandboxButton />
</SandpackThemeProvider>
</SandpackProvider>
);

export const Unstyled = (): JSX.Element => (
<SandpackProvider>
<SandpackThemeProvider>
<SandpackCodeEditor />
<UnstyledOpenInCodeSandboxButton>
Open in CodeSandbox
</UnstyledOpenInCodeSandboxButton>
</SandpackThemeProvider>
</SandpackProvider>
);
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useClasser } from "@code-hike/classer";
import * as React from "react";

import { useCodeSandboxLink } from "../hooks/useCodeSandboxLink";
import { useSandpackTheme } from "../hooks/useSandpackTheme";
import { CSBIcon } from "../icons";
import { isDarkColor } from "../utils/stringUtils";
import { useSandpackTheme } from "../../hooks/useSandpackTheme";
import { CSBIcon } from "../../icons";
import { isDarkColor } from "../../utils/stringUtils";

import { UnstyledOpenInCodeSandboxButton } from "./UnstyledOpenInCodeSandboxButton";

/**
* @category Components
*/
export const OpenInCodeSandboxButton: React.FC = () => {
const url = useCodeSandboxLink();
const { theme } = useSandpackTheme();
const c = useClasser("sp");

Expand All @@ -19,14 +19,10 @@ export const OpenInCodeSandboxButton: React.FC = () => {
: "csb-icon-light";

return (
<a
<UnstyledOpenInCodeSandboxButton
className={c("button", "icon-standalone", csbIconClass)}
href={url}
rel="noreferrer noopener"
target="_blank"
title="Open in CodeSandbox"
>
<CSBIcon />
</a>
</UnstyledOpenInCodeSandboxButton>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { SandpackBundlerFiles } from "@codesandbox/sandpack-client";
import { getParameters } from "codesandbox-import-utils/lib/api/define";
import * as React from "react";

import { useSandpack } from "../../hooks/useSandpack";
import type { SandboxEnvironment } from "../../types";

const CSB_URL = "https://codesandbox.io/api/v1/sandboxes/define";

const getFileParameters = (
files: SandpackBundlerFiles,
environment?: SandboxEnvironment
) => {
type NormalizedFiles = Record<
string,
{
content: string;
isBinary: boolean;
}
>;

const normalizedFiles = Object.keys(files).reduce((prev, next) => {
const fileName = next.replace("/", "");
const value = {
content: files[next].code,
isBinary: false,
};

return { ...prev, [fileName]: value };
}, {} as NormalizedFiles);

return getParameters({
files: normalizedFiles,
...(environment ? { template: environment } : null),
});
};

export const UnstyledOpenInCodeSandboxButton: React.FC<{
className?: string;
}> = ({ children, ...props }) => {
const [paramsValues, setParamsValues] = React.useState("");
const { sandpack } = useSandpack();
const formRef = React.useRef<HTMLFormElement>(null);

React.useEffect(
function debounce() {
const timer = setTimeout(() => {
const params = getFileParameters(sandpack.files, sandpack.environment);

setParamsValues(params);
}, 600);

return () => {
clearTimeout(timer);
};
},
[sandpack.environment, sandpack.files]
);

// Register the usage of the codesandbox link
React.useEffect(function registerUsage() {
sandpack.openInCSBRegisteredRef.current = true;
}, []);

/**
* This is a safe limit to avoid too long requests (401),
* as all parameters are attached in the URL
*/
if (paramsValues.length > 1500) {
return (
<button
onClick={() => formRef.current?.submit()}
title="Open in CodeSandbox"
{...props}
>
<form ref={formRef} action={CSB_URL} method="POST" target="_blank">
<input name="parameters" type="hidden" value={paramsValues} />
</form>
{children}
</button>
);
}

return (
<a
href={`${CSB_URL}?parameters=${paramsValues}&query=file=${sandpack.activePath}%26from-sandpack=true`}
rel="noreferrer noopener"
target="_blank"
title="Open in CodeSandbox"
{...props}
>
{children}
</a>
);
};
2 changes: 2 additions & 0 deletions sandpack-react/src/common/OpenInCodeSandboxButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { OpenInCodeSandboxButton } from "./OpenInCodeSandboxButton";
export { UnstyledOpenInCodeSandboxButton } from "./UnstyledOpenInCodeSandboxButton";
1 change: 0 additions & 1 deletion sandpack-react/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from "./useActiveCode";
export * from "./useCodeSandboxLink";
export * from "./useErrorMessage";
export * from "./useLoadingOverlayState";
export * from "./useSandpack";
Expand Down
44 changes: 0 additions & 44 deletions sandpack-react/src/hooks/useCodeSandboxLink.ts

This file was deleted.

20 changes: 11 additions & 9 deletions sandpack-react/src/presets/CustomSandpack.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useState } from "react";

import { useSandpack } from "../hooks/useSandpack";
import type { ViewportSize } from "../";
import {
Sandpack,
Expand All @@ -13,12 +12,13 @@ import {
SandpackCodeViewer,
SandpackCodeEditor,
SandpackTranspiledCode,
useCodeSandboxLink,
useSandpackTheme,
useActiveCode,
useSandpackNavigation,
SandpackStack,
} from "../";
import { UnstyledOpenInCodeSandboxButton } from "../common";
import { useSandpack } from "../hooks/useSandpack";

export default {
title: "presets/Sandpack: custom",
Expand Down Expand Up @@ -71,12 +71,6 @@ export const UsingVisualElements: React.FC = () => (
</SandpackProvider>
);

const CustomOpenInCSB = () => {
const url = useCodeSandboxLink();

return <a href={url}>Open in CodeSandbox</a>;
};

const CustomRefreshButton = () => {
const { refresh } = useSandpackNavigation();

Expand All @@ -87,6 +81,14 @@ const CustomRefreshButton = () => {
);
};

const CustomOpenInCSB = () => {
return (
<UnstyledOpenInCodeSandboxButton>
Open in CodeSandbox
</UnstyledOpenInCodeSandboxButton>
);
};

const CustomCodeEditor = () => {
const { code, updateCode } = useActiveCode();
const { theme } = useSandpackTheme();
Expand All @@ -103,7 +105,7 @@ const CustomCodeEditor = () => {
background: theme.palette.defaultBackground,
border: `1px solid ${theme.palette.inactiveText}`,
color: theme.palette.activeText,
lineHeight: theme.typography.lineheight,
lineHeight: theme.typography.lineHeight,
}}
>
{code}
Expand Down
27 changes: 27 additions & 0 deletions website/docs/docs/advanced-usage/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,33 @@ For situations when you strictly want to show some code and run it in the browse
</SandpackLayout>
</SandpackProvider>

## UnstyledOpenInCodeSandboxButton & OpenInCodeSandboxButton

You can build a custom button that creates a new sandbox from the sandpack files. It will include any edits made in the Sandpack editor, so it is a great way to persist your changes. The created sandbox will open on [CodeSandbox](https://codesandbox.io) in a new tab.

Let's use the `UnstyledOpenInCodeSandboxButton` as an example:

```jsx
import {
SandpackProvider,
SandpackLayout,
SandpackCodeEditor,
UnstyledOpenInCodeSandboxButton,
} from "@codesandbox/sandpack-react";
const CustomSandpack = () => (
<SandpackProvider template="react">
<SandpackLayout>
<SandpackCodeEditor />
<UnstyledOpenInCodeSandboxButton>
Open in CodeSandbox
</UnstyledOpenInCodeSandboxButton>
</SandpackLayout>
</SandpackProvider>
);
```

The `UnstyledOpenInCodeSandboxButton` is a basic component that does not carry any styles. If you want a ready-to-use component, use the `OpenInCodeSandboxButton` instead, which has the same functionality but includes the CodeSandbox logo.

## Other components

You can also bring other components in the mix: `SandpackTranspiledCode`, `FileTabs`, `FileExplorer`, `Navigator` and so on.
Expand Down
18 changes: 0 additions & 18 deletions website/docs/docs/advanced-usage/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,6 @@ const CustomRefreshButton = () => {
};
```

## useCodeSandboxLink

Similarly, we can build a custom link that opens the sandpack files in a new tab
on https://codesandbox.io. Let's the use `useCodeSandboxLink` for that:

```jsx
import { useCodeSandboxLink } from "@codesandbox/sandpack-react";

const CustomOpenInCSB = () => {
const url = useCodeSandboxLink();
return (
<a href={url} target="_blank" rel="noopener noreferrer">
Open in CodeSandbox
</a>
);
};
```

## useActiveCode

We implemented the `SandpackCodeEditor` on top of
Expand Down

0 comments on commit 3ad5261

Please sign in to comment.