Skip to content

Commit

Permalink
feat(files): read-only mode (#300)
Browse files Browse the repository at this point in the history
* feat(codeeditor): read-only mode

* Update custom-content.md

* fixes

* fixes

* docs

* Update sandpack-react/src/components/FileTabs/index.tsx

Co-authored-by: Roman Kuba <[email protected]>

* Update sandpack-react/src/components/FileTabs/index.tsx

Co-authored-by: Roman Kuba <[email protected]>

* Update sandpack-react/src/components/FileTabs/index.tsx

Co-authored-by: Roman Kuba <[email protected]>

* Update sandpack-react/src/presets/Sandpack.tsx

Co-authored-by: Roman Kuba <[email protected]>

* Update website/docs/docs/getting-started/custom-content.md

Co-authored-by: Roman Kuba <[email protected]>

* revert changes

* update layout

* Update CodeEditor.stories.tsx and Sandpack.tsx

* Update custom-content.md

Co-authored-by: Danilo Woznica <[email protected]>
Co-authored-by: Roman Kuba <[email protected]>
  • Loading branch information
3 people authored Jan 14, 2022
1 parent 4cf9ca7 commit 9d5d1bf
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 13 deletions.
1 change: 1 addition & 0 deletions sandpack-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ITemplate } from "codesandbox-import-util-types";

export interface SandpackBundlerFile {
code: string;
readOnly?: boolean;
}

export type SandpackBundlerFiles = Record<string, SandpackBundlerFile>;
Expand Down
57 changes: 57 additions & 0 deletions sandpack-react/src/components/CodeEditor/CodeEditor.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,60 @@ export const ExtensionAutocomplete: React.FC = () => (
</SandpackThemeProvider>
</SandpackProvider>
);

