Skip to content

Commit 84675b6

Browse files
authored
Improve embed for mobile view (codesandbox#3279)
* Style the resizer * Add new button, fix editor sizing * Undo unnecessary change * Add Edit Sandbox button * Change order of importance when opening a default module * Adjust knob extra width * Use tokens for font-size and spacing
1 parent 2796c49 commit 84675b6

File tree

15 files changed

+228
-152
lines changed

15 files changed

+228
-152
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import styled from 'styled-components';
22
import css from '@styled-system/css';
3+
import { CodeSandboxIcon as CodeSandboxIconBase } from '@codesandbox/common/lib/components/icons/CodeSandbox';
4+
import { PreviewIcon as PreviewIconBase } from '@codesandbox/common/lib/components/icons/Preview';
35
import { HeartIconSVG, ReloadIconSVG, NewWindowIconSVG } from './icons';
46

57
export const Container = styled.div(props =>
@@ -57,5 +59,20 @@ export const HeartIcon = styled(HeartIconSVG)(props =>
5759
})
5860
);
5961

62+
export const IconButton = styled(Button)`
63+
padding: 0 3px;
64+
`;
65+
66+
export const CodeSandboxIcon = styled(CodeSandboxIconBase)(() =>
67+
css({
68+
fontSize: 28,
69+
})
70+
);
71+
export const PreviewIcon = styled(PreviewIconBase)(() =>
72+
css({
73+
fontSize: 28,
74+
})
75+
);
76+
6077
export const ReloadIcon = ReloadIconSVG;
6178
export const NewWindowIcon = NewWindowIconSVG;

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

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,31 @@ import {
88
HeartIcon,
99
ReloadIcon,
1010
NewWindowIcon,
11+
CodeSandboxIcon,
12+
PreviewIcon,
13+
IconButton,
1114
} from './elements';
1215

