Skip to content

Commit e66555d

Browse files
authored
Dashboard: draggable cards! (codesandbox#4091)
1 parent 222fcff commit e66555d

File tree

3 files changed

+55
-30
lines changed

3 files changed

+55
-30
lines changed

packages/app/src/app/pages/NewDashboard/Components/Sandbox/SandboxCard.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
Stats,
99
Input,
1010
SkeletonText,
11-
isMenuClicked,
1211
} from '@codesandbox/components';
1312
import css from '@styled-system/css';
1413
import { MenuOptions } from './Menu';
@@ -28,19 +27,14 @@ export const SandboxCard = ({
2827
enterEditing,
2928
...props
3029
}) => (
31-
<Link
32-
as={RouterLink}
33-
to={url}
34-
onClick={event => {
35-
if (edit || isMenuClicked(event)) event.preventDefault();
36-
}}
37-
>
30+
<Link as={RouterLink} to={url} {...props}>
3831
<Stack
3932
direction="vertical"
4033
gap={2}
4134
css={css({
4235
width: '100%',
4336
height: 240,
37+
backgroundColor: 'grays.700',
4438
border: '1px solid',
4539
borderColor: 'grays.600',
4640
borderRadius: 'medium',
@@ -52,7 +46,6 @@ export const SandboxCard = ({
5246
boxShadow: theme => '0 4px 16px 0 ' + theme.colors.grays[900],
5347
},
5448
})}
55-
{...props}
5649
>
5750
<Element
5851
as="div"

packages/app/src/app/pages/NewDashboard/Components/Sandbox/SandboxListItem.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
Link,
1010
ListAction,
1111
SkeletonText,
12-
isMenuClicked,
1312
} from '@codesandbox/components';
1413
import css from '@styled-system/css';
1514
import { MenuOptions } from './Menu';
@@ -30,14 +29,7 @@ export const SandboxListItem = ({
3029
...props
3130
}) => (
3231
<ListAction css={css({ paddingX: 0 })}>
33-
<Link
34-
as={RouterLink}
35-
to={url}
36-
onClick={event => {
37-
if (edit || isMenuClicked(event)) event.preventDefault();
38-
}}
39-
style={{ width: '100%' }}
40-
>
32+
<Link as={RouterLink} to={url} style={{ width: '100%' }} {...props}>
4133
<Stack
4234
gap={2}
4335
align="center"
@@ -49,7 +41,6 @@ export const SandboxListItem = ({
4941
borderBottomColor: 'grays.600',
5042
overflow: 'hidden',
5143
})}
52-
{...props}
5344
>
5445
<Stack gap={4} align="center">
5546
<Element

packages/app/src/app/pages/NewDashboard/Components/Sandbox/index.tsx

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import React from 'react';
22
import { useLocation } from 'react-router-dom';
3+
import { motion } from 'framer-motion';
34
import { useOvermind } from 'app/overmind';
45
import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator';
56
import { ESC } from '@codesandbox/common/lib/utils/keycodes';
7+
import { isMenuClicked } from '@codesandbox/components';
68
import { SandboxCard, SkeletonCard } from './SandboxCard';
79
import { SandboxListItem, SkeletonListItem } from './SandboxListItem';
810

@@ -22,6 +24,8 @@ export const Sandbox = ({ sandbox, isTemplate = false, ...props }) => {
2224
alias: sandbox.alias,
2325
});
2426

27+
/* Edit logic */
28+
2529
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
2630
setNewTitle(event.target.value);
2731
};
@@ -57,13 +61,43 @@ export const Sandbox = ({ sandbox, isTemplate = false, ...props }) => {
5761
setTimeout(() => inputRef.current.focus());
5862
};
5963

64+
/* Drag logic */
65+
66+
const [dragging, setDragging] = React.useState(false);
67+
68+
const onDragStart = () => {
69+
setDragging(true);
70+
};
71+
72+
const onDragEnd = () => {
73+
// delay by a frame so that click happens first
74+
window.requestAnimationFrame(() => setDragging(false));
75+
};
76+
77+
/* View logic */
78+
const location = useLocation();
79+
80+
let viewMode: string;
81+
if (location.pathname.includes('deleted')) viewMode = 'list';
82+
else if (location.pathname.includes('start')) viewMode = 'grid';
83+
else viewMode = dashboard.viewMode;
84+
85+
const Component = viewMode === 'list' ? SandboxListItem : SandboxCard;
86+
87+
/* Prevent opening sandbox while interacting */
88+
const onClick = event => {
89+
if (edit || dragging || isMenuClicked(event)) event.preventDefault();
90+
};
91+
6092
const sandboxProps = {
6193
sandboxTitle,
62-
newTitle,
6394
sandbox,
6495
isTemplate,
6596
url,
97+
onClick,
98+
// edit mode
6699
edit,
100+
newTitle,
67101
inputRef,
68102
onChange,
69103
onKeyDown,
@@ -72,17 +106,24 @@ export const Sandbox = ({ sandbox, isTemplate = false, ...props }) => {
72106
enterEditing,
73107
};
74108

75-
const location = useLocation();
76-
77-
let viewMode;
78-
if (location.pathname.includes('deleted')) viewMode = 'list';
79-
else if (location.pathname.includes('start')) viewMode = 'grid';
80-
else viewMode = dashboard.viewMode;
109+
const dragProps = {
110+
drag: true,
111+
// boundaries beyond which dragging is constrained
112+
// setting it to 0 brings card back to its position
113+
dragConstraints: { top: 0, bottom: 0, left: 0, right: 0 },
114+
// To allow drag dispite constraints, we can allow
115+
// movement outside constraints (it still springs back)
116+
dragElastic: 1,
117+
// event handlers
118+
onDragStart,
119+
onDragEnd,
120+
};
81121

82-
if (viewMode === 'list') {
83-
return <SandboxListItem {...sandboxProps} {...props} />;
84-
}
85-
return <SandboxCard {...sandboxProps} {...props} />;
122+
return (
123+
<motion.div {...dragProps}>
124+
<Component {...sandboxProps} {...props} />
125+
</motion.div>
126+
);
86127
};
87128

88129
export const SkeletonSandbox = props => {

0 commit comments

Comments
 (0)