Skip to content

Centralised Breakpoints using useIsMobile hook #3042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions client/components/Dropdown/TableDropdown.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import React from 'react';
import { useMediaQuery } from 'react-responsive';
import styled from 'styled-components';
import { prop, remSize } from '../../theme';
import DropdownMenu from './DropdownMenu';

import DownFilledTriangleIcon from '../../images/down-filled-triangle.svg';
import MoreIconSvg from '../../images/more.svg';
import useIsMobile from '../../modules/IDE/hooks/useIsMobile';

const DotsHorizontal = styled(MoreIconSvg)`
transform: rotate(90deg);
`;

const TableDropdownIcon = () => {
// TODO: centralize breakpoints
const isMobile = useMediaQuery({ maxWidth: 770 });

const isMobile = useIsMobile();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at these lines you can see why this PR is so important! The other components with MediaQuery are using minWidth={770}. I wrote the code on the left and used maxWidth: 770. But it should have been maxWidth: 669 so that it looks right when the screen is exactly 770px wide.

return isMobile ? (
<DotsHorizontal focusable="false" aria-hidden="true" />
) : (
Expand Down
28 changes: 13 additions & 15 deletions client/modules/IDE/components/Header/Nav.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { sortBy } from 'lodash';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import MediaQuery from 'react-responsive';
import NavDropdownMenu from '../../../../components/Nav/NavDropdownMenu';
import NavMenuItem from '../../../../components/Nav/NavMenuItem';
import { availableLanguages, languageKeyToLabel } from '../../../../i18n';
Expand All @@ -30,21 +29,20 @@ import {
import { logoutUser } from '../../../User/actions';
import { CmControllerContext } from '../../pages/IDEView';
import MobileNav from './MobileNav';
import useIsMobile from '../../hooks/useIsMobile';

const Nav = ({ layout }) => (
<MediaQuery minWidth={770}>
{(matches) =>
matches ? (
<NavBar>
<LeftLayout layout={layout} />
<UserMenu />
</NavBar>
) : (
<MobileNav />
)
}
</MediaQuery>
);
const Nav = ({ layout }) => {
const isMobile = useIsMobile();

return isMobile ? (
<MobileNav />
) : (
<NavBar>
<LeftLayout layout={layout} />
<UserMenu />
</NavBar>
);
};

Nav.propTypes = {
layout: PropTypes.oneOf(['dashboard', 'project'])
Expand Down
20 changes: 7 additions & 13 deletions client/modules/IDE/components/Header/index.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import MediaQuery from 'react-responsive';
import useIsMobile from '../../hooks/useIsMobile';

import Nav from './Nav';
import Toolbar from './Toolbar';

const Header = (props) => {
const project = useSelector((state) => state.project);

const isMobile = useIsMobile();

return (
<header>
<Nav />
<MediaQuery minWidth={770}>
{(matches) => {
if (matches)
return (
<Toolbar
syncFileContent={props.syncFileContent}
key={project.id}
/>
);
return null;
}}
</MediaQuery>
{!isMobile && (
<Toolbar syncFileContent={props.syncFileContent} key={project.id} />
)}
Comment on lines +17 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much cleaner!

</header>
);
};
Expand Down
9 changes: 9 additions & 0 deletions client/modules/IDE/hooks/useIsMobile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useMediaQuery } from 'react-responsive';

const useIsMobile = (customBreakpoint) => {
const breakPoint = customBreakpoint || 770;
const isMobile = useMediaQuery({ maxWidth: breakPoint });
return isMobile;
};

export default useIsMobile;
197 changes: 97 additions & 100 deletions client/modules/IDE/pages/IDEView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Helmet } from 'react-helmet';
import SplitPane from 'react-split-pane';
import MediaQuery from 'react-responsive';
import IDEKeyHandlers from '../components/IDEKeyHandlers';
import Sidebar from '../components/Sidebar';
import PreviewFrame from '../components/PreviewFrame';
Expand All @@ -27,6 +26,7 @@ import {
PreviewWrapper
} from '../components/Editor/MobileEditor';
import IDEOverlays from '../components/IDEOverlays';
import useIsMobile from '../hooks/useIsMobile';

function getTitle(project) {
const { id } = project;
Expand Down Expand Up @@ -90,6 +90,7 @@ function WarnIfUnsavedChanges() {
export const CmControllerContext = React.createContext({});

const IDEView = () => {
const isMobile = useIsMobile();
const ide = useSelector((state) => state.ide);
const preferences = useSelector((state) => state.preferences);
const project = useSelector((state) => state.project);
Expand Down Expand Up @@ -175,115 +176,111 @@ const IDEView = () => {
<CmControllerContext.Provider value={cmRef}>
<Header syncFileContent={syncFileContent} />
</CmControllerContext.Provider>
<MediaQuery minWidth={770}>
{(matches) =>
matches ? (
<main className="editor-preview-container">
{isMobile ? (
<>
<FloatingActionButton
syncFileContent={syncFileContent}
offsetBottom={ide.isPlaying ? currentConsoleSize : 0}
/>
<PreviewWrapper show={ide.isPlaying}>
<SplitPane
style={{ position: 'static' }}
split="horizontal"
primary="second"
size={currentConsoleSize}
minSize={consoleCollapsedSize}
onChange={(size) => {
setConsoleSize(size);
setIsOverlayVisible(true);
}}
onDragFinished={() => {
setIsOverlayVisible(false);
}}
allowResize={ide.consoleIsExpanded}
className="editor-preview-subpanel"
>
<PreviewFrame
fullView
hide={!ide.isPlaying}
cmController={cmRef.current}
isOverlayVisible={isOverlayVisible}
/>
<Console />
</SplitPane>
</PreviewWrapper>
<EditorSidebarWrapper show={!ide.isPlaying}>
<Sidebar />
<Editor
provideController={(ctl) => {
cmRef.current = ctl;
}}
/>
</EditorSidebarWrapper>
</>
) : (
<main className="editor-preview-container">
<SplitPane
split="vertical"
size={ide.sidebarIsExpanded ? sidebarSize : 20}
onChange={(size) => {
setSidebarSize(size);
}}
allowResize={ide.sidebarIsExpanded}
minSize={150}
>
<Sidebar />
<SplitPane
split="vertical"
maxSize={MaxSize * 0.965}
defaultSize="50%"
onChange={() => {
setIsOverlayVisible(true);
}}
onDragFinished={() => {
setIsOverlayVisible(false);
}}
resizerStyle={{
borderLeftWidth: '2px',
borderRightWidth: '2px',
width: '2px',
margin: '0px 0px'
}}
>
<SplitPane
split="vertical"
size={ide.sidebarIsExpanded ? sidebarSize : 20}
split="horizontal"
primary="second"
size={currentConsoleSize}
minSize={consoleCollapsedSize}
onChange={(size) => {
setSidebarSize(size);
setConsoleSize(size);
}}
allowResize={ide.sidebarIsExpanded}
minSize={150}
allowResize={ide.consoleIsExpanded}
className="editor-preview-subpanel"
>
<Sidebar />
<SplitPane
split="vertical"
maxSize={MaxSize * 0.965}
defaultSize="50%"
onChange={() => {
setIsOverlayVisible(true);
}}
onDragFinished={() => {
setIsOverlayVisible(false);
}}
resizerStyle={{
borderLeftWidth: '2px',
borderRightWidth: '2px',
width: '2px',
margin: '0px 0px'
<Editor
provideController={(ctl) => {
cmRef.current = ctl;
}}
>
<SplitPane
split="horizontal"
primary="second"
size={currentConsoleSize}
minSize={consoleCollapsedSize}
onChange={(size) => {
setConsoleSize(size);
}}
allowResize={ide.consoleIsExpanded}
className="editor-preview-subpanel"
>
<Editor
provideController={(ctl) => {
cmRef.current = ctl;
}}
/>
<Console />
</SplitPane>
<section className="preview-frame-holder">
<header className="preview-frame__header">
<h2 className="preview-frame__title">
{t('Toolbar.Preview')}
</h2>
</header>
<div className="preview-frame__content">
<PreviewFrame
cmController={cmRef.current}
isOverlayVisible={isOverlayVisible}
/>
</div>
</section>
</SplitPane>
/>
<Console />
</SplitPane>
</main>
) : (
<>
<FloatingActionButton
syncFileContent={syncFileContent}
offsetBottom={ide.isPlaying ? currentConsoleSize : 0}
/>
<PreviewWrapper show={ide.isPlaying}>
<SplitPane
style={{ position: 'static' }}
split="horizontal"
primary="second"
size={currentConsoleSize}
minSize={consoleCollapsedSize}
onChange={(size) => {
setConsoleSize(size);
setIsOverlayVisible(true);
}}
onDragFinished={() => {
setIsOverlayVisible(false);
}}
allowResize={ide.consoleIsExpanded}
className="editor-preview-subpanel"
>
<section className="preview-frame-holder">
<header className="preview-frame__header">
<h2 className="preview-frame__title">
{t('Toolbar.Preview')}
</h2>
</header>
<div className="preview-frame__content">
<PreviewFrame
fullView
hide={!ide.isPlaying}
cmController={cmRef.current}
isOverlayVisible={isOverlayVisible}
/>
<Console />
</SplitPane>
</PreviewWrapper>
<EditorSidebarWrapper show={!ide.isPlaying}>
<Sidebar />
<Editor
provideController={(ctl) => {
cmRef.current = ctl;
}}
/>
</EditorSidebarWrapper>
</>
)
}
</MediaQuery>
</div>
</section>
</SplitPane>
</SplitPane>
</main>
)}
<IDEOverlays />
</RootPage>
);
Expand Down
Loading