Skip to content

Commit

Permalink
Add responsive style #9
Browse files Browse the repository at this point in the history
  • Loading branch information
Godsenal committed Jul 23, 2019
1 parent 87f2a7c commit a6ba59c
Show file tree
Hide file tree
Showing 31 changed files with 516 additions and 233 deletions.
63 changes: 33 additions & 30 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ProtectedRoute,
PrevLocation,
LoadingBar,
ViewportChecker,
} from './components';
import { notifier } from './utils/renoti';
import { IRootState } from './reducers';
Expand All @@ -35,36 +36,38 @@ class App extends Component<IAppProps> {
}
return (
<Router history={history}>
<Header />
<PrevLocation>
<ScrollChecker history={history}>
<Suspense fallback={<LoadingBar />}>
<PageLayout>
<Switch>
<Route exact path="/" component={Home} />
<ProtectedRoute exact path="/ask" component={Ask} />
<ProtectedRoute
exact
path="/question/:questionId/edit"
component={QuestionEdit}
/>
<Route
exact
path="/question/:questionId"
component={Question}
/>
<ProtectedRoute
path="/profile/:userId/edit"
component={ProfileEdit}
/>
<Route path="/profile/:userId" component={Profile} />
<Route path="/search" component={Search} />
<Route path="/tagged/:tag" component={TagSearch} />
</Switch>
</PageLayout>
</Suspense>
</ScrollChecker>
</PrevLocation>
<ViewportChecker>
<Header />
<PrevLocation>
<ScrollChecker history={history}>
<Suspense fallback={<LoadingBar />}>
<PageLayout>
<Switch>
<Route exact path="/" component={Home} />
<ProtectedRoute exact path="/ask" component={Ask} />
<ProtectedRoute
exact
path="/question/:questionId/edit"
component={QuestionEdit}
/>
<Route
exact
path="/question/:questionId"
component={Question}
/>
<ProtectedRoute
path="/profile/:userId/edit"
component={ProfileEdit}
/>
<Route path="/profile/:userId" component={Profile} />
<Route path="/search" component={Search} />
<Route path="/tagged/:tag" component={TagSearch} />
</Switch>
</PageLayout>
</Suspense>
</ScrollChecker>
</PrevLocation>
</ViewportChecker>
<Modal />
<NotiPortal notifier={notifier} />
</Router>
Expand Down
6 changes: 3 additions & 3 deletions src/components/LikeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Button = styled.button`
`;

const LikeOrDislike = styled(Button) <{ likeType: LikeType; active?: boolean }>`
font-size: 1em;
font-size: 1.2rem;
${props => props.active && `color: ${COLOR[props.likeType].active};`}
&:hover {
color: ${props => COLOR[props.likeType].hover};
Expand All @@ -46,9 +46,9 @@ const LikeButton: React.SFC<ILikeButtonProps> = ({
return (
<LikeOrDislike likeType={likeType} active={active} {...buttonProps}>
{likeType === 'like' ? (
<MdThumbUp size={24} />
<MdThumbUp fontSize="inherit" />
) : (
<MdThumbDown size={24} />
<MdThumbDown fontSize="inherit" />
)}
{typeof count !== 'undefined' && <div>{count}</div>}
</LikeOrDislike>
Expand Down
53 changes: 35 additions & 18 deletions src/components/ProfileMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React, { useState, useCallback } from 'react';
import Button from '@material-ui/core/Button';
import MenuList from '@material-ui/core/MenuList';
import MenuItem from '@material-ui/core/MenuItem';
import styled from 'styled-components';
import Popper from '@material-ui/core/Popper';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Paper from '@material-ui/core/Paper';
import { ProfilePhoto } from '.';
import { Divider, ProfilePhoto } from '.';
import { IUser } from '../models/user';
import { history } from '../utils/history';
import { MenuList, MenuItem } from '../styles/common';

export interface IProfileMenuProps extends IUser {
logout: () => void;
}
const ProfileMenu: React.SFC<IProfileMenuProps> = ({ id, image, logout }) => {

const UserInfo = styled.div`
display: flex;
align-items: center;
div {
margin-left: 10px;
}
`

const ProfileMenu: React.SFC<IProfileMenuProps> = ({ id, nickname, image, logout }) => {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(e.currentTarget);
Expand All @@ -22,26 +31,34 @@ const ProfileMenu: React.SFC<IProfileMenuProps> = ({ id, image, logout }) => {
}, []);

// Menu 액션
const handleProfile = useCallback(() => {
const withClose = <T extends Function>(fn: T) => {
return () => {
handleClose();
fn();
}
};
const handlePost = withClose(() => {
history.push(`/ask`);
});
const handleProfile = withClose(() => {
history.push(`/profile/${id}`);
}, [id]);
const handleLogout = useCallback(() => {
handleClose();
logout();
}, [handleClose, logout]);
});
const handleLogout = withClose(logout);
return (
<>
<Button
aria-owns={anchorEl ? 'simple-menu' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<ProfilePhoto src={image} />
</Button>
<button onClick={handleClick}><ProfilePhoto src={image} /></button>
<Popper id="simple-menu" anchorEl={anchorEl} open={Boolean(anchorEl)}>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList>
<MenuItem onClick={handleProfile}>
<UserInfo>
<ProfilePhoto src={image} width={40} />
<div>{nickname}</div>
</UserInfo>
</MenuItem>
<MenuItem onClick={handlePost}>코드 올리기</MenuItem>
<MenuItem isDivider><Divider /></MenuItem>
<MenuItem onClick={handleProfile}>프로필 보기</MenuItem>
<MenuItem onClick={handleLogout}>로그아웃</MenuItem>
</MenuList>
Expand Down
6 changes: 3 additions & 3 deletions src/components/ProfilePhoto.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo } from 'react';
import styled from 'styled-components';
import profilephoto from '../assets/ic_profilephoto.png';
import { Link } from 'react-router-dom';
Expand All @@ -12,7 +12,7 @@ interface ProfilePhotoProps extends React.ImgHTMLAttributes<HTMLImageElement> {
}

const ProfilePhoto: React.SFC<ProfilePhotoProps> = ({
width = 40,
width = 32,
src = profilephoto,
userId,
...props
Expand Down Expand Up @@ -44,4 +44,4 @@ const ProfilePhoto: React.SFC<ProfilePhotoProps> = ({
);
};

export default ProfilePhoto;
export default memo(ProfilePhoto);
58 changes: 17 additions & 41 deletions src/components/Search.tsx → src/components/Search/Desktop.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import React, { useState, useCallback, useEffect } from 'react';
import React, { useState } from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import Paper, { PaperProps } from '@material-ui/core/Paper';
import Paper from '@material-ui/core/Paper';
import InputBase, { InputBaseProps } from '@material-ui/core/InputBase';
import { MdSearch } from 'react-icons/md';
import { history, questionQueryHelper } from '../utils/history';
import { RouteComponentProps, withRouter } from 'react-router';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
padding: '2px 4px',
width: '100%',
fontSize: '1rem',
alignItems: 'center',
boxSizing: 'border-box',
boxShadow: '0 0 0 0',
},
input: {
fontSize: 'inherit',
borderRadius: 5,
padding: '5px 10px',
padding: '0.25rem 0.75rem',
paddingLeft: 35,
width: '100%',
transition: 'background-color 0.1s ease-in-out',
Expand All @@ -38,66 +38,42 @@ const useStyles = makeStyles((theme: Theme) =>
},
icon: {
position: 'absolute',
top: 8,
top: '50%',
left: 5,
transform: 'translateY(-55%)',
color: theme.palette.grey[500],
zIndex: 2,
},
}),
);

interface IInputProps extends RouteComponentProps {
paperProps?: PaperProps;
inputProps?: InputBaseProps;
type IDesktopSearchProps = InputBaseProps & {
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
}
const Search: React.SFC<IInputProps> = ({
location,
paperProps = {},
inputProps = {},
const DesktopSearch: React.SFC<IDesktopSearchProps> = ({
handleSubmit,
...props
}) => {
const classes = useStyles();
const [focused, setFocused] = useState(false);
const [search, setSearch] = useState(
questionQueryHelper.searchQuery.subject || '',
);

const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value),
[],
);
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
history.push(
`/search?${questionQueryHelper.makeQuery({ subject: search })}`,
);
};

useEffect(() => {
setSearch(questionQueryHelper.searchQuery.subject || '');
}, [location.search]);

return (
<Paper
className={classes.root}
elevation={focused ? 4 : 1}
square
{...paperProps}
>
<form className={classes.form} onSubmit={handleSearch}>
<MdSearch size={25} className={classes.icon} />
<form className={classes.form} onSubmit={handleSubmit}>
<MdSearch size={20} className={classes.icon} />
<InputBase
fullWidth
value={search}
onChange={handleChange}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
className={`${classes.input} ${focused && classes.focusedinput}`}
placeholder="질문을 검색해주세요."
{...inputProps}
{...props}
/>
</form>
</Paper>
);
};

export default withRouter(Search);
export default DesktopSearch;
81 changes: 81 additions & 0 deletions src/components/Search/Mobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useState, useCallback } from 'react';
import styled from 'styled-components';
import TextField, { StandardTextFieldProps } from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import { MdSearch } from 'react-icons/md';

const FixedContainer = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.9);
z-index: 999;
`
const InnerContainer = styled.div`
position: relative;
box-sizing: border-box;
top: 40%;
width: 100%;
max-width: 800px;
margin: auto;
padding: 0 10px;
`

const Form = styled.form`
width: 100%;
margin-bottom: 5px;
input {
font-size: 125%;
}
`;

const FloatedButton = styled(Button)`
float: right;
`
const Icon = styled(MdSearch)`
color: ${props => props.theme.palette.darkGray};
cursor: pointer;
&:hover {
color: black;
transition: color 0.1s;
}
`

interface IMobileSearchProps extends StandardTextFieldProps {
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void
}

const MobileSearch: React.SFC<IMobileSearchProps> = ({ handleSubmit, ...props }) => {
const [open, setOpen] = useState(false);
const close = useCallback(() => {
setOpen(false);
}, []);
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
e.persist();
setOpen(false);
handleSubmit(e);
}
return (
<>
<Icon role="button" size={24} onClick={() => setOpen(true)} />
{
open && (
<FixedContainer>
<InnerContainer>
<Form onSubmit={onSubmit}>
<TextField autoFocus placeholder="질문을 검색해주세요." fullWidth {...props} />
</Form>
<FloatedButton onClick={close} variant="outlined">닫기</FloatedButton>
</InnerContainer>
</FixedContainer>
)
}

</>
)
}

export default MobileSearch;
Loading

0 comments on commit a6ba59c

Please sign in to comment.