Skip to content

Commit fff61c4

Browse files
authored
Adds Create and Edit Template to new sidebar (codesandbox#3358)
* make work * clean css * return defaultOpen to his place0
1 parent d284819 commit fff61c4

File tree

5 files changed

+267
-57
lines changed

5 files changed

+267
-57
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React, { MouseEvent } from 'react';
2+
import getTemplateDefinition from '@codesandbox/common/lib/templates';
3+
import { useOvermind } from 'app/overmind';
4+
import { css } from '@styled-system/css';
5+
import { Button, Element, Stack } from '@codesandbox/components';
6+
7+
import { TemplateConfig } from './TemplateConfig';
8+
9+
export const Config = () => {
10+
const {
11+
actions: {
12+
modalOpened,
13+
workspace: { addedTemplate, deleteTemplate },
14+
},
15+
state: {
16+
user,
17+
editor: {
18+
currentSandbox: { customTemplate, template },
19+
},
20+
workspace: {
21+
project: { description, title },
22+
},
23+
},
24+
} = useOvermind();
25+
26+
const onDelete = (event: MouseEvent<HTMLButtonElement>) => {
27+
event.preventDefault();
28+
29+
if (customTemplate) {
30+
deleteTemplate();
31+
} else {
32+
modalOpened({ modal: 'deleteSandbox' });
33+
}
34+
};
35+
36+
const onCreateTemplate = (event: MouseEvent<HTMLButtonElement>) => {
37+
event.preventDefault();
38+
39+
if (!user) {
40+
modalOpened({ modal: 'signInForTemplates' });
41+
}
42+
43+
addedTemplate({
44+
color:
45+
(customTemplate && customTemplate.color) ||
46+
getTemplateDefinition(template).color(),
47+
description,
48+
title,
49+
});
50+
};
51+
52+
return (
53+
<>
54+
{customTemplate && <TemplateConfig />}
55+
{!customTemplate && (
56+
<Element marginX={2} marginY={4}>
57+
<Button
58+
// @ts-ignore
59+
onClick={onCreateTemplate}
60+
variant="secondary"
61+
>
62+
Save as template
63+
</Button>
64+
</Element>
65+
)}
66+
67+
<Stack
68+
justify="center"
69+
css={css({
70+
bottom: 3,
71+
position: 'absolute',
72+
width: '100%',
73+
})}
74+
>
75+
<Button
76+
// @ts-ignore
77+
onClick={onDelete}
78+
variant="link"
79+
css={css({
80+
':hover,:focus': { color: 'dangerButton.background' },
81+
})}
82+
>
83+
{`Delete ${customTemplate ? `Template` : `Sandbox`}`}
84+
</Button>
85+
</Stack>
86+
</>
87+
);
88+
};

packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/Privacy.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from 'react';
2-
import { withTheme } from 'styled-components';
32
import { Collapsible, Text, Stack, Select } from '@codesandbox/components';
43
import { useOvermind } from 'app/overmind';
4+
import { css } from '@styled-system/css';
55
import { GlobeIcon } from './icons';
66

7-
export const PrivacyComponent = ({ theme }) => {
7+
export const Privacy = () => {
88
const {
99
actions: {
1010
workspace: { sandboxPrivacyChanged },
@@ -17,11 +17,7 @@ export const PrivacyComponent = ({ theme }) => {
1717

1818
return (
1919
<Collapsible title="Privacy" defaultOpen>
20-
<Stack
21-
direction="vertical"
22-
gap={4}
23-
style={{ padding: `0 ${theme.space[3]}px` }}
24-
>
20+
<Stack direction="vertical" gap={4} css={css({ paddingX: 3 })}>
2521
<Select
2622
disabled={!isPatron}
2723
icon={GlobeIcon}
@@ -52,5 +48,3 @@ export const PrivacyComponent = ({ theme }) => {
5248
</Collapsible>
5349
);
5450
};
55-
56-
export const Privacy = withTheme(PrivacyComponent);
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import * as templates from '@codesandbox/common/lib/templates';
2+
import React, { FunctionComponent, useState } from 'react';
3+
import { SketchPicker } from 'react-color';
4+
import { useOvermind } from 'app/overmind';
5+
import {
6+
Collapsible,
7+
List,
8+
ListAction,
9+
Text,
10+
Element,
11+
} from '@codesandbox/components';
12+
import styled, { css } from 'styled-components';
13+
import getIcon from '@codesandbox/common/lib/templates/icons';
14+
import { ColorIcons as Icons } from '@codesandbox/template-icons';
15+
import { Popover, usePopoverState, PopoverDisclosure } from 'reakit/Popover';
16+
17+
const buttonStyles = css`
18+
padding: 0;
19+
border: 0;
20+
background: transparent;
21+
cursor: pointer;
22+
`;
23+
24+
const PickColor = styled(PopoverDisclosure)`
25+
${({ theme, color }) => css`
26+
width: ${theme.space[5]}px;
27+
height: ${theme.space[4]}px;
28+
border: 1px solid inherit;
29+
background: ${color};
30+
border-radius: ${theme.radii.small}px;
31+
`}
32+
`;
33+
34+
export const Button = styled(PopoverDisclosure)<{ color: string }>`
35+
${({ color }) => css`
36+
color: ${color};
37+
${buttonStyles}
38+
`};
39+
`;
40+
41+
export const IconButton = styled.button`
42+
${buttonStyles}
43+
`;
44+
45+
export const IconWrapper = styled(Popover)`
46+
${({ theme }) => css`
47+
padding: ${theme.space[3]}px;
48+
background: ${theme.colors.sideBar.background};
49+
`};
50+
`;
51+
52+
export const IconList = styled.ul`
53+
list-style: none;
54+
display: grid;
55+
padding: 0;
56+
margin: 0;
57+
grid-template-columns: repeat(7, 24px);
58+
grid-gap: 10px;
59+
60+
li {
61+
cursor: pointer;
62+
}
63+
`;
64+
65+
export const TemplateConfig: FunctionComponent = () => {
66+
const {
67+
actions: {
68+
workspace: { editTemplate },
69+
},
70+
state: {
71+
editor: {
72+
currentSandbox: { customTemplate, template },
73+
},
74+
},
75+
} = useOvermind();
76+
const iconPopover = usePopoverState({
77+
placement: 'top',
78+
});
79+
const colorPopover = usePopoverState({
80+
placement: 'top',
81+
});
82+
const [selectedIcon, setSelectedIcon] = useState(
83+
customTemplate.iconUrl || ''
84+
);
85+
86+
const DefaultIcon = getIcon(template);
87+
const defaultColor =
88+
customTemplate.color || templates.default(template).color();
89+
90+
const setIcon = (key: string) => {
91+
setSelectedIcon(key);
92+
iconPopover.hide();
93+
editTemplate({ ...customTemplate, iconUrl: key });
94+
};
95+
const TemplateIcon = Icons[selectedIcon];
96+
const [selectedColor, setSelectedColor] = useState(
97+
() => customTemplate.color || templates.default(template).color()
98+
);
99+
const colors = Object.keys(templates)
100+
.filter(x => x !== 'default')
101+
.map(t => templates[t])
102+
.map(a => ({ color: a.color(), title: a.niceName }));
103+
104+
return (
105+
<Collapsible title="Template" defaultOpen>
106+
<List>
107+
<ListAction justify="space-between" gap={2}>
108+
<Text>Color</Text>
109+
<Element>
110+
<PickColor {...colorPopover} color={selectedColor} />
111+
<Popover
112+
aria-label="Choose a Color"
113+
hideOnClickOutside
114+
hideOnEsc
115+
{...colorPopover}
116+
>
117+
<SketchPicker
118+
color={selectedColor}
119+
disableAlpha
120+
onChangeComplete={({ hex }) => {
121+
colorPopover.hide();
122+
setSelectedColor(hex);
123+
}}
124+
presetColors={[...new Set(colors)]}
125+
/>
126+
</Popover>
127+
</Element>
128+
</ListAction>
129+
<ListAction justify="space-between" gap={2}>
130+
<Text>Icon</Text>
131+
<Element>
132+
<Button {...iconPopover} color={defaultColor}>
133+
{selectedIcon && TemplateIcon ? (
134+
<TemplateIcon width={24} />
135+
) : (
136+
<DefaultIcon width={24} />
137+
)}
138+
</Button>
139+
<IconWrapper
140+
aria-label="Choose an Icon"
141+
hideOnClickOutside
142+
hideOnEsc
143+
{...iconPopover}
144+
>
145+
<IconList>
146+
{Object.keys(Icons).map((i: string) => {
147+
const TemplateIconMap = Icons[i];
148+
return (
149+
// eslint-disable-next-line
150+
<li onClick={() => setIcon(i)} role="button" tabIndex={0}>
151+
<IconButton>
152+
<TemplateIconMap width={24} />
153+
</IconButton>
154+
</li>
155+
);
156+
})}
157+
</IconList>
158+
</IconWrapper>
159+
</Element>
160+
</ListAction>
161+
</List>
162+
</Collapsible>
163+
);
164+
};

packages/app/src/app/pages/Sandbox/Editor/Workspace/screens/ProjectInfo/index.tsx

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, MouseEvent } from 'react';
1+
import React, { useEffect } from 'react';
22
import {
33
Element,
44
Collapsible,
@@ -7,33 +7,24 @@ import {
77
Stack,
88
List,
99
ListItem,
10-
Button,
1110
Switch,
1211
Stats,
1312
} from '@codesandbox/components';
1413
import { getSandboxName } from '@codesandbox/common/lib/utils/get-sandbox-name';
1514
import { useOvermind } from 'app/overmind';
16-
import styled, { withTheme } from 'styled-components';
1715

16+
import { css } from '@styled-system/css';
1817
import { Title } from './Title';
1918
import { Description } from './Description';
2019
import { Privacy } from './Privacy';
21-
22-
const DeleteButton = styled(Button)`
23-
&:hover,
24-
&:focus {
25-
color: ${props => props.theme.colors.dangerButton.background};
26-
}
27-
`;
20+
import { Config } from './Config';
2821

2922
const Link = props => <Text variant="muted" {...props} />;
3023

31-
export const ProjectInfoComponent = ({ theme }) => {
24+
export const ProjectInfo = () => {
3225
const {
3326
actions: {
34-
modalOpened,
3527
editor: { frozenUpdated, sessionFreezeOverride },
36-
workspace: { deleteTemplate },
3728
},
3829
state: {
3930
editor: { currentSandbox, sessionFrozen },
@@ -63,21 +54,11 @@ export const ProjectInfoComponent = ({ theme }) => {
6354
return frozenUpdated({ frozen: !isFrozen });
6455
};
6556

66-
const onDelete = (event: MouseEvent<HTMLButtonElement>) => {
67-
event.preventDefault();
68-
69-
if (customTemplate) {
70-
deleteTemplate();
71-
} else {
72-
modalOpened({ modal: 'deleteSandbox' });
73-
}
74-
};
75-
7657
return (
7758
<>
7859
<Collapsible title="Sandbox Info" defaultOpen>
7960
<Stack direction="vertical" gap={6}>
80-
<Element style={{ padding: `0 ${theme.space[3]}px` }}>
61+
<Element css={css({ paddingX: 2 })}>
8162
<Title editable />
8263
<Description editable />
8364
</Element>
@@ -86,7 +67,7 @@ export const ProjectInfoComponent = ({ theme }) => {
8667
gap={2}
8768
align="center"
8869
marginBottom={4}
89-
style={{ padding: `0 ${theme.space[3]}px` }}
70+
css={css({ paddingX: 2 })}
9071
>
9172
<Avatar user={author} /> <Text>{author.username}</Text>
9273
</Stack>
@@ -117,25 +98,7 @@ export const ProjectInfoComponent = ({ theme }) => {
11798
</Stack>
11899
</Collapsible>
119100
<Privacy />
120-
121-
<Element marginX={2} marginY={4}>
122-
<Button variant="secondary">Save as template</Button>
123-
</Element>
124-
125-
<Stack
126-
justify="center"
127-
style={{ position: 'absolute', width: '100%', bottom: theme.space[3] }}
128-
>
129-
<DeleteButton
130-
// @ts-ignore
131-
onClick={onDelete}
132-
variant="link"
133-
>
134-
{`Delete ${customTemplate ? `Template` : `Sandbox`}`}
135-
</DeleteButton>
136-
</Stack>
101+
<Config />
137102
</>
138103
);
139104
};
140-
141-
export const ProjectInfo = withTheme(ProjectInfoComponent);

0 commit comments

Comments
 (0)