From 1b490dcc5a059cf38983bb168e65c6bf19bb0ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Thu, 13 Feb 2020 22:46:51 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=A8=20Switch=20Dashboard/Sidebar?= =?UTF-8?q?=20to=20use=20useOvermind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/app/package.json | 4 +- .../Sidebar/Item/Container/elements.ts | 34 +++++ .../Sidebar/Item/Container/index.tsx | 25 ++++ .../pages/Dashboard/Sidebar/Item/elements.js | 52 -------- .../pages/Dashboard/Sidebar/Item/elements.ts | 34 +++++ .../app/pages/Dashboard/Sidebar/Item/index.js | 116 ------------------ .../pages/Dashboard/Sidebar/Item/index.tsx | 94 ++++++++++++++ .../SandboxesItem/FolderEntry/elements.ts | 50 ++++---- .../{elements.js => elements.ts} | 0 .../pages/Dashboard/Sidebar/TemplateItem.tsx | 86 +++++++++++++ .../Dashboard/Sidebar/TemplateItem/index.tsx | 80 ------------ .../app/pages/Dashboard/Sidebar/TrashItem.tsx | 77 ++++++++++++ .../Dashboard/Sidebar/TrashItem/index.tsx | 73 ----------- .../app/pages/Dashboard/Sidebar/elements.ts | 5 +- .../src/app/pages/Dashboard/Sidebar/index.tsx | 9 +- yarn.lock | 8 +- 16 files changed, 390 insertions(+), 357 deletions(-) create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/elements.ts create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/index.tsx delete mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.js create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.ts delete mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/index.js create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/Item/index.tsx rename packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/{elements.js => elements.ts} (100%) create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx delete mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem/index.tsx create mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx delete mode 100644 packages/app/src/app/pages/Dashboard/Sidebar/TrashItem/index.tsx diff --git a/packages/app/package.json b/packages/app/package.json index c3f6e2bd7b0..185f220d27b 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -204,7 +204,7 @@ "react-motion": "^0.5.0", "react-outside-click-handler": "^1.2.3", "react-refresh": "^0.7.2", - "react-router-dom": "^5.0.1", + "react-router-dom": "^5.1.2", "react-show": "^3.0.4", "react-split-pane": "^0.1.87", "react-spring": "^8.0.25", @@ -270,7 +270,7 @@ "@types/react-helmet": "^5.0.11", "@types/react-icons": "2.2.7", "@types/react-instantsearch": "^5.2.3", - "@types/react-router-dom": "^5.0.1", + "@types/react-router-dom": "^5.1.3", "@types/react-stripe-elements": "^1.3.2", "@types/resolve": "^0.0.8", "@types/semver": "6.2.0", diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/elements.ts b/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/elements.ts new file mode 100644 index 00000000000..267a50a4752 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/elements.ts @@ -0,0 +1,34 @@ +import { NavLink } from 'react-router-dom'; +import styled, { css } from 'styled-components'; + +export const Container = styled(NavLink)<{ active: boolean }>` + ${({ active, theme }) => css` + transition: 0.3s ease all; + display: flex; + width: 100%; + height: 2.5rem; + user-select: none; + + align-items: center; + + padding: 0 0.5rem; + box-sizing: border-box; + + color: ${theme.placeholder}; + text-decoration: none; + background-color: transparent; + + cursor: pointer; + position: relative; + + &:hover { + color: white; + } + + ${active && + css` + background-color: ${theme.secondary}; + color: white; + `}; + `}; +`; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/index.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/index.tsx new file mode 100644 index 00000000000..306746f5dc7 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/Item/Container/index.tsx @@ -0,0 +1,25 @@ +import React, { ComponentProps, FunctionComponent } from 'react'; + +import { ContextMenu } from 'app/components/ContextMenu'; + +import { Container as ContainerBase } from './elements'; + +type ContainerBaseProps = ComponentProps; +type Props = { + contextItems?: ComponentProps['items']; +} & Partial> & + Pick; +export const Container: FunctionComponent = ({ + contextItems, + ...props +}) => { + if (!contextItems) { + return ; + } + + return ( + + + + ); +}; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.js b/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.js deleted file mode 100644 index c6e71e811eb..00000000000 --- a/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.js +++ /dev/null @@ -1,52 +0,0 @@ -import styled, { css } from 'styled-components'; -import { NavLink } from 'react-router-dom'; -import ChevronRight from 'react-icons/lib/md/chevron-right'; - -export const AnimatedChevron = styled(ChevronRight)` - transition: 0.25s ease transform; - transform: rotate(${props => (props.open ? 90 : 0)}deg); - margin-right: 0.25rem; - width: 1rem; -`; - -export const Container = styled(NavLink)` - transition: 0.3s ease all; - display: flex; - width: 100%; - height: 2.5rem; - user-select: none; - - align-items: center; - - padding: 0 0.5rem; - box-sizing: border-box; - - color: ${props => props.theme.placeholder}; - text-decoration: none; - background-color: transparent; - - cursor: pointer; - position: relative; - - &:hover { - color: white; - } - - ${props => - props.active && - css` - background-color: ${props.theme.secondary}; - color: white; - `}; -`; - -export const IconContainer = styled.div` - display: flex; - align-items: center; - width: 2rem; - font-size: 1.25rem; -`; - -export const ItemName = styled.div` - font-size: 0.875rem; -`; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.ts b/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.ts new file mode 100644 index 00000000000..b4b6ddf2b6d --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/Item/elements.ts @@ -0,0 +1,34 @@ +import ChevronRight from 'react-icons/lib/md/chevron-right'; +import { Animate as AnimateBase } from 'react-show'; +import styled, { css } from 'styled-components'; + +export const Animate = styled(AnimateBase)` + height: auto; + overflow: hidden; +`; + +export const AnimatedChevron = styled(ChevronRight)<{ open: boolean }>` + ${({ open }) => css` + transition: 0.25s ease transform; + transform: rotate(${open ? 90 : 0}deg); + margin-right: 0.25rem; + width: 1rem; + `}; +`; + +export const ChevronPlaceholder = styled.div` + width: 16px; + height: 16px; + margin-right: 0.25rem; +`; + +export const IconContainer = styled.div` + display: flex; + align-items: center; + width: 2rem; + font-size: 1.25rem; +`; + +export const ItemName = styled.div` + font-size: 0.875rem; +`; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.js b/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.js deleted file mode 100644 index 0bb20171f5a..00000000000 --- a/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.js +++ /dev/null @@ -1,116 +0,0 @@ -import React from 'react'; -import { Route } from 'react-router-dom'; -import { Animate } from 'react-show'; -import { ContextMenu } from 'app/components/ContextMenu'; -import { - AnimatedChevron, - Container, - IconContainer, - ItemName, -} from './elements'; - -const getContainer = contextItems => { - if (contextItems) { - return props => ( - - {React.createElement(Container, props)} - - ); - } - - return Container; -}; - -export class Item extends React.Component { - state = { - open: this.props.openByDefault, - }; - - toggleOpen = e => { - e.preventDefault(); - this.setState(state => ({ open: !state.open })); - }; - - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.openByDefault === true && !this.props.openByDefault) { - this.setState({ open: true }); - } - } - - render() { - const { - name, - contextItems, - Icon, - path, - children, - style, - active, - noActive, - ...props - } = this.props; - - const UsedContainer = getContainer(contextItems); - - return ( - - {res => { - const isActive = - (!noActive && res.match && res.match.isExact) || active; - const isOpen = - this.state.open === undefined ? isActive : this.state.open; - - if ( - (res.match || isActive) && - this.state.open === undefined && - children - ) { - this.setState({ open: true }); - } - return ( - <> - - {children ? ( - - ) : ( -
- )} - - - - {name} - - - {children && ( - - {children} - - )} - - ); - }} - - ); - } -} diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.tsx new file mode 100644 index 00000000000..5cc05c87c1f --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/Item/index.tsx @@ -0,0 +1,94 @@ +import React, { + ComponentProps, + ComponentType, + FunctionComponent, + useEffect, + useState, +} from 'react'; +import { useRouteMatch } from 'react-router-dom'; + +import { Container } from './Container'; +import { + Animate, + AnimatedChevron, + ChevronPlaceholder, + IconContainer, + ItemName, +} from './elements'; + +type ContainerProps = ComponentProps; +type Props = { + active?: boolean; + Icon: ComponentType; + name: string; + openByDefault?: boolean; + path: ContainerProps['to']; +} & Omit< + ComponentProps, + 'active' | 'children' | 'exact' | 'to' +>; +export const Item: FunctionComponent = ({ + active = false, + children, + Icon, + name, + openByDefault, + path, + ...props +}) => { + const match = useRouteMatch(path); + const [open, setOpen] = useState(openByDefault); + const isActive = match?.isExact || active; + const isOpen = open || isActive; + + useEffect(() => { + if (openByDefault) { + setOpen(true); + } + }, [openByDefault]); + + useEffect(() => { + if ((match || isActive) && open === undefined && children) { + setOpen(true); + } + }, [children, isActive, match, open]); + + const toggleOpen = event => { + event.preventDefault(); + + setOpen(show => !show); + }; + + return ( + <> + + {children ? ( + + ) : ( + + )} + + + + + + {name} + + + {children && ( + + {children} + + )} + + ); +}; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/FolderEntry/elements.ts b/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/FolderEntry/elements.ts index 14b6125e086..476874acf4d 100644 --- a/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/FolderEntry/elements.ts +++ b/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/FolderEntry/elements.ts @@ -1,34 +1,36 @@ -import styled from 'styled-components'; -import { NavLink } from 'react-router-dom'; import ChevronRight from 'react-icons/lib/md/chevron-right'; +import { NavLink } from 'react-router-dom'; +import styled, { css } from 'styled-components'; export const Container = styled(NavLink)<{ depth?: number; }>` - transition: 0.25s ease all; - display: flex; - align-items: center; - height: 2rem; + ${({ depth, theme }) => css` + transition: 0.25s ease all; + display: flex; + align-items: center; + height: 2rem; - color: rgba(255, 255, 255, 0.6); - text-decoration: none; + color: rgba(255, 255, 255, 0.6); + text-decoration: none; - border-left: 2px solid transparent; - padding-left: ${props => 1 + (props.depth || 0) * 0.75}rem; + border-left: 2px solid transparent; + padding-left: ${1 + (depth || 0) * 0.75}rem; - user-select: none; + user-select: none; - cursor: pointer; + cursor: pointer; - &:hover { - color: rgba(255, 255, 255, 0.8); - } + &:hover { + color: rgba(255, 255, 255, 0.8); + } - &:focus { - outline: none; - border-color: ${props => props.theme.secondary.clearer(0.5)}; - color: rgba(255, 255, 255, 0.8); - } + &:focus { + outline: none; + border-color: ${theme.secondary.clearer(0.5)}; + color: rgba(255, 255, 255, 0.8); + } + `}; `; export const CreateDirectoryContainer = Container.withComponent('div'); @@ -41,7 +43,9 @@ export const IconContainer = styled.div` `; export const AnimatedChevron = styled(ChevronRight)<{ open?: boolean }>` - transition: 0.25s ease transform; - transform: rotate(${props => (props.open ? 90 : 0)}deg); - margin-right: 0.25rem; + ${({ open }) => css` + transition: 0.25s ease transform; + transform: rotate(${open ? 90 : 0}deg); + margin-right: 0.25rem; + `}; `; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/elements.js b/packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/elements.ts similarity index 100% rename from packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/elements.js rename to packages/app/src/app/pages/Dashboard/Sidebar/SandboxesItem/elements.ts diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx new file mode 100644 index 00000000000..9cdde2f87ff --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx @@ -0,0 +1,86 @@ +import React, { FunctionComponent } from 'react'; +import { + DropTarget, + DropTargetCollector, + DropTargetConnector, + DropTargetMonitor, + DropTargetSpec, +} from 'react-dnd'; + +import { MAKE_TEMPLATE_DROP_KEY } from '../Content/SandboxCard'; + +import { Item } from './Item'; + +import TemplateIcon from '-!svg-react-loader!@codesandbox/common/lib/icons/template.svg'; + +type OwnProps = { + currentPath: string; + teamId?: string; +}; +type Props = OwnProps & CollectedProps; +const TemplateItemComponent: FunctionComponent = ({ + canDrop, + connectDropTarget, + currentPath, + isOver, + teamId, +}) => { + const url = teamId + ? `/dashboard/teams/${teamId}/templates` + : `/dashboard/templates`; + + return connectDropTarget( +
+ +
+ ); +}; + +const entryTarget: DropTargetSpec = { + canDrop: (_props, monitor) => monitor.getItem() !== null, + drop: ({ teamId }, monitor) => { + // Check if only child is selected: + if (!monitor.isOver({ shallow: true })) { + return {}; + } + + // Used in SandboxCard + return { + [MAKE_TEMPLATE_DROP_KEY]: true, + teamId, + }; + }, +}; + +const collectTarget: DropTargetCollector = ( + connect, + monitor +) => ({ + canDrop: monitor.canDrop(), + // Call this function inside render() + // to let React DnD handle the drag events: + connectDropTarget: connect.dropTarget(), + // You can ask the monitor about the current drag state: + isOver: monitor.isOver({ shallow: true }), +}); + +type CollectedProps = { + canDrop: ReturnType; + connectDropTarget: ReturnType; + isOver: ReturnType; +}; +export const TemplateItem = DropTarget( + ['SANDBOX'], + entryTarget, + collectTarget +)(TemplateItemComponent); diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem/index.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem/index.tsx deleted file mode 100644 index 56a24f32dff..00000000000 --- a/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; -import { DropTarget } from 'react-dnd'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -// @ts-ignore -import TemplateIcon from '-!svg-react-loader!@codesandbox/common/lib/icons/template.svg'; -import { Item } from '../Item'; -import { MAKE_TEMPLATE_DROP_KEY } from '../../Content/SandboxCard'; - -interface ITemplateItemProps { - currentPath: string; - teamId?: string; - canDrop?: boolean; - isOver?: boolean; - connectDropTarget?: any; -} - -const TemplateItemComponent: React.FC = ({ - currentPath, - isOver, - canDrop, - connectDropTarget, - teamId, -}) => { - const url = teamId - ? `/dashboard/teams/${teamId}/templates` - : `/dashboard/templates`; - - return connectDropTarget( -
- -
- ); -}; - -export const entryTarget = { - drop: (props, monitor) => { - if (monitor == null) return {}; - - // Check if only child is selected: - if (!monitor.isOver({ shallow: true })) return {}; - - // Used in SandboxCard - return { [MAKE_TEMPLATE_DROP_KEY]: true, teamId: props.teamId }; - }, - - canDrop: (props, monitor) => { - if (monitor == null) return false; - const source = monitor.getItem(); - if (source == null) return false; - - return !props.removedAt; - }, -}; - -export function collectTarget(connectMonitor, monitor) { - return { - // Call this function inside render() - // to let React DnD handle the drag events: - connectDropTarget: connectMonitor.dropTarget(), - // You can ask the monitor about the current drag state: - isOver: monitor.isOver({ shallow: true }), - canDrop: monitor.canDrop(), - itemType: monitor.getItemType(), - }; -} - -export const TemplateItem = DropTarget( - ['SANDBOX'], - entryTarget, - collectTarget -)(withRouter(TemplateItemComponent)); diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx new file mode 100644 index 00000000000..2644bb62611 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx @@ -0,0 +1,77 @@ +import React, { FunctionComponent } from 'react'; +import { + DropTarget, + DropTargetCollector, + DropTargetConnector, + DropTargetMonitor, + DropTargetSpec, +} from 'react-dnd'; +import TrashIcon from 'react-icons/lib/md/delete'; + +import { DELETE_SANDBOX_DROP_KEY } from '../Content/SandboxCard'; + +import { Item } from './Item'; + +type OwnProps = { + currentPath: string; +}; +type Props = OwnProps & CollectedProps; +const TrashItemComponent: FunctionComponent = ({ + canDrop, + connectDropTarget, + currentPath, + isOver, +}) => + connectDropTarget( +
+ +
+ ); + +const entryTarget: DropTargetSpec = { + canDrop: (_props, monitor) => monitor.getItem() !== null, + drop: (_props, monitor) => { + // Check if only child is selected: + if (!monitor.isOver({ shallow: true })) { + return {}; + } + + // Used in SandboxCard + return { + [DELETE_SANDBOX_DROP_KEY]: true, + }; + }, +}; + +const collectTarget: DropTargetCollector = ( + connect, + monitor +) => ({ + canDrop: monitor.canDrop(), + // Call this function inside render() + // to let React DnD handle the drag events: + connectDropTarget: connect.dropTarget(), + // You can ask the monitor about the current drag state: + isOver: monitor.isOver({ shallow: true }), +}); + +type CollectedProps = { + canDrop: ReturnType; + connectDropTarget: ReturnType; + isOver: ReturnType; +}; +export const TrashItem = DropTarget( + ['SANDBOX'], + entryTarget, + collectTarget +)(TrashItemComponent); diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem/index.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem/index.tsx deleted file mode 100644 index df4b054b534..00000000000 --- a/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { DropTarget } from 'react-dnd'; -import TrashIcon from 'react-icons/lib/md/delete'; - -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { Item } from '../Item'; -import { DELETE_SANDBOX_DROP_KEY } from '../../Content/SandboxCard'; - -interface Props { - currentPath: string; - isOver: boolean; - canDrop: boolean; - connectDropTarget: (target: React.ReactElement) => React.ReactElement; -} - -const TrashItemComponent: React.FC = ({ - currentPath, - isOver, - canDrop, - connectDropTarget, -}) => - connectDropTarget( -
- -
- ); - -export const entryTarget = { - drop: (_, monitor) => { - if (monitor == null) return {}; - - // Check if only child is selected: - if (!monitor.isOver({ shallow: true })) return {}; - - // Used in SandboxCard - return { [DELETE_SANDBOX_DROP_KEY]: true }; - }, - - canDrop: (props, monitor) => { - if (monitor == null) return false; - const source = monitor.getItem(); - if (source == null) return false; - - return !props.removedAt; - }, -}; - -export function collectTarget(connectMonitor, monitor) { - return { - // Call this function inside render() - // to let React DnD handle the drag events: - connectDropTarget: connectMonitor.dropTarget(), - // You can ask the monitor about the current drag state: - isOver: monitor.isOver({ shallow: true }), - canDrop: monitor.canDrop(), - itemType: monitor.getItemType(), - }; -} - -export const TrashItem = DropTarget( - ['SANDBOX'], - entryTarget, - collectTarget -)(withRouter(TrashItemComponent)); diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/elements.ts b/packages/app/src/app/pages/Dashboard/Sidebar/elements.ts index 80b826fd9ca..ea60f06406d 100644 --- a/packages/app/src/app/pages/Dashboard/Sidebar/elements.ts +++ b/packages/app/src/app/pages/Dashboard/Sidebar/elements.ts @@ -19,12 +19,11 @@ export const CategoryHeader = styled.div` font-weight: 600; `; -export const SidebarStyled = styled.aside` +export const Container = styled.aside` width: 275px; overflow-y: auto; `; export const InputWrapper = styled.div` - margin: 0 1rem; - margin-bottom: 1.5rem; + margin: 0 1rem 1.5rem; `; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/index.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/index.tsx index 7bdce854ed3..e087f37883c 100644 --- a/packages/app/src/app/pages/Dashboard/Sidebar/index.tsx +++ b/packages/app/src/app/pages/Dashboard/Sidebar/index.tsx @@ -12,14 +12,14 @@ import DashboardIcon from '-!svg-react-loader!@codesandbox/common/lib/icons/dash import { Item } from './Item'; import { SandboxesItem } from './SandboxesItem'; import { TrashItem } from './TrashItem'; -import { Items, CategoryHeader, SidebarStyled, InputWrapper } from './elements'; +import { CategoryHeader, Container, InputWrapper, Items } from './elements'; import { TEAMS_QUERY } from '../queries'; import { TemplateItem } from './TemplateItem'; const SidebarComponent = () => { const { - state: { dashboard: dashboardState }, actions: { dashboard: dashboardAction }, + state: { dashboard: dashboardState }, } = useOvermind(); const handleSearchFocus = () => { @@ -31,7 +31,7 @@ const SidebarComponent = () => { }; return ( - + {
{name} + { Create Team
-
+ ); }; export const Sidebar = withRouter(SidebarComponent); diff --git a/yarn.lock b/yarn.lock index b5f85fc5da0..8235b5b9067 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4727,10 +4727,10 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-router-dom@^5.0.1": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090" - integrity sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw== +"@types/react-router-dom@^5.1.3": + version "5.1.3" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196" + integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA== dependencies: "@types/history" "*" "@types/react" "*" From 63ce1937ccb0a759f66f5fec2dc41bebb30a66bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Tue, 18 Feb 2020 16:18:38 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=A8=20Switch=20Dashboard/Content?= =?UTF-8?q?=20to=20use=20useOvermind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/app/package.json | 7 +- .../DragLayer/SelectedSandboxItems/index.tsx | 48 ----------- .../Dashboard/Content/DragLayer/index.js | 82 ------------------- .../AnimatedSandboxItem.tsx | 39 ++++----- .../SelectedSandboxItems/elements.ts | 12 +-- .../DragLayer/SelectedSandboxItems/index.tsx | 41 ++++++++++ .../Content/SandboxGrid/DragLayer/elements.ts | 11 +++ .../Content/SandboxGrid/DragLayer/index.tsx | 60 ++++++++++++++ .../Dashboard/Content/SandboxGrid/Row.js | 64 --------------- .../Dashboard/Content/SandboxGrid/Row.tsx | 80 ++++++++++++++++++ .../SandboxCard/KebabIcon.tsx | 0 .../{ => SandboxGrid}/SandboxCard/elements.ts | 0 .../{ => SandboxGrid}/SandboxCard/index.tsx | 56 +++++++------ .../Content/SandboxGrid/Selection/elements.ts | 12 +++ .../Selection/index.tsx} | 35 +++++--- .../Dashboard/Content/SandboxGrid/elements.js | 29 ------- .../Dashboard/Content/SandboxGrid/elements.ts | 32 ++++++++ .../Dashboard/Content/SandboxGrid/index.tsx | 30 ++++--- .../Content/{elements.js => elements.ts} | 21 ++--- .../src/app/pages/Dashboard/Content/index.js | 39 --------- .../src/app/pages/Dashboard/Content/index.tsx | 46 +++++++++++ .../pages/Dashboard/Sidebar/TemplateItem.tsx | 2 +- .../app/pages/Dashboard/Sidebar/TrashItem.tsx | 2 +- .../app/src/app/pages/Dashboard/index.tsx | 2 +- yarn.lock | 64 ++++++++++----- 25 files changed, 435 insertions(+), 379 deletions(-) delete mode 100644 packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/index.tsx delete mode 100644 packages/app/src/app/pages/Dashboard/Content/DragLayer/index.js rename packages/app/src/app/pages/Dashboard/Content/{ => SandboxGrid}/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx (75%) rename packages/app/src/app/pages/Dashboard/Content/{ => SandboxGrid}/DragLayer/SelectedSandboxItems/elements.ts (71%) create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/index.tsx create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/elements.ts create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/index.tsx delete mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.js create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.tsx rename packages/app/src/app/pages/Dashboard/Content/{ => SandboxGrid}/SandboxCard/KebabIcon.tsx (100%) rename packages/app/src/app/pages/Dashboard/Content/{ => SandboxGrid}/SandboxCard/elements.ts (100%) rename packages/app/src/app/pages/Dashboard/Content/{ => SandboxGrid}/SandboxCard/index.tsx (96%) create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/elements.ts rename packages/app/src/app/pages/Dashboard/Content/{Selection/index.js => SandboxGrid/Selection/index.tsx} (52%) delete mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.js create mode 100644 packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.ts rename packages/app/src/app/pages/Dashboard/Content/{elements.js => elements.ts} (61%) delete mode 100644 packages/app/src/app/pages/Dashboard/Content/index.js create mode 100644 packages/app/src/app/pages/Dashboard/Content/index.tsx diff --git a/packages/app/package.json b/packages/app/package.json index 185f220d27b..7064afd36d5 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -190,8 +190,8 @@ "react-content-loader": "^4.2.2", "react-day-picker": "^7.2.4", "react-devtools-inline": "^4.4.0", - "react-dnd": "^9.4.0", - "react-dnd-html5-backend": "^9.4.0", + "react-dnd": "^9.5.1", + "react-dnd-html5-backend": "^9.5.1", "react-dom": "^16.9.0", "react-error-overlay": "^1.0.10", "react-icons": "^2.2.7", @@ -211,7 +211,7 @@ "react-stripe-elements": "^5.0.0", "react-tagsinput": "^3.19.0", "react-use": "^9.7.2", - "react-virtualized": "^9.19.1", + "react-virtualized": "^9.21.2", "reakit": "^1.0.0-beta.4", "resize-observer-polyfill": "^1.5.1", "sha1": "^1.1.1", @@ -270,6 +270,7 @@ "@types/react-helmet": "^5.0.11", "@types/react-icons": "2.2.7", "@types/react-instantsearch": "^5.2.3", + "@types/react-virtualized": "^9.21.8", "@types/react-router-dom": "^5.1.3", "@types/react-stripe-elements": "^1.3.2", "@types/resolve": "^0.0.8", diff --git a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/index.tsx b/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/index.tsx deleted file mode 100644 index 26553d3bcfe..00000000000 --- a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useMemo } from 'react'; -import { useOvermind } from 'app/overmind'; - -import { AnimatedSandboxItem } from './AnimatedSandboxItem'; - -interface ISelectedSandboxItemsProps { - x: number; - y: number; - isOverPossibleTargets: boolean; - id: string; -} - -export const SelectedSandboxItems: React.FC = ({ - x, - y, - isOverPossibleTargets, - id, -}) => { - const { - state: { - dashboard: { selectedSandboxes }, - }, - } = useOvermind(); - - const selectedIds = useMemo( - () => [id, ...selectedSandboxes.filter(b => b !== id)], - [id, selectedSandboxes] - ); - - const scale = isOverPossibleTargets ? 0.4 : 0.8; - - return ( - <> - {selectedIds.map((sid, i) => ( - - ))} - - ); -}; diff --git a/packages/app/src/app/pages/Dashboard/Content/DragLayer/index.js b/packages/app/src/app/pages/Dashboard/Content/DragLayer/index.js deleted file mode 100644 index d2c2d985e11..00000000000 --- a/packages/app/src/app/pages/Dashboard/Content/DragLayer/index.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; - -import { DragLayer as DNDDragLayer } from 'react-dnd'; - -import { SelectedSandboxItems } from './SelectedSandboxItems'; - -const layerStyles = { - position: 'fixed', - pointerEvents: 'none', - zIndex: 100, - left: 0, - top: 0, - width: '100%', - height: '100%', -}; - -function getItemCoords(props) { - const { currentOffset } = props; - - const { x, y } = currentOffset; - - return { - x, - y, - }; -} - -class CustomDragLayer extends React.Component { - renderItem(type, item, isOverPossibleTargets, { x, y }) { - if (type !== 'SANDBOX') { - return null; - } - return ( - - ); - } - - render() { - const { - item, - itemType, - isOverPossibleTargets, - currentOffset, - isDragging, - } = this.props; - - if (!isDragging || !currentOffset) { - return null; - } - - return ( -
-
- {this.renderItem( - itemType, - item, - isOverPossibleTargets, - getItemCoords(this.props) - )} -
-
- ); - } -} - -function collect(monitor) { - const isOverPossibleTargets = monitor.getTargetIds().length > 0; - return { - item: monitor.getItem(), - itemType: monitor.getItemType(), - currentOffset: monitor.getSourceClientOffset(), - isDragging: monitor.isDragging(), - isOverPossibleTargets, - }; -} - -export const DragLayer = DNDDragLayer(collect)(CustomDragLayer); diff --git a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx similarity index 75% rename from packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx index e0d543e1b9c..f45eadc294c 100644 --- a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/AnimatedSandboxItem.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, FunctionComponent } from 'react'; import { Spring, animated, interpolate } from 'react-spring/renderprops'; import { @@ -10,22 +10,21 @@ import { type Props = { id: string; - i: number; + index: number; + isLast: boolean; + scale: number; + selectedSandboxes: string[]; x: number; y: number; - scale: number; - isLast: boolean; - selectedSandboxes: Array; }; - -export const AnimatedSandboxItem: React.FC = ({ +export const AnimatedSandboxItem: FunctionComponent = ({ id, - i, - x, - y, - scale, + index, isLast, + scale, selectedSandboxes, + x, + y, }) => { const [render, setRender] = useState(true); const [position, setPosition] = useState(null); @@ -39,7 +38,7 @@ export const AnimatedSandboxItem: React.FC = ({ setPosition(sandboxBrotherItem.getBoundingClientRect() as DOMRect); } - if (i !== 0 && !isLast) { + if (index !== 0 && !isLast) { timeout = global.setTimeout(() => { setRender(false); }, 200); @@ -50,7 +49,7 @@ export const AnimatedSandboxItem: React.FC = ({ global.clearTimeout(timeout); } }; - }, [id, i, isLast]); + }, [id, index, isLast]); if (!render || !position) { return null; @@ -59,7 +58,7 @@ export const AnimatedSandboxItem: React.FC = ({ return ( el !== 'scale' : false} + immediate={index === 0 ? el => el !== 'scale' : false} from={{ x: position.x, y: position.y, shadow: 2, scale: 1 }} to={{ scale, x, y, shadow: isLast ? 16 : 2 }} key={id} @@ -70,7 +69,7 @@ export const AnimatedSandboxItem: React.FC = ({ position: 'absolute', willChange: 'transform', boxShadow: - i === 0 || isLast + index === 0 || isLast ? interpolate( [newShadow], s => `0 ${s}px ${s * 2}px rgba(0, 0, 0, 0.3)` @@ -81,20 +80,22 @@ export const AnimatedSandboxItem: React.FC = ({ (xx, yy, zz) => `translate3d(${xx}px, ${yy}px, 0px) scale3d(${zz}, ${zz}, ${zz})` ), - zIndex: i === 0 ? 20 : 10, + zIndex: index === 0 ? 20 : 10, }} > + - {selectedSandboxes.length}{' '} - {selectedSandboxes.length === 1 ? 'Sandbox' : 'Sandboxes'} + {`${selectedSandboxes.length} ${ + selectedSandboxes.length === 1 ? 'Sandbox' : 'Sandboxes' + }`} diff --git a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/elements.ts b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/elements.ts similarity index 71% rename from packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/elements.ts rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/elements.ts index bc8b03d295e..b5cc4395abd 100644 --- a/packages/app/src/app/pages/Dashboard/Content/DragLayer/SelectedSandboxItems/elements.ts +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/elements.ts @@ -1,11 +1,13 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; export const Container = styled.div` - height: 210px; - width: 346px; + ${({ theme }) => css` + height: 210px; + width: 346px; - background-color: ${props => props.theme.background}; - border-radius: 2px; + background-color: ${theme.background}; + border-radius: 2px; + `}; `; export const SandboxImageContainer = styled.div` diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/index.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/index.tsx new file mode 100644 index 00000000000..e0e8f1ebaf5 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/SelectedSandboxItems/index.tsx @@ -0,0 +1,41 @@ +import React, { FunctionComponent } from 'react'; +import { useOvermind } from 'app/overmind'; + +import { AnimatedSandboxItem } from './AnimatedSandboxItem'; + +type Props = { + id: string; + isOverPossibleTargets: boolean; + x: number; + y: number; +}; +export const SelectedSandboxItems: FunctionComponent = ({ + id, + isOverPossibleTargets, + x, + y, +}) => { + const { + state: { + dashboard: { selectedSandboxes }, + }, + } = useOvermind(); + const selectedIds = [id, ...selectedSandboxes.filter(b => b !== id)]; + + return ( + <> + {selectedIds.map((selectedId, index) => ( + + ))} + + ); +}; diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/elements.ts b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/elements.ts new file mode 100644 index 00000000000..41ae6a0bf47 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/elements.ts @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + position: fixed; + pointer-events: none; + z-index: 100; + left: 0; + top: 0; + width: 100%; + height: 100%; +`; diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/index.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/index.tsx new file mode 100644 index 00000000000..7930087a6bb --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/DragLayer/index.tsx @@ -0,0 +1,60 @@ +import React, { FunctionComponent } from 'react'; +import { + DragLayer as DNDDragLayer, + DragLayerCollector, + DragLayerMonitor, +} from 'react-dnd'; + +import { Container } from './elements'; +import { SelectedSandboxItems } from './SelectedSandboxItems'; + +type OwnProps = {}; +type Props = OwnProps & CollectedProps; +const DragLayerComponent: FunctionComponent = ({ + currentOffset, + isDragging, + isOverPossibleTargets, + item, + itemType, +}) => { + if (!isDragging || !currentOffset) { + return null; + } + + return ( + + {itemType === 'SANDBOX' ? ( + + ) : null} + + ); +}; + +const collect: DragLayerCollector = monitor => { + // @ts-ignore + const isOverPossibleTargets = monitor.getTargetIds().length > 0; + + return { + currentOffset: monitor.getSourceClientOffset(), + isDragging: monitor.isDragging(), + isOverPossibleTargets, + item: monitor.getItem(), + itemType: monitor.getItemType(), + }; +}; + +type CollectedProps = { + currentOffset: ReturnType; + isDragging: ReturnType; + isOverPossibleTargets: boolean; + item: ReturnType; + itemType: ReturnType; +}; +export const DragLayer = DNDDragLayer(collect)( + DragLayerComponent +); diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.js b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.js deleted file mode 100644 index a3512c22ef3..00000000000 --- a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.js +++ /dev/null @@ -1,64 +0,0 @@ -/** @flow */ -import * as React from 'react'; - -/** - * Default row renderer for Table. - */ -export default function defaultRowRenderer({ - className, - columns, - index, - key, - onRowClick, - onRowDoubleClick, - onRowMouseOut, - onRowMouseOver, - onRowRightClick, - selectSandboxes, - rowData, - style, - id, -}) { - const a11yProps = {}; - - a11yProps['aria-label'] = 'row'; - a11yProps.tabIndex = 0; - - if (onRowClick) { - a11yProps.onClick = event => onRowClick({ event, index, rowData }); - } - if (onRowDoubleClick) { - a11yProps.onDoubleClick = event => - onRowDoubleClick({ event, index, rowData }); - } - if (onRowMouseOut) { - a11yProps.onMouseOut = event => onRowMouseOut({ event, index, rowData }); - } - if (onRowMouseOver) { - a11yProps.onMouseOver = event => onRowMouseOver({ event, index, rowData }); - } - if (onRowRightClick) { - a11yProps.onContextMenu = event => - onRowRightClick({ event, index, rowData }); - } - - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - - selectSandboxes([id], { additive: e.metaKey, range: e.shiftKey }); - }} - className={className} - key={key} - role="row" - style={style} - id={id} - tabIndex="0" - > - {columns} -
- ); -} diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.tsx new file mode 100644 index 00000000000..add8a47ee7d --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Row.tsx @@ -0,0 +1,80 @@ +import React, { FunctionComponent, HTMLAttributes, MouseEvent } from 'react'; +import { TableRowProps } from 'react-virtualized'; + +const noop = () => undefined; + +type Props = { + id: string; + selectSandboxes: ( + ids: string[], + options: { additive: boolean; range: boolean } + ) => void; +} & Omit & + Pick, 'id'>; +export const Row: FunctionComponent = ({ + className, + columns, + id, + index, + key, + onRowClick, + onRowDoubleClick, + onRowMouseOut, + onRowMouseOver, + onRowRightClick, + rowData, + selectSandboxes, + style, +}) => { + const onMouseDown = ({ + metaKey, + shiftKey, + ...event + }: MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + + selectSandboxes([id], { + additive: metaKey, + range: shiftKey, + }); + }; + const a11yProps = { + 'aria-label': 'row', + onClick: onRowClick + ? (event: MouseEvent) => + onRowClick({ event, index, rowData }) + : noop, + onContextMenu: onRowRightClick + ? (event: MouseEvent) => + onRowRightClick({ event, index, rowData }) + : noop, + onDoubleClick: onRowDoubleClick + ? (event: MouseEvent) => + onRowDoubleClick({ event, index, rowData }) + : noop, + onMouseDown, + onMouseOut: onRowMouseOut + ? (event: MouseEvent) => + onRowMouseOut({ event, index, rowData }) + : noop, + onMouseOver: onRowMouseOver + ? (event: MouseEvent) => + onRowMouseOver({ event, index, rowData }) + : noop, + tabIndex: 0, + }; + + return ( +
+ {columns} +
+ ); +}; diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxCard/KebabIcon.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/KebabIcon.tsx similarity index 100% rename from packages/app/src/app/pages/Dashboard/Content/SandboxCard/KebabIcon.tsx rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/KebabIcon.tsx diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxCard/elements.ts b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/elements.ts similarity index 100% rename from packages/app/src/app/pages/Dashboard/Content/SandboxCard/elements.ts rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/elements.ts diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxCard/index.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/index.tsx similarity index 96% rename from packages/app/src/app/pages/Dashboard/Content/SandboxCard/index.tsx rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/index.tsx index 5d155323ae3..8f5981cf4c2 100644 --- a/packages/app/src/app/pages/Dashboard/Content/SandboxCard/index.tsx +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/SandboxCard/index.tsx @@ -1,24 +1,28 @@ -// @ts-check /* eslint-disable react/prefer-stateless-function */ -import React from 'react'; -import history from 'app/utils/history'; +import Input from '@codesandbox/common/lib/components/Input'; +import getTemplate, { TemplateType } from '@codesandbox/common/lib/templates'; +import theme from '@codesandbox/common/lib/theme'; +import track from '@codesandbox/common/lib/utils/analytics'; +import { ESC, ENTER } from '@codesandbox/common/lib/utils/keycodes'; import { sandboxUrl } from '@codesandbox/common/lib/utils/url-generator'; +import React, { + CSSProperties, + FocusEvent, + KeyboardEvent, + MouseEvent, + PureComponent, +} from 'react'; +import { Mutation } from 'react-apollo'; import { DragSource } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; -import { Mutation } from 'react-apollo'; import TrashIcon from 'react-icons/lib/md/delete'; - import Unlisted from 'react-icons/lib/md/insert-link'; import Private from 'react-icons/lib/md/lock'; -import Input from '@codesandbox/common/lib/components/Input'; -import getTemplate, { TemplateType } from '@codesandbox/common/lib/templates'; -import theme from '@codesandbox/common/lib/theme'; -import track from '@codesandbox/common/lib/utils/analytics'; - -import { ESC, ENTER } from '@codesandbox/common/lib/utils/keycodes'; import { SandboxFragment } from 'app/graphql/types'; -import { RENAME_SANDBOX_MUTATION } from '../../queries'; +import history from 'app/utils/history'; + +import { RENAME_SANDBOX_MUTATION } from '../../../queries'; import { Container, @@ -56,7 +60,7 @@ type Props = { isPatron: boolean; isScrolling: () => boolean; removedAt?: string; - style?: React.CSSProperties; + style?: CSSProperties; alias: string | undefined; setSandboxesPrivacy: (privacy: 0 | 1 | 2) => void; @@ -93,7 +97,7 @@ const copyToClipboard = (str: string) => { document.body.removeChild(el); }; -class SandboxItemComponent extends React.PureComponent { +class SandboxItemComponent extends PureComponent { el: HTMLDivElement; screenshotTimeout: number; @@ -379,7 +383,7 @@ class SandboxItemComponent extends React.PureComponent { ].filter(Boolean); }; - selectSandbox = (e: React.MouseEvent | React.FocusEvent) => { + selectSandbox = (e: MouseEvent | FocusEvent) => { this.props.setSandboxesSelected([this.props.id], { additive: 'metaKey' in e ? e.metaKey : false, range: 'shiftKey' in e ? e.shiftKey : false, @@ -410,7 +414,7 @@ class SandboxItemComponent extends React.PureComponent { return true; }; - handleMouseDown = (e: React.MouseEvent) => { + handleMouseDown = (e: MouseEvent) => { e.stopPropagation(); if (!this.props.selected || e.metaKey) { @@ -418,7 +422,7 @@ class SandboxItemComponent extends React.PureComponent { } }; - handleKeyDown = (e: React.KeyboardEvent) => { + handleKeyDown = (e: KeyboardEvent) => { if (e.keyCode === ENTER) { track('Dashboard - Sandbox Opened With Enter'); // enter @@ -426,20 +430,20 @@ class SandboxItemComponent extends React.PureComponent { } }; - handleOnContextMenu = (e: React.MouseEvent) => { + handleOnContextMenu = (e: MouseEvent) => { track('Dashboard - Sandbox Context Menu Opened'); if (!this.props.selected) { this.selectSandbox(e); } }; - handleOnFocus = (e: React.FocusEvent) => { + handleOnFocus = (e: FocusEvent) => { if (!this.props.selected) { this.selectSandbox(e); } }; - handleOnBlur = (e: React.FocusEvent) => { + handleOnBlur = (e: FocusEvent) => { if (this.props.selected && e.bubbles) { this.props.setSandboxesSelected([]); } @@ -673,7 +677,7 @@ const cardSource = { if (result && result[MAKE_TEMPLATE_DROP_KEY]) { track('Template - Created', { source: 'Dragging', - team: !!result.teamId, + team: Boolean(result.teamId), }); props.makeTemplates(result.teamId); } @@ -683,12 +687,10 @@ const cardSource = { /** * Specifies the props to inject into your component. */ -function collect(connect) { - return { - connectDragSource: connect.dragSource(), - connectDragPreview: connect.dragPreview(), - }; -} +const collect = connect => ({ + connectDragSource: connect.dragSource(), + connectDragPreview: connect.dragPreview(), +}); export const SandboxItem = DragSource( 'SANDBOX', diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/elements.ts b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/elements.ts new file mode 100644 index 00000000000..aefd31a8d56 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/elements.ts @@ -0,0 +1,12 @@ +import styled, { css } from 'styled-components'; + +import { animated } from 'react-spring/renderprops'; + +export const Container = styled(animated.div)` + ${({ theme }) => css` + position: fixed; + border: 1px solid ${theme.secondary}; + background-color: ${theme.secondary.clearer(0.5)}; + pointer-events: none; + `}; +`; diff --git a/packages/app/src/app/pages/Dashboard/Content/Selection/index.js b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/index.tsx similarity index 52% rename from packages/app/src/app/pages/Dashboard/Content/Selection/index.js rename to packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/index.tsx index 2465a86ff42..e2a203bd153 100644 --- a/packages/app/src/app/pages/Dashboard/Content/Selection/index.js +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/Selection/index.tsx @@ -1,16 +1,14 @@ -import React from 'react'; -import styled from 'styled-components'; +import React, { FunctionComponent } from 'react'; +import { Spring } from 'react-spring/renderprops'; -import { Spring, animated } from 'react-spring/renderprops'; +import { Container } from './elements'; -const Container = styled(animated.div)` - position: fixed; - border: 1px solid ${props => props.theme.secondary}; - background-color: ${props => props.theme.secondary.clearer(0.5)}; - pointer-events: none; -`; - -export function getBounds(startX, startY, endX, endY) { +export const getBounds = ( + startX: number, + startY: number, + endX: number, + endY: number +) => { const top = startY > endY ? endY : startY; const left = startX > endX ? endX : startX; const width = startX > endX ? startX - endX : endX - startX; @@ -22,9 +20,20 @@ export function getBounds(startX, startY, endX, endY) { left, width, }; -} +}; -export const Selection = ({ startX, startY, endX, endY }) => { +type Props = { + endX: number; + endY: number; + startX: number; + startY: number; +}; +export const Selection: FunctionComponent = ({ + endX, + endY, + startX, + startY, +}) => { const { top, height, left, width } = getBounds(startX, startY, endX, endY); return ( diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.js b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.js deleted file mode 100644 index 6da4e189a99..00000000000 --- a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.js +++ /dev/null @@ -1,29 +0,0 @@ -import styled, { css } from 'styled-components'; -import Row from './Row'; - -export const StyledRow = styled(Row)` - font-weight: 600; - user-select: none; - - ${props => - props.selected && - css` - background-color: ${props.theme.secondary.clearer(0.9)}; - color: ${props.theme.secondary}; - `}; - &:focus { - outline: none; - } -`; - -export const Content = styled.div` - width: 100%; - height: calc(100% - 23px); - padding-top: 2rem; - box-sizing: border-box; - - @media (max-width: 768px) { - width: calc(100% - 30px); - margin-left: 30px; - } -`; diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.ts b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.ts new file mode 100644 index 00000000000..3bd461550c9 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/elements.ts @@ -0,0 +1,32 @@ +import styled, { css } from 'styled-components'; + +import { Row as RowBase } from './Row'; + +export const Row = styled(RowBase)<{ selected: boolean }>` + ${({ selected, theme }) => css` + font-weight: 600; + user-select: none; + + ${selected && + css` + background-color: ${theme.secondary.clearer(0.9)}; + color: ${theme.secondary}; + `}; + + &:focus { + outline: none; + } + `}; +`; + +export const Content = styled.div` + width: 100%; + height: calc(100% - 23px); + padding-top: 2rem; + box-sizing: border-box; + + @media (max-width: 768px) { + width: calc(100% - 30px); + margin-left: 30px; + } +`; diff --git a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/index.tsx b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/index.tsx index 7a767d4d06e..878a866a9f4 100644 --- a/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/index.tsx +++ b/packages/app/src/app/pages/Dashboard/Content/SandboxGrid/index.tsx @@ -1,35 +1,33 @@ -import 'react-virtualized/styles.css'; - import { basename } from 'path'; +import { Sandbox } from '@codesandbox/common/lib/types'; import track from '@codesandbox/common/lib/utils/analytics'; import { getSandboxName } from '@codesandbox/common/lib/utils/get-sandbox-name'; import { protocolAndHost } from '@codesandbox/common/lib/utils/url-generator'; -import { makeTemplates } from 'app/components/CreateNewSandbox/queries'; -import downloadZip from 'app/overmind/effects/zip/create-zip'; import { formatDistanceToNow } from 'date-fns'; import { zonedTimeToUtc } from 'date-fns-tz'; import { camelizeKeys } from 'humps'; import { uniq } from 'lodash-es'; import React from 'react'; -import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer'; -import Grid from 'react-virtualized/dist/commonjs/Grid'; -import Table from 'react-virtualized/dist/commonjs/Table'; -import Column from 'react-virtualized/dist/commonjs/Table/Column'; +import { AutoSizer, Column, Grid, Table } from 'react-virtualized'; +import 'react-virtualized/styles.css'; +import { makeTemplates } from 'app/components/CreateNewSandbox/queries'; import { SandboxFragment } from 'app/graphql/types'; -import { Sandbox } from '@codesandbox/common/lib/types'; +import downloadZip from 'app/overmind/effects/zip/create-zip'; + import { deleteSandboxes, permanentlyDeleteSandboxes, setSandboxesPrivacy, undeleteSandboxes, } from '../../queries'; -import { DragLayer } from '../DragLayer'; -import { SandboxItem } from '../SandboxCard'; -import { PADDING } from '../SandboxCard/elements'; -import { Selection, getBounds } from '../Selection'; -import { Content, StyledRow } from './elements'; + +import { DragLayer } from './DragLayer'; +import { Content, Row } from './elements'; +import { SandboxItem } from './SandboxCard'; +import { PADDING } from './SandboxCard/elements'; +import { Selection, getBounds } from './Selection'; type State = { selection: @@ -416,9 +414,9 @@ class SandboxGridComponent extends React.Component< }; rowRenderer = props => { - const selected = this.selectedSandboxesObject[props.rowData.id]; + const selected = Boolean(this.selectedSandboxesObject[props.rowData.id]); return ( - - props.theme.light ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)'}; + ${({ theme }) => css` + font-size: 1rem; + width: 100%; + font-weight: 600; + color: ${theme.light ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)'}; - line-height: 1.6; + line-height: 1.6; - @media (max-width: 768px) { - margin-left: 1rem; - } + @media (max-width: 768px) { + margin-left: 1rem; + } + `}; `; export const HeaderTitle = styled.div` diff --git a/packages/app/src/app/pages/Dashboard/Content/index.js b/packages/app/src/app/pages/Dashboard/Content/index.js deleted file mode 100644 index b095d36ee0e..00000000000 --- a/packages/app/src/app/pages/Dashboard/Content/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { Route, Switch, Redirect, withRouter } from 'react-router-dom'; - -import codesandbox from '@codesandbox/components/lib/themes/codesandbox.json'; -import { ThemeProvider } from '@codesandbox/components'; - -import { RecentSandboxes } from './routes/RecentSandboxes'; -import PathedSandboxes from './routes/PathedSandboxes'; -import { Templates } from './routes/Templates'; -import DeletedSandboxes from './routes/DeletedSandboxes'; -import SearchSandboxes from './routes/SearchSandboxes'; -import CreateTeam from './routes/CreateTeam'; -import TeamView from './routes/TeamView'; - -const Content = () => ( - - - - - - - - - - - - - - -); - -export default withRouter(Content); diff --git a/packages/app/src/app/pages/Dashboard/Content/index.tsx b/packages/app/src/app/pages/Dashboard/Content/index.tsx new file mode 100644 index 00000000000..b230da418c0 --- /dev/null +++ b/packages/app/src/app/pages/Dashboard/Content/index.tsx @@ -0,0 +1,46 @@ +import React, { FunctionComponent } from 'react'; +import { Route, Switch, Redirect } from 'react-router-dom'; + +import codesandbox from '@codesandbox/components/lib/themes/codesandbox.json'; +import { ThemeProvider } from '@codesandbox/components'; + +import CreateTeam from './routes/CreateTeam'; +import DeletedSandboxes from './routes/DeletedSandboxes'; +import PathedSandboxes from './routes/PathedSandboxes'; +import { RecentSandboxes } from './routes/RecentSandboxes'; +import SearchSandboxes from './routes/SearchSandboxes'; +import TeamView from './routes/TeamView'; +import { Templates } from './routes/Templates'; + +export const Content: FunctionComponent = () => ( + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx index 9cdde2f87ff..52b50da059c 100644 --- a/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx +++ b/packages/app/src/app/pages/Dashboard/Sidebar/TemplateItem.tsx @@ -7,7 +7,7 @@ import { DropTargetSpec, } from 'react-dnd'; -import { MAKE_TEMPLATE_DROP_KEY } from '../Content/SandboxCard'; +import { MAKE_TEMPLATE_DROP_KEY } from '../Content/SandboxGrid/SandboxCard'; import { Item } from './Item'; diff --git a/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx b/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx index 2644bb62611..779e415eb70 100644 --- a/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx +++ b/packages/app/src/app/pages/Dashboard/Sidebar/TrashItem.tsx @@ -8,7 +8,7 @@ import { } from 'react-dnd'; import TrashIcon from 'react-icons/lib/md/delete'; -import { DELETE_SANDBOX_DROP_KEY } from '../Content/SandboxCard'; +import { DELETE_SANDBOX_DROP_KEY } from '../Content/SandboxGrid/SandboxCard'; import { Item } from './Item'; diff --git a/packages/app/src/app/pages/Dashboard/index.tsx b/packages/app/src/app/pages/Dashboard/index.tsx index 412ca3b9b55..432bfeccc5a 100644 --- a/packages/app/src/app/pages/Dashboard/index.tsx +++ b/packages/app/src/app/pages/Dashboard/index.tsx @@ -6,7 +6,7 @@ import { client } from 'app/graphql/client'; import { useOvermind } from 'app/overmind'; import { Navigation } from 'app/pages/common/Navigation'; -import Content from './Content'; +import { Content } from './Content'; import { Container, ContentContainer, diff --git a/yarn.lock b/yarn.lock index 8235b5b9067..917c2cb568e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4766,6 +4766,14 @@ dependencies: "@types/react" "*" +"@types/react-virtualized@^9.21.8": + version "9.21.8" + resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.8.tgz#dc0150a75fd6e42f33729886463ece04d03367ea" + integrity sha512-7fZoA0Azd2jLIE9XC37fMZgMqaJe3o3pfzGjvrzphoKjBCdT4oNl6wikvo4dDMESDnpkZ8DvVTc7aSe4DW86Ew== + dependencies: + "@types/prop-types" "*" + "@types/react" "*" + "@types/react@*", "@types/react@^16.8.12", "@types/react@^16.8.6", "@types/react@^16.9.17": version "16.9.17" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.17.tgz#58f0cc0e9ec2425d1441dd7b623421a867aa253e" @@ -8597,7 +8605,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.3, classnames@^2.2.5: +classnames@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -8785,6 +8793,11 @@ clone@^2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= +clsx@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702" + integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA== + cnbuilder@^1.1.7: version "1.3.2" resolved "https://registry.yarnpkg.com/cnbuilder/-/cnbuilder-1.3.2.tgz#f8ade3986543e6736fc2c8a304d74a244c36d541" @@ -10816,10 +10829,10 @@ disparity@^2.0.0: ansi-styles "^2.0.1" diff "^1.3.2" -dnd-core@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-9.4.0.tgz#ccf605d36887f18cdde8fd5576ca3145d2e69fa8" - integrity sha512-Kg+8VwU8s7TgdR/BUYGUHrvFiS+5ePMZ0Q0XD7p+cFVJvgKqykBaeQDuaziuauFMPm8QxtnUy8Pncey9flXW3Q== +dnd-core@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-9.5.1.tgz#e9ec02d33529b68fa528865704d40ac4b14f2baf" + integrity sha512-/yEWFF2jg51yyB8uA2UbvBr9Qis0Oo/4p9cqHLEKZdxzHHVSPfq0a/ool8NG6dIS6Q4uN+oKGObY0rNWiopJDA== dependencies: "@types/asap" "^2.0.0" "@types/invariant" "^2.2.30" @@ -10893,13 +10906,21 @@ dom-converter@^0.2: dependencies: utila "~0.4" -"dom-helpers@^2.4.0 || ^3.0.0", dom-helpers@^3.2.0, dom-helpers@^3.2.1: +dom-helpers@^3.2.0, dom-helpers@^3.2.1: version "3.4.0" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== dependencies: "@babel/runtime" "^7.1.2" +dom-helpers@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821" + integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw== + dependencies: + "@babel/runtime" "^7.6.3" + csstype "^2.6.7" + dom-helpers@^5.0.1: version "5.1.4" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b" @@ -25044,21 +25065,21 @@ react-devtools-inline@^4.4.0: dependencies: es6-symbol "^3" -react-dnd-html5-backend@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-9.4.0.tgz#5b1d192f57d103298657cde1fe0eabdbf2726311" - integrity sha512-gehPwLp505F6RoFkQiDX7Q4mbpbyfyT0TbIoZop/m4vkBw6yUE/QLrnxBQdNpDPSwL/9XkZxxd/PrbeMCQ+WrQ== +react-dnd-html5-backend@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-9.5.1.tgz#e6a0aed3ece800c1abe004f9ed9991513e2e644c" + integrity sha512-wUdzjREwLqHxFkA6E+XDVL5IFjRDbBI3SHVKil9n3qrGT5dm2tA2oi1aIALdfMKsu00c+OXA9lz/LuKZCE9KXg== dependencies: - dnd-core "^9.4.0" + dnd-core "^9.5.1" -react-dnd@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-9.4.0.tgz#eec87035c6360fb33a44932326b3369af011a41c" - integrity sha512-jnLF8qKowCKTqSddfCiLx5+sb+HxO1qgdiAgbBeL8yuo5tRYNtKxZYn7+wVwNoyZuWEuM1Gw/Wsdhr+yb2RELQ== +react-dnd@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-9.5.1.tgz#907e55c791d6c50cbed1a4021c14b989b86ac467" + integrity sha512-j2MvziPNLsxXkb3kIJzLvvOv/TQ4sysp6U4CmxAXd4C884dXm/9UGdB7K1wkTW3ZxVpI1K7XhKbX0JgNlPfLcA== dependencies: "@types/hoist-non-react-statics" "^3.3.1" "@types/shallowequal" "^1.1.1" - dnd-core "^9.4.0" + dnd-core "^9.5.1" hoist-non-react-statics "^3.3.0" shallowequal "^1.1.0" @@ -25677,13 +25698,14 @@ react-use@^9.7.2: throttle-debounce "^2.0.1" ts-easing "^0.2.0" -react-virtualized@^9.19.1: - version "9.19.1" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.19.1.tgz#84b53253df2d9df61c85ce037141edccc70a73fd" +react-virtualized@^9.21.2: + version "9.21.2" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.21.2.tgz#02e6df65c1e020c8dbf574ec4ce971652afca84e" + integrity sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA== dependencies: babel-runtime "^6.26.0" - classnames "^2.2.3" - dom-helpers "^2.4.0 || ^3.0.0" + clsx "^1.0.1" + dom-helpers "^5.0.0" loose-envify "^1.3.0" prop-types "^15.6.0" react-lifecycles-compat "^3.0.4"