export const ReadOnly: React.FC = () => {
return (
<>
<p>Read-only by file</p>
<Sandpack
customSetup={{ entry: "/index.tsx", main: "/App.tsx" }}
files={{
"/index.tsx": { code: "", hidden: true },
"/src/App.tsx": { code: "Hello", readOnly: true, active: true },
"/src/components/button.tsx": { code: "World", readOnly: false },
}}
options={{ showTabs: true }}
template="react-ts"
/>

<p>Read-only global</p>
<Sandpack
options={{ showTabs: true, readOnly: true }}
template="react-ts"
/>

<p>Read-only global and by file</p>
<Sandpack
options={{ showTabs: false, readOnly: true }}
files={{
"/index.tsx": { code: "", hidden: true },
"/src/App.tsx": { code: "Hello", readOnly: true },
"/src/components/button.tsx": { code: "World", readOnly: false },
}}
template="react-ts"
/>

<p>Read-only global, but no label</p>
<Sandpack
options={{ showTabs: false, readOnly: true, showReadOnly: false }}
files={{
"/index.tsx": { code: "", hidden: true },
"/src/App.tsx": { code: "Hello", readOnly: true },
"/src/components/button.tsx": { code: "World", readOnly: false },
}}
template="react-ts"
/>

<p>Read-only by file, but no label</p>
<Sandpack
options={{ showTabs: true, readOnly: false, showReadOnly: false }}
files={{
"/index.tsx": { code: "", hidden: true },
"/src/App.tsx": { code: "Hello", readOnly: true, active: true },
"/src/components/button.tsx": { code: "World", readOnly: false },
}}
template="react-ts"
/>
</>
);
};
13 changes: 13 additions & 0 deletions sandpack-react/src/components/CodeEditor/CodeMirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,15 @@ interface CodeMirrorProps {
showInlineErrors?: boolean;
wrapContent?: boolean;
editorState?: SandpackEditorState;
/**
* This disables editing of content by the user in all files.
*/
readOnly?: boolean;
/**
* Controls the visibility of Read-only label, which will only
* appears when `readOnly` is `true`
*/
showReadOnly?: boolean;
decorators?: Decorators;
initMode: SandpackInitMode;
id?: string;
Expand All @@ -93,6 +101,7 @@ export const CodeMirror = React.forwardRef<CodeMirrorRef, CodeMirrorProps>(
wrapContent = false,
editorState = "pristine",
readOnly = false,
showReadOnly = true,
decorators,
initMode = "lazy",
id,
Expand Down Expand Up @@ -344,6 +353,10 @@ export const CodeMirror = React.forwardRef<CodeMirrorRef, CodeMirrorProps>(
{!shouldInitEditor && (
<code className={c("pre-placeholder")}>{code}</code>
)}

{readOnly && showReadOnly && (
<span className={c("read-only")}>Read-only</span>
)}
</pre>
);
}
Expand Down
17 changes: 15 additions & 2 deletions sandpack-react/src/components/CodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ export interface CodeEditorProps {
*/
extensionsKeymap?: Array<readonly KeyBinding[]>;
id?: string;
/**
* This disables editing of the editor content by the user.
*/
readOnly?: boolean;
/**
* Controls the visibility of Read-only label, which will only
* appears when `readOnly` is `true`
*/
showReadOnly?: boolean;
}

export { CodeMirror as CodeEditor };
Expand All @@ -63,11 +72,13 @@ export const SandpackCodeEditor = React.forwardRef<
extensions,
extensionsKeymap,
id,
readOnly,
showReadOnly,
},
ref
) => {
const { sandpack } = useSandpack();
const { code, updateCode } = useActiveCode();
const { code, updateCode, readOnly: readOnlyFile } = useActiveCode();
const { activePath, status, editorState } = sandpack;
const shouldShowTabs = showTabs ?? sandpack.openPaths.length > 1;

Expand All @@ -79,7 +90,7 @@ export const SandpackCodeEditor = React.forwardRef<

return (
<SandpackStack customStyle={customStyle}>
{shouldShowTabs ? <FileTabs closableTabs={closableTabs} /> : null}
{shouldShowTabs && <FileTabs closableTabs={closableTabs} />}

<div className={c("code-editor")}>
<CodeMirror
Expand All @@ -96,6 +107,8 @@ export const SandpackCodeEditor = React.forwardRef<
showInlineErrors={showInlineErrors}
showLineNumbers={showLineNumbers}
wrapContent={wrapContent}
readOnly={readOnly || readOnlyFile}
showReadOnly={showReadOnly}
/>

{showRunButton && status === "idle" ? <RunButton /> : null}
Expand Down
1 change: 1 addition & 0 deletions sandpack-react/src/components/CodeViewer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const SandpackCodeViewer = React.forwardRef<
showLineNumbers={showLineNumbers}
wrapContent={wrapContent}
readOnly
showReadOnly={false}
/>

{sandpack.status === "idle" ? <RunButton /> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
import { SandpackLayout } from "../../common/Layout";
import { SandpackProvider } from "../../contexts/sandpackContext";
import { SandpackCodeViewer } from "../CodeViewer";
import { Sandpack } from "../../";

import { FileTabs } from "./index";

Expand Down
9 changes: 7 additions & 2 deletions sandpack-react/src/components/FileTabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ import {
} from "../../utils/stringUtils";

export interface FileTabsProps {
/**
* This adds a close button next to each file with a unique trigger to close it.
*/
closableTabs?: boolean;
}

/**
* FileTabs is a list of all open files, the active file, and its state.
*
* @category Components
*/
export const FileTabs = ({ closableTabs }: FileTabsProps): JSX.Element => {
Expand Down Expand Up @@ -80,11 +85,11 @@ export const FileTabs = ({ closableTabs }: FileTabsProps): JSX.Element => {
type="button"
>
{getTriggerText(filePath)}
{closableTabs && openPaths.length > 1 ? (
{closableTabs && openPaths.length > 1 && (
<span className={c("close-button")} onClick={handleCloseFile}>
<CloseIcon />
</span>
) : null}
)}
</button>
))}
</div>
Expand Down
5 changes: 5 additions & 0 deletions sandpack-react/src/hooks/useActiveCode.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { useSandpack } from "./useSandpack";

/**
* This returns the current state of the active file
* and a method to update its content.
*
* @category Hooks
*/
export const useActiveCode = (): {
code: string;
readOnly: boolean;
updateCode: (newCode: string) => void;
} => {
const { sandpack } = useSandpack();

return {
code: sandpack.files[sandpack.activePath].code,
readOnly: sandpack.files[sandpack.activePath].readOnly ?? false,
updateCode: sandpack.updateCurrentFile,
};
};
4 changes: 2 additions & 2 deletions sandpack-react/src/presets/Playground.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export const Main = (): JSX.Element => {
return (
<Sandpack
customSetup={{ entry: "/index.tsx", main: "/App.tsx" }}
files={{ "/App.tsx": code }}
options={{ showTabs: true, openPaths: ["/index.tsx"] }}
files={{ "/App.tsx": { code, readOnly: true } }}
options={{ showTabs: true }}
template="react-ts"
/>
);
Expand Down
12 changes: 12 additions & 0 deletions sandpack-react/src/presets/Sandpack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ export interface SandpackProps {
recompileMode?: "immediate" | "delayed";
recompileDelay?: number;
codeEditor?: SandpackCodeOptions;

/**
* This disables editing of content by the user in all files.
*/
readOnly?: boolean;
/**
* Controls the visibility of Read-only label, which will only
* appears when `readOnly` is `true`
*/
showReadOnly?: boolean;
};
}

Expand Down Expand Up @@ -88,6 +98,8 @@ export const Sandpack: React.FC<SandpackProps> = (props) => {
initMode: props.options?.initMode,
extensions: props.options?.codeEditor?.extensions,
extensionsKeymap: props.options?.codeEditor?.extensionsKeymap,
readOnly: props.options?.readOnly,
showReadOnly: props.options?.showReadOnly,
};

const providerOptions: SandpackProviderProps = {
Expand Down
18 changes: 15 additions & 3 deletions sandpack-react/src/styles/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@
/* Common Styling */

.sp-tab-button {
display: block;
display: flex;
align-items: center;
background: transparent;
appearance: none;
font-size: inherit;
Expand Down Expand Up @@ -306,8 +307,6 @@
padding: var(--sp-space-1) var(--sp-space-3) var(--sp-space-1)
var(--sp-space-2);
border-radius: var(--sp-border-radius);
display: flex;
align-items: center;
color: var(--sp-colors-fg-default);
background-color: var(--sp-colors-bg-default);
font-size: inherit;
Expand Down Expand Up @@ -516,3 +515,16 @@
height: var(--sp-layout-height);
width: 100%;
}

.sp-read-only {
font-family: var(--sp-font-mono);
font-size: 0.8em;
position: absolute;
right: var(--sp-space-2);
bottom: var(--sp-space-2);
z-index: 2;
color: var(--sp-colors-bg-active);
background-color: var(--sp-colors-fg-active);
border-radius: 99999px;
padding: calc(var(--sp-space-1) / 2) var(--sp-space-2);
}
1 change: 1 addition & 0 deletions sandpack-react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface SandpackFile {
code: string;
hidden?: boolean;
active?: boolean;
readOnly?: boolean;
}

export type SandpackFiles = Record<string, string | SandpackFile>;
Expand Down
48 changes: 44 additions & 4 deletions website/docs/docs/getting-started/custom-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ default:
```jsx
<Sandpack
files={{
'/App.js': reactCode,
'/button.js': {
"/App.js": reactCode,
"/button.js": {
code: buttonCode,
active: true,
}
'/link.js': {
},
"/link.js": {
code: linkCode,
hidden: true,
},
Expand All @@ -152,6 +152,46 @@ default:
The `active` flag has precendence over the `hidden` flag. So a file with both `hidden` and `active` set as `true` will be visible.
:::

### Read-only mode

You can set one, multiple files, or the entire Sandpack as read-only, which will make all files non-editable.

**Per file:**

```jsx
<Sandpack
files={{
"/App.js": reactCode,
"/button.js": {
code: buttonCode,
readOnly: true,
},
}}
template="react"
/>
```

**Globally:**

```jsx
<Sandpack
options={{
readOnly: true,
}}
/>
```

Plus, you can hide the Read-only label which appears on top of the code editor:

```jsx
<Sandpack
options={{
readOnly: true,
showReadOnly: false,
}}
/>
```

### openPaths and activePath

You can override the entire hidden/active system with two settings (`openPaths` and `activePath`) inside the
Expand Down

0 comments on commit 9d5d1bf

Please sign in to comment.