diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9d50eeedb5a..399806e1633 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,5 +6,9 @@
"javascriptreact",
{ "language": "typescript", "autoFix": true },
{ "language": "typescriptreact", "autoFix": true }
- ]
+ ],
+ "files.watcherExclude": {
+ "**/node_modules": true,
+ "**/node_modules/**": false
+ }
}
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx
index 4a873cee38f..cf0f9aa6508 100644
--- a/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/index.tsx
@@ -25,6 +25,7 @@ import { More } from './items/More';
import { NotOwnedSandboxInfo } from './items/NotOwnedSandboxInfo';
import { ProjectInfo } from './items/ProjectInfo';
import { ProjectInfo as ProjectInfoNew } from './screens/ProjectInfo';
+import { Deployment as DeploymentNew } from './screens/Deployment/index';
import { Server } from './items/Server';
import { SSEDownNotice } from './SSEDownNotice';
@@ -37,7 +38,7 @@ const workspaceTabs = {
'project-summary': NotOwnedSandboxInfo,
files: FilesItem,
github: GitHub,
- deploy: Deployment,
+ deploy: REDESIGNED_SIDEBAR ? DeploymentNew : Deployment,
config: REDESIGNED_SIDEBAR ? ConfigurationFilesNew : ConfigurationFiles,
live: Live,
server: Server,
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx
new file mode 100644
index 00000000000..8c0ba4e2a4b
--- /dev/null
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Netlify.tsx
@@ -0,0 +1,113 @@
+import React, { useEffect } from 'react';
+import getTemplate from '@codesandbox/common/lib/templates';
+import track from '@codesandbox/common/lib/utils/analytics';
+import { useOvermind } from 'app/overmind';
+
+import {
+ Element,
+ ListAction,
+ Text,
+ Stack,
+ List,
+ ListItem,
+ Button,
+ Integration,
+} from '@codesandbox/components';
+import { css } from '@styled-system/css';
+import { NetlifyIcon, FileIcon, VisitIcon, FlagIcon } from './icons';
+
+export const Netlify = () => {
+ const {
+ actions: {
+ modalOpened,
+ deployment: { deployWithNetlify, getNetlifyDeploys },
+ },
+ state: {
+ deployment: {
+ deploying,
+ netlifySite,
+ building,
+ netlifyLogs,
+ netlifyClaimUrl,
+ },
+ editor: { currentSandbox },
+ },
+ } = useOvermind();
+
+ useEffect(() => {
+ getNetlifyDeploys();
+ }, [getNetlifyDeploys]);
+
+ const template = getTemplate(currentSandbox.template);
+
+ return (
+ template.netlify !== false && (
+ <>
+
+
+ Enables
+ Deployments
+
+
+
+ {netlifySite ? (
+
+
+ {netlifySite.name}
+
+ {building && !netlifyLogs && (
+
+ Building
+
+ )}
+ {netlifySite.url ? (
+ window.open(netlifySite.url, '_blank')}
+ >
+
+
+ {' '}
+ Visit Site
+
+ ) : null}
+ {netlifySite.url ? (
+ window.open(netlifyClaimUrl, '_blank')}
+ >
+
+
+ {' '}
+ Claim Site
+
+ ) : null}
+ {netlifyLogs ? (
+ modalOpened({ modal: 'netlifyLogs' })}
+ >
+
+
+ {' '}
+ View Logs
+
+ ) : null}
+
+ ) : null}
+
+
+ >
+ )
+ );
+};
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx
new file mode 100644
index 00000000000..d085867b664
--- /dev/null
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/Zeit.tsx
@@ -0,0 +1,90 @@
+import React from 'react';
+import {
+ Element,
+ Text,
+ Stack,
+ Button,
+ Integration,
+ ListAction,
+ ListItem,
+ List,
+} from '@codesandbox/components';
+import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
+import { css } from '@styled-system/css';
+import { useOvermind } from 'app/overmind';
+import { ZeitIcon, VisitIcon, TrashIcon } from './icons';
+import { State } from './elements';
+
+export const Zeit = () => {
+ const {
+ actions: { modalOpened, deployment },
+ state: {
+ deployment: { deploying, sandboxDeploys, deploysBeingDeleted },
+ user: { integrations },
+ },
+ } = useOvermind();
+ const { deploySandboxClicked, setDeploymentToDelete } = deployment;
+ return (
+
+
+
+ Enables
+ Deployments
+
+
+
+ {sandboxDeploys &&
+ sandboxDeploys.map(deploy => {
+ const disabled = deploysBeingDeleted
+ ? deploysBeingDeleted.includes(deploy.uid)
+ : deployment[`${deploy.uid}Deleting`];
+ return (
+
+
+ {deploy.name}
+
+
+
+ {deploy.state.toString().toLowerCase()}
+
+
+ {`(${distanceInWordsToNow(deploy.created)} ago)`}
+
+
+
+ window.open(`https://${deploy.url}`, '_blank')
+ }
+ >
+
+
+ {' '}
+ Visit Site
+
+ {
+ setDeploymentToDelete({ id: deploy.uid });
+ modalOpened({ modal: 'deleteDeployment' });
+ }}
+ >
+
+
+ {' '}
+ Delete
+
+
+ );
+ })}
+
+
+
+ );
+};
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts
new file mode 100644
index 00000000000..9c61fd27f45
--- /dev/null
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/elements.ts
@@ -0,0 +1,49 @@
+import { ZeitDeploymentState } from '@codesandbox/common/lib/types';
+import styled, { css } from 'styled-components';
+import { Text } from '@codesandbox/components';
+
+const mapColorToState = (state: ZeitDeploymentState, theme: any) => {
+ const STARTING = [
+ ZeitDeploymentState.BUILDING,
+ ZeitDeploymentState.DEPLOYING,
+ ZeitDeploymentState.INITIALIZING,
+ ];
+ const ERROR = [
+ ZeitDeploymentState.BUILD_ERROR,
+ ZeitDeploymentState.DEPLOYMENT_ERROR,
+ ZeitDeploymentState.ERROR,
+ ];
+ const STARTED = [ZeitDeploymentState.BOOTED, ZeitDeploymentState.READY];
+
+ if (STARTING.includes(state)) {
+ return '#FCCB7E';
+ }
+ if (ERROR.includes(state)) {
+ return theme.red;
+ }
+ if (STARTED.includes(state)) {
+ return theme.green;
+ }
+ if (state === ZeitDeploymentState.FROZEN) {
+ return theme.blue;
+ }
+
+ return theme.gray;
+};
+
+export const State = styled(Text)<{ state: ZeitDeploymentState }>`
+ ${({ state, theme }) => css`
+ display: flex;
+ align-items: center;
+ text-transform: capitalize;
+ &:before {
+ content: '';
+ display: block;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ margin-right: 0.5rem;
+ background: ${mapColorToState(state, theme)};
+ }
+ `};
+`;
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx
new file mode 100644
index 00000000000..c9e225f44fc
--- /dev/null
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/icons.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+
+export const NetlifyIcon = props => (
+
+);
+
+export const ZeitIcon = props => (
+
+);
+
+export const FileIcon = props => (
+
+);
+
+export const VisitIcon = props => (
+
+);
+
+export const FlagIcon = props => (
+
+);
+
+export const TrashIcon = props => (
+
+);
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx
new file mode 100644
index 00000000000..0bfddcd7179
--- /dev/null
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/Deployment/index.tsx
@@ -0,0 +1,68 @@
+import React, { FunctionComponent, useEffect } from 'react';
+
+import { useOvermind } from 'app/overmind';
+
+import { Element, Collapsible, Text } from '@codesandbox/components';
+
+import { Netlify } from './Netlify';
+import { Zeit } from './Zeit';
+
+import { More } from '../../items/More';
+
+export const Deployment: FunctionComponent = () => {
+ const {
+ actions: {
+ deployment: { getDeploys },
+ },
+ state: {
+ editor: { currentSandbox },
+ isLoggedIn,
+ },
+ } = useOvermind();
+ const showPlaceholder = !(currentSandbox.owned && isLoggedIn);
+
+ useEffect(() => {
+ if (!showPlaceholder) {
+ getDeploys();
+ }
+ }, [getDeploys, showPlaceholder]);
+
+ if (showPlaceholder) {
+ const message = isLoggedIn ? (
+ <>
+
+ You need to own this sandbox to deploy this sandbox to Netlify or
+ ZEIT.
+
+ Fork this sandbox to make a deploy!
+ >
+ ) : (
+
+ You need to be signed in to deploy this sandbox to Netlify or ZEIT.
+
+ );
+
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ You can deploy a production version of your sandbox using one our
+ supported providers.
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx
index 9d0d5bb4e2d..dcba4186be9 100644
--- a/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx
+++ b/packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Title.tsx
@@ -72,9 +72,7 @@ export const Title = ({ editable }) => {
onKeyUp={onKeyUp}
placeholder="Title"
ref={(el: any) => el && el.focus()}
- // @ts-ignore
type="text"
- // @ts-ignore
value={title}
/>
diff --git a/packages/codesandbox-api/src/dispatcher/index.ts b/packages/codesandbox-api/src/dispatcher/index.ts
index 10954b533da..d7c315a5b04 100644
--- a/packages/codesandbox-api/src/dispatcher/index.ts
+++ b/packages/codesandbox-api/src/dispatcher/index.ts
@@ -95,6 +95,7 @@ export function listen(callback: Callback): () => void {
}
export function notifyListeners(data: object, source?: MessageEvent['source']) {
+ // eslint-disable-next-line no-shadow
Object.keys(listeners).forEach(listenerId => {
if (listeners[listenerId]) {
listeners[listenerId](data, source);
diff --git a/packages/components/src/components/Button/index.tsx b/packages/components/src/components/Button/index.tsx
index d5e227e9ca1..eac4364d31f 100644
--- a/packages/components/src/components/Button/index.tsx
+++ b/packages/components/src/components/Button/index.tsx
@@ -85,6 +85,8 @@ const variantStyles = {
export const Button = styled(Element).attrs({ as: 'button' })<{
type?: 'submit' | 'button' | 'reset';
variant?: 'primary' | 'secondary' | 'link' | 'danger';
+ disabled?: boolean;
+ onClick?: any;
}>(({ variant = 'primary', ...props }) =>
css(
deepmerge(
diff --git a/packages/components/src/components/Element/index.tsx b/packages/components/src/components/Element/index.tsx
index ee4f9d9a6b9..b08a9b90145 100644
--- a/packages/components/src/components/Element/index.tsx
+++ b/packages/components/src/components/Element/index.tsx
@@ -7,6 +7,15 @@ export interface IElementProps {
marginY?: number;
marginBottom?: number;
marginTop?: number; // prefer margin bottom to top
+ marginLeft?: number;
+ marginRight?: number;
+ padding?: number;
+ paddingX?: number;
+ paddingY?: number;
+ paddingBottom?: number;
+ paddingTop?: number;
+ paddingLeft?: number;
+ paddingRight?: number;
css?: Object;
}
@@ -17,6 +26,15 @@ export const Element = styled.div(props =>
marginY: props.marginY || null,
marginBottom: props.marginBottom || null,
marginTop: props.marginTop || null,
+ marginLeft: props.marginLeft || null,
+ marginRight: props.marginRight || null,
+ padding: props.padding || null,
+ paddingX: props.paddingX || null,
+ paddingY: props.paddingY || null,
+ paddingBottom: props.paddingBottom || null,
+ paddingTop: props.paddingTop || null,
+ paddingLeft: props.paddingLeft || null,
+ paddingRight: props.paddingRight || null,
...(props.css || {}),
})
);
diff --git a/packages/components/src/components/Input/index.tsx b/packages/components/src/components/Input/index.tsx
index 4aef7eccbf8..a6b218b3cd8 100644
--- a/packages/components/src/components/Input/index.tsx
+++ b/packages/components/src/components/Input/index.tsx
@@ -7,7 +7,15 @@ const placeholderStyles = {
fontSize: 3,
};
-export const Input = styled(Element).attrs({ as: 'input' })(
+export const Input = styled(Element).attrs({ as: 'input' })<{
+ type?: string;
+ onBlur?: any;
+ onChange?: any;
+ onKeyUp?: any;
+ placeholder?: string;
+ ref?: any;
+ value?: string | number;
+}>(
css({
height: 6,
width: '100%',
diff --git a/packages/components/src/components/List/index.tsx b/packages/components/src/components/List/index.tsx
index 13a366821dd..8522d14d505 100644
--- a/packages/components/src/components/List/index.tsx
+++ b/packages/components/src/components/List/index.tsx
@@ -21,14 +21,15 @@ export const ListItem = styled(Stack).attrs({
})
);
-export const ListAction = styled(ListItem)(
- css({
- ':hover': {
- cursor: 'pointer',
- backgroundColor: 'sideBar.hoverBackground',
- },
- ':focus-within': {
- backgroundColor: 'sideBar.hoverBackground',
- },
- })
+export const ListAction = styled(ListItem)<{ disabled?: boolean }>(
+ ({ disabled }) =>
+ css({
+ ':hover': {
+ cursor: !disabled ? 'pointer' : 'disabled',
+ backgroundColor: !disabled ? 'sideBar.hoverBackground' : 'inherit',
+ },
+ ':focus-within': {
+ backgroundColor: !disabled ? 'sideBar.hoverBackground' : 'inherit',
+ },
+ })
);
diff --git a/packages/components/src/components/Select/index.tsx b/packages/components/src/components/Select/index.tsx
index b1c9ae8133b..cc7412467b2 100644
--- a/packages/components/src/components/Select/index.tsx
+++ b/packages/components/src/components/Select/index.tsx
@@ -60,9 +60,17 @@ const SelectWithIcon = styled(Element)(
interface ISelectProps {
icon?: any;
placeholder?: string;
+ children?: any;
+ disabled?: boolean;
+ onChange?: any;
+ value?: string | number;
}
-export const Select = ({ icon = null, placeholder = null, ...props }) => {
+export const Select = ({
+ icon = null,
+ placeholder = null,
+ ...props
+}: ISelectProps) => {
const PrefixIcon = icon || React.Fragment;
const SelectContainer = icon ? SelectWithIcon : React.Fragment;
diff --git a/packages/sandbox-hooks/console/index.js b/packages/sandbox-hooks/console/index.js
index 5890d603ef0..7c6a0a8841d 100644
--- a/packages/sandbox-hooks/console/index.js
+++ b/packages/sandbox-hooks/console/index.js
@@ -24,6 +24,7 @@ export default function setupConsole() {
try {
const wrapped = `(${data.command})`;
// `new Function` is used to validate Javascript syntax
+ // eslint-disable-next-line
const validate = new Function(wrapped);
data.command = wrapped;
} catch (e) {