Skip to content

Commit 628250e

Browse files
siddharthkpCompuIves
authored andcommitted
Embed: Make preview/editor resizable (codesandbox#2941)
* get split pane to work in embed * add a cute little toggle * snap to edges * get default size from props * handle toggle position for different vertical views * get full height back * add transition for resize toggle * breaking things to test on mobile * bring back the toggle * header buttons work again * hide resizer on mobile * only offset height if header is visible * handle 50% and pane container * add transition to snap * add sidebar toggle to header * handle screen resize * vertical panes only * remove header for mobile * refactor and simplify code * Move the heart button to preview * refactor actions + add refresh * add button to open in new window * get the right icons * fade transition * get animation right for navigation actions * add border on all sides for preview * Add types for new props * i dont know what to tell you fam * increase tap width for resizer * increase snap radius for the right side * add threshold for showing navigation icons on smaller screens * (has bugs) add click functionality for resizer * rename width to newSize * add opacity for border * update icons for navigation * move global actions inside splitpane * position open sandbox lower for editor * fold when like overlaps + isDragging transition * first stab at intro animation * animation intensifies * i think thats good * introduce on load for mobile * fix the animation timeline * fix opacity for animation * variables good good * use window.outerWidth because that doesnt lie * dont wait for mouseover on mobile * oh noes, outerWidth is also a lie * solve preview size problems * try out document.width instead * accept secret editorWidth prop
1 parent 65b43e8 commit 628250e

File tree

16 files changed

+563
-123
lines changed

16 files changed

+563
-123
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import styled from 'styled-components';
2+
import css from '@styled-system/css';
3+
import { HeartIconSVG, ReloadIconSVG, NewWindowIconSVG } from './icons';
4+
5+
// TODO: Check if we still need previewVisible
6+
export const Container = styled.div(props =>
7+
css({
8+
position: 'absolute',
9+
[props.align]: 16,
10+
zIndex: 99,
11+
12+
display: 'flex',
13+
// margin between consecutive elements
14+
'* + *': {
15+
marginLeft: 1,
16+
},
17+
18+
// 28 is the height of console
19+
bottom: props.isDragging ? -32 : props.previewVisible ? 28 + 16 : 16,
20+
opacity: props.isDragging ? 0 : 1,
21+
transitionProperty: 'opacity, bottom',
22+
transitionDuration: theme =>
23+
// go out fast, come back slow
24+
props.isDragging ? theme.speed[3] : theme.speed[5],
25+
})
26+
);
27+
28+
export const Button = styled.button(
29+
css({
30+
display: 'inline-flex',
31+
alignItems: 'center',
32+
height: 32,
33+
paddingX: 3,
34+
paddingY: 0,
35+
36+
fontSize: 3,
37+
fontWeight: 'medium',
38+
border: '1px solid',
39+
borderColor: 'grays.500',
40+
color: 'white',
41+
backgroundColor: 'grays.700',
42+
borderRadius: 4,
43+
textDecoration: 'none',
44+
cursor: 'pointer',
45+
46+
':hover': {
47+
backgroundColor: 'grays.500',
48+
},
49+
})
50+
);
51+
52+
export const HeartIcon = styled(HeartIconSVG)(props =>
53+
css({
54+
path: {
55+
stroke: props.liked ? 'reds.300' : 'white',
56+
fill: props.liked ? 'reds.300' : 'none',
57+
},
58+
})
59+
);
60+
61+
export const ReloadIcon = ReloadIconSVG;
62+
export const NewWindowIcon = NewWindowIconSVG;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
3+
export const HeartIconSVG = props => (
4+
<svg
5+
width="14"
6+
height="14"
7+
viewBox="0 0 14 14"
8+
fill="none"
9+
xmlns="http://www.w3.org/2000/svg"
10+
{...props}
11+
>
12+
<path
13+
d="M6.91607 12C17.423 6.10866 12.2269 -1.90872 6.91608 2.06249C1.8346 -1.90872 -3.36155 6.10865 6.91607 12Z"
14+
stroke="none"
15+
/>
16+
</svg>
17+
);
18+
19+
export const ReloadIconSVG = props => (
20+
<svg
21+
width="11"
22+
height="11"
23+
viewBox="0 0 11 11"
24+
fill="none"
25+
xmlns="http://www.w3.org/2000/svg"
26+
{...props}
27+
>
28+
<path
29+
fillRule="evenodd"
30+
clipRule="evenodd"
31+
d="M9.99523 0.450012H11L11 4.97145V5.97621H5.97619V4.97145H9.94213C9.49598 3.16488 7.87424 1.8261 5.94194 1.8261C3.66552 1.8261 1.82013 3.68416 1.82013 5.9762C1.82013 8.26824 3.66552 10.1263 5.94194 10.1263C7.25318 10.1263 8.42143 9.50981 9.17634 8.54899L9.77639 9.19091C8.86113 10.2964 7.48313 11 5.94194 11C3.18628 11 0.952377 8.75077 0.952377 5.9762C0.952377 3.20163 3.18628 0.952389 5.94194 0.952389C7.61147 0.952389 9.08949 1.778 9.99523 3.04586V0.450012Z"
32+
fill="currentcolor"
33+
/>
34+
</svg>
35+
);
36+
37+
export const NewWindowIconSVG = props => (
38+
<svg
39+
width="15"
40+
height="12"
41+
viewBox="0 0 15 12"
42+
fill="none"
43+
xmlns="http://www.w3.org/2000/svg"
44+
{...props}
45+
>
46+
<path
47+
fillRule="evenodd"
48+
clipRule="evenodd"
49+
d="M14.4545 0H10.5C10.1988 0 10 0.198754 10 0.5C10 0.801246 10.1988 1 10.5 1H13L9.5 4.5L10.5 5.5L14 2V4.5C14 4.80125 14.1988 5 14.5 5C14.8012 5 15 4.80125 15 4.5V0.545455C15 0.244208 14.7558 0 14.4545 0ZM1.73333 1H8.00001V1.86667H1.73333C1.25469 1.86667 0.866667 2.25469 0.866667 2.73333V9.32003C0.866667 9.79868 1.25469 10.1867 1.73333 10.1867H12.1333C12.612 10.1867 13 9.79868 13 9.32004V7.00003H13.8667V9.32004C13.8667 10.2773 13.0906 11.0534 12.1333 11.0534H1.73333C0.776041 11.0534 0 10.2773 0 9.32003V2.73333C0 1.77604 0.77604 1 1.73333 1Z"
50+
fill="currentcolor"
51+
/>
52+
</svg>
53+
);
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from 'react';
2+
import Tooltip from '@codesandbox/common/lib/components/Tooltip';
3+
import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator';
4+
5+
import {
6+
Container,
7+
Button,
8+
HeartIcon,
9+
ReloadIcon,
10+
NewWindowIcon,
11+
} from './elements';
12+
13+
export function GlobalActions({
14+
sandbox,
15+
toggleLike,
16+
previewVisible,
17+
isDragging,
18+
}) {
19+
return (
20+
<Container
21+
align="right"
22+
previewVisible={previewVisible}
23+
isDragging={isDragging}
24+
>
25+
{toggleLike ? (
26+
<Tooltip
27+
content={sandbox.userLiked ? 'Dislike sandbox' : 'Like sandbox'}
28+
>
29+
<Button
30+
onClick={toggleLike}
31+
aria-label={sandbox.userLiked ? 'Dislike sandbox' : 'Like sandbox'}
32+
>
33+
<HeartIcon liked={sandbox.userLiked} />
34+
</Button>
35+
</Tooltip>
36+
) : null}
37+
<Button
38+
as="a"
39+
target="_blank"
40+
rel="noopener noreferrer"
41+
href={`${sandboxUrl(sandbox)}?from-embed`}
42+
>
43+
Open Sandbox
44+
</Button>
45+
</Container>
46+
);
47+
}
48+
49+
export function NavigationActions({ refresh, openInNewWindow, isDragging }) {
50+
return (
51+
<Container align="left" previewVisible isDragging={isDragging}>
52+
<Tooltip content="Refresh preview">
53+
<Button onClick={refresh} aria-label="Refresh preview">
54+
<ReloadIcon />
55+
</Button>
56+
</Tooltip>
57+
<Tooltip content="Open preview in new window">
58+
<Button
59+
onClick={openInNewWindow}
60+
aria-label="Open preview in new window"
61+
>
62+
<NewWindowIcon />
63+
</Button>
64+
</Tooltip>
65+
</Container>
66+
);
67+
}

packages/app/src/embed/components/App/elements.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// @flow
22
import styled from 'styled-components';
3-
import { SIDEBAR_SHOW_SCREEN_SIZE } from '../../util/constants';
43

54
export const Container = styled.div`
65
display: flex;
@@ -27,12 +26,7 @@ export const Moving = styled.div`
2726
left: 0;
2827
right: 0;
2928
transform: translateX(${props => (props.sidebarOpen ? 250 : 0)}px);
30-
box-shadow: -3px 3px 3px rgba(0, 0, 0, 0.5);
31-
32-
@media (min-width: ${SIDEBAR_SHOW_SCREEN_SIZE}px) {
33-
left: 250px;
34-
transform: inherit;
35-
box-shadow: none;
36-
border-left: 1px solid ${props => props.theme.colors.sideBar.border};
37-
}
29+
border: 1px solid;
30+
/* 8 digit hex code with last 2 for opacity */
31+
border-color: ${props => props.theme.colors.sideBar.border + '33'};
3832
`;

packages/app/src/embed/components/App/index.js

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,10 @@ import {
1313
} from '@codesandbox/common/lib/sandbox/modules';
1414
import { Title } from 'app/components/Title';
1515
import { SubTitle } from 'app/components/SubTitle';
16-
import Header from '../Header';
1716
import Content from '../Content';
1817
import Sidebar from '../Sidebar';
19-
import EditorLink from '../EditorLink';
2018
import { Container, Fullscreen, Moving } from './elements';
19+
import { SIDEBAR_SHOW_SCREEN_SIZE } from '../../util/constants';
2120

2221
// Okay, this looks veeeery strange, we need this because Webpack has a bug currently
2322
// that makes it think we havecore-js/es6/map available in embed, but we don't.
@@ -101,7 +100,7 @@ export default class App extends React.PureComponent<
101100
isInProjectView,
102101
currentModule,
103102
initialPath,
104-
sidebarOpen: false,
103+
sidebarOpen: window.innerWidth > SIDEBAR_SHOW_SCREEN_SIZE,
105104
autoResize,
106105
hideNavigation,
107106
enableEslint,
@@ -194,6 +193,7 @@ export default class App extends React.PureComponent<
194193
}
195194
}
196195

196+
// TODO: See if this is still useful
197197
setEditorView = () => this.setState({ showEditor: true, showPreview: false });
198198
setPreviewView = () =>
199199
this.setState({ showEditor: false, showPreview: true });
@@ -359,20 +359,12 @@ export default class App extends React.PureComponent<
359359
}}
360360
>
361361
<Container>
362-
<Header
362+
<Content
363363
showEditor={showEditor}
364364
showPreview={showPreview}
365365
setEditorView={this.setEditorView}
366366
setPreviewView={this.setPreviewView}
367367
setMixedView={this.setMixedView}
368-
sandbox={sandbox}
369-
toggleSidebar={this.toggleSidebar}
370-
toggleLike={this.jwt() && this.toggleLike}
371-
liked={sandbox.userLiked}
372-
/>
373-
<Content
374-
showEditor={showEditor}
375-
showPreview={showPreview}
376368
previewWindow={previewWindow}
377369
isInProjectView={isInProjectView}
378370
setProjectView={this.setProjectView}
@@ -392,6 +384,9 @@ export default class App extends React.PureComponent<
392384
tabs={this.state.tabs}
393385
runOnClick={runOnClick}
394386
verticalMode={verticalMode}
387+
sidebarOpen={this.state.sidebarOpen}
388+
toggleSidebar={this.toggleSidebar}
389+
toggleLike={this.jwt() && this.toggleLike}
395390
/>
396391
</Container>
397392
</ThemeProvider>
@@ -410,10 +405,6 @@ export default class App extends React.PureComponent<
410405
currentModule={this.getCurrentModuleFromPath(sandbox).id}
411406
sandbox={sandbox}
412407
/>
413-
<EditorLink
414-
sandbox={sandbox}
415-
previewVisible={this.state.showPreview}
416-
/>
417408
</>
418409
)}
419410
<Moving sidebarOpen={this.state.sidebarOpen}>{this.content()}</Moving>

packages/app/src/embed/components/Content/elements.ts

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ export const Container = styled.div`
44
display: flex;
55
position: relative;
66
background-color: ${props => props.theme['editor.background']};
7-
/* compatibility mode for the redesign, the header is smaller now*/
8-
height: calc(100% - 32px);
7+
height: 100%;
98
`;
109

1110
export const Tabs = styled.div`
1211
display: flex;
13-
height: 35px;
14-
min-height: 35px;
12+
height: 33px;
13+
min-height: 33px;
1514
background-color: ${props => props.theme['tab.inactiveBackground']};
1615
/* shadow instead of border to align with the border of the child tab */
1716
box-shadow: inset 0px -1px 0 ${props => props.theme['sideBar.border']};
@@ -33,26 +32,9 @@ export const Tabs = styled.div`
3332
}
3433
`;
3534

36-
export const Split = styled.div<{
37-
verticalMode?: boolean;
38-
show?: boolean;
39-
only?: boolean;
40-
size?: number;
41-
}>`
42-
display: flex;
43-
flex-direction: column;
44-
position: relative;
45-
${props =>
46-
(props.verticalMode ? 'height: ' : 'width: ') +
47-
(props.show ? `${props.size}%` : '0px')};
48-
49-
${props =>
50-
(props.verticalMode ? 'max-height: ' : 'max-width: ') +
51-
(props.only ? '100%' : `${props.size}%`)};
52-
53-
${props =>
54-
(props.verticalMode ? 'min-height: ' : 'min-width: ') +
55-
(props.only ? '100%' : `${props.size}%`)};
56-
35+
export const MenuInTabs = styled.span`
36+
display: inline-flex;
5737
height: 100%;
38+
align-items: center;
39+
padding-left: 8px;
5840
`;

0 commit comments

Comments
 (0)