1316
export function GlobalActions({
1417
sandbox,
1518
toggleLike,
1619
offsetBottom,
1720
isDragging,
21+
openEditor,
22+
openPreview,
23+
smallTouchScreen,
24+
previewVisible,
1825
}) {
26+
const smallTouchScreenButton = previewVisible ? (
27+
<Button onClick={openEditor}>View Source</Button>
28+
) : (
29+
<IconButton onClick={openPreview}>
30+
<PreviewIcon />
31+
</IconButton>
32+
);
33+
34+
const isEmbedded = window.parent !== window;
35+
1936
return (
2037
<Container
2138
align="right"
@@ -33,15 +50,31 @@ export function GlobalActions({
3350
<HeartIcon liked={sandbox.userLiked} />
3451
</Button>
3552
</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>
53+
) : (
54+
smallTouchScreen &&
55+
isEmbedded && (
56+
<IconButton
57+
as="a"
58+
target="_blank"
59+
rel="noopener noreferrer"
60+
href={sandboxUrl(sandbox)}
61+
>
62+
<CodeSandboxIcon />
63+
</IconButton>
64+
)
65+
)}
66+
{smallTouchScreen ? (
67+
smallTouchScreenButton
68+
) : (
69+
<Button
70+
as="a"
71+
target="_blank"
72+
rel="noopener noreferrer"
73+
href={`${sandboxUrl(sandbox)}?from-embed`}
74+
>
75+
Open Sandbox
76+
</Button>
77+
)}
4578
</Container>
4679
);
4780
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const Moving = styled.div`
3333
position: absolute;
3434
top: 0;
3535
bottom: 0;
36-
left: props.sidebarOpen ? 250 : 0;
36+
left: ${props => (props.sidebarOpen ? 250 : 0)}px;
3737
right: 0;
3838
width: ${props => (props.sidebarOpen ? 'calc(100% - 250px)' : '100%')};
3939
border: 1px solid;

packages/app/src/embed/components/Content/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type State = {
7979
dragging: boolean;
8080
running: boolean;
8181
currentDevToolPosition: DevToolsTabPosition;
82+
editorSize: number;
8283
};
8384
// eslint-disable-next-line import/no-default-export
8485
export default class Content extends React.PureComponent<Props, State> {
@@ -98,6 +99,7 @@ export default class Content extends React.PureComponent<Props, State> {
9899
devToolIndex: 0,
99100
tabPosition: 0,
100101
},
102+
editorSize: this.props.editorSize,
101103
};
102104

103105
this.errors = [];
@@ -369,6 +371,12 @@ export default class Content extends React.PureComponent<Props, State> {
369371
}
370372
};
371373

374+
setEditorSize = editorSize => {
375+
this.setState({
376+
editorSize,
377+
});
378+
};
379+
372380
openInNewWindow = () => {
373381
// this is set in app/Preview
374382
// i don't know why but I ain't complaining
@@ -491,14 +499,16 @@ export default class Content extends React.PureComponent<Props, State> {
491499
sandbox={sandbox}
492500
showEditor={showEditor}
493501
showPreview={showPreview}
494-
isMobile={verticalMode}
502+
isSmallScreen={verticalMode}
495503
sidebarOpen={sidebarOpen}
496504
showNavigationActions={hideNavigation}
497505
refresh={this.refresh}
498506
openInNewWindow={this.openInNewWindow}
499507
toggleLike={toggleLike}
500508
initialEditorSize={editorSize}
509+
setEditorSize={this.setEditorSize}
501510
hideDevTools={hideDevTools}
511+
setDragging={this.setDragging}
502512
>
503513
<>
504514
<Tabs>
@@ -567,6 +577,7 @@ export default class Content extends React.PureComponent<Props, State> {
567577
onChange={this.setCode}
568578
onModuleChange={this.setCurrentModule}
569579
highlightedLines={this.props.highlightedLines}
580+
width={this.state.editorSize}
570581
/>
571582
</div>
572583
</>

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,25 @@ export const Container = styled.div(
1919
height: isIOS ? '100%' : '100vh',
2020
})
2121
);
22+
23+
export const Button = styled.a(
24+
css({
25+
transition: '0.3s ease background-color',
26+
backgroundColor: theme => (theme.light ? 'grays.200' : 'grays.500'),
27+
padding: 2,
28+
display: 'block',
29+
color: theme => (theme.light ? 'grays.800' : 'white'),
30+
border: 'none',
31+
outline: 'none',
32+
borderRadius: 2,
33+
width: '100%',
34+
fontSize: 3,
35+
boxSizing: 'border-box',
36+
cursor: 'pointer',
37+
textDecoration: 'none',
38+
textAlign: 'center',
39+
':hover': {
40+
backgroundColor: theme => (theme.light ? 'grays.300' : 'grays.600'),
41+
},
42+
})
43+
);

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import * as React from 'react';
22
import type { Sandbox } from '@codesandbox/common/lib/types';
33

4+
import Margin from '@codesandbox/common/lib/components/spacing/Margin';
5+
import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator';
46
import Section from './Section';
57
import { SandboxInfo } from './SandboxInfo';
68
import FileTree from './FileTree';
79
import Dependencies from './Dependencies';
810
import ExternalResources from './ExternalResources';
911

10-
import { Container } from './elements';
12+
import { Container, Button } from './elements';
1113

1214
type Props = {
1315
sandbox: Sandbox,
@@ -37,6 +39,16 @@ function Sidebar({ sandbox, setCurrentModule, currentModule }: Props) {
3739
>
3840
<ExternalResources sandbox={sandbox} />
3941
</Section>
42+
43+
<Margin horizontal={0.5} vertical={1}>
44+
<Button
45+
href={sandboxUrl(sandbox) + '?from-embed'}
46+
target="_blank"
47+
rel="noreferrer noopener"
48+
>
49+
Edit Sandbox
50+
</Button>
51+
</Margin>
4052
</Container>
4153
);
4254
}
Lines changed: 27 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,56 @@
1-
import styled, { css, keyframes } from 'styled-components';
1+
import styled from 'styled-components';
22

3-
const introduction = {
4-
slideFromLeft: keyframes(css`
5-
0% {
6-
margin-left: -12px;
7-
background: #fff;
8-
opacity: 1;
9-
}
10-
50% {
11-
margin-left: 12px;
12-
background: #fff;
13-
opacity: 1;
14-
}
15-
100% {
16-
margin-left: 4px;
17-
background: #fff;
18-
opacity: 1;
19-
}
20-
`),
21-
slideFromRight: keyframes(css`
22-
0% {
23-
margin-left: 4px;
24-
background: #fff;
25-
opacity: 1;
26-
}
27-
50% {
28-
margin-left: -20px;
29-
background: #fff;
30-
opacity: 1;
31-
}
32-
100% {
33-
margin-left: -12px;
34-
background: #fff;
35-
opacity: 1;
36-
}
37-
`),
38-
fade: keyframes(css`
39-
0% {
40-
opacity: 1;
41-
background: #fff;
42-
}
43-
100% {
44-
opacity: 0.6;
45-
background: #242424;
46-
}
47-
`),
48-
};
49-
50-
const notIntroducedYetStyles = css`
51-
margin-left: ${props => (props.fullSize ? 4 : -12)}px;
52-
background: #fff;
53-
opacity: 1;
54-
`;
55-
56-
const introductionAnimation = css`
57-
animation: ${props =>
58-
props.fullSize
59-
? introduction.slideFromRight
60-
: introduction.slideFromLeft}
61-
1s,
62-
${introduction.fade} 1s 1s;
63-
animation-fill-mode: forwards, forwards;
64-
`;
3+
export const RESIZER_WIDTH = 16;
4+
export const KNOB_WIDTH = 4;
5+
/**
6+
* We leave a bit more room for the user to grab around the resize area, this defines
7+
* how much room that actually is
8+
*/
9+
export const RESIZER_GRAB_EXTRA_WIDTH = 4;
6510

6611
export const Container = styled.div`
6712
width: 100%;
6813
height: 100%;
6914
7015
.Resizer {
71-
z-index: 99;
7216
/* Safari, sigh.
7317
Quote: We recently encountered this and discovered that promoting the
7418
affected element to a composite layer with translateZ in CSS fixed
7519
the issue without needing extra JavaScript.
7620
— https://stackoverflow.com/a/21947628/1501871
7721
*/
7822
transform: translateZ(0);
23+
24+
background-color: ${props => props.theme.colors.separator.background};
25+
width: ${RESIZER_WIDTH}px;
26+
display: block;
27+
height: 100%;
28+
cursor: ew-resize;
29+
z-index: 50;
7930
}
8031
8132
.Resizer::after {
8233
content: '';
83-
background: #242424;
34+
display: flex;
35+
background: ${props => props.theme.colors.separator.foreground};
8436
border-radius: 50px;
85-
border: 1px solid #fff;
86-
transition: margin 500ms, height 150ms, top 150ms ease;
8737
position: absolute;
8838
89-
width: 5px;
90-
height: ${props => (props.isDragging ? 32 : 40)}px;
91-
top: ${props =>
92-
props.isDragging ? `calc(50% - 16px)` : `calc(50% - 20px)`};
93-
margin-left: ${props => (props.fullSize ? -12 : 4)}px;
94-
opacity: ${props => (props.isDragging ? 0.6 : 0.4)};
95-
96-
/* intro animations */
97-
${props => {
98-
if (props.hasBeenIntroduced) return null;
99-
if (props.hasAttention) return introductionAnimation;
100-
return notIntroducedYetStyles;
101-
}}
39+
width: ${KNOB_WIDTH}px;
40+
height: 41px;
41+
top: calc(50% - 20px);
42+
margin-left: ${Math.floor(RESIZER_WIDTH / 2 - KNOB_WIDTH / 2)}px;
43+
opacity: 1;
10244
}
10345
104-
.Resizer:hover::after {
105-
opacity: 0.6;
106-
}
107-
108-
/* Big tap area - 48*2 by 48*/
10946
.Resizer::before {
11047
content: '';
11148
position: absolute;
112-
width: calc(48px * 2);
113-
height: 64px;
114-
top: calc(50% - 32px);
115-
left: -48px;
116-
}
117-
118-
.Resizer {
119-
cursor: ew-resize;
49+
top: 0;
50+
bottom: 0;
51+
margin-left: -${RESIZER_GRAB_EXTRA_WIDTH}px;
52+
width: ${RESIZER_WIDTH + RESIZER_GRAB_EXTRA_WIDTH * 2}px;
53+
z-index: 50;
12054
}
12155
12256
.Pane {
@@ -130,12 +64,3 @@ export const PaneContainer = styled.div`
13064
flex-direction: column;
13165
height: 100%;
13266
`;
133-
134-
export const PointerOverlay = styled.div`
135-
position: absolute;
136-
top: 0;
137-
right: 0;
138-
bottom: 0;
139-
left: 0;
140-
z-index: 99;
141-
`;

0 commit comments

Comments
 (0)