-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #17 from xyzith/feature/profilePage
feat profile page
- Loading branch information
Showing
10 changed files
with
501 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; | ||
import { faCamera } from '@fortawesome/free-solid-svg-icons'; | ||
import './ImageInput.scss'; | ||
|
||
const ImageInput = ({ value }) => { | ||
|
||
const [tmpImage, setTmpImage ]= useState(''); | ||
|
||
const changeImage = async (ev) => { | ||
const { files } = ev?.target || {}; | ||
const dataUrl = await new Promise((resolve, reject) => { | ||
const reader = new FileReader(); | ||
reader.readAsDataURL(files[0]); | ||
reader.onload = () => resolve(reader.result); | ||
reader.onerror = (error) => reject(error); | ||
}); | ||
setTmpImage(dataUrl); | ||
}; | ||
|
||
const displayImg = tmpImage ? tmpImage : value; | ||
|
||
return ( | ||
<div className="image-input"> | ||
<div className="image-wrapper"> | ||
<img src={displayImg} /> | ||
<div className="camera-tip"> | ||
<FontAwesomeIcon icon={faCamera} /> | ||
</div> | ||
</div> | ||
<input type="file" onChange={changeImage} /> | ||
</div> | ||
); | ||
}; | ||
|
||
ImageInput.propTypes = { | ||
value: PropTypes.string.isRequired, | ||
}; | ||
|
||
export default ImageInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
.image-input { | ||
display: flex; | ||
width: 100%; | ||
align-items: center; | ||
justify-content: center; | ||
|
||
.image-wrapper { | ||
position: relative; | ||
width: 240rem; | ||
height: 240rem; | ||
border-radius: 50%; | ||
overflow: hidden; | ||
} | ||
|
||
.camera-tip { | ||
position: absolute; | ||
bottom: 0; | ||
background: #00000066; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
width: 100%; | ||
height: 40%; | ||
transition: transform .3s; | ||
transform: translateY(100%); | ||
svg { | ||
width: 40rem; | ||
height: 40rem; | ||
path{ | ||
fill: white; | ||
} | ||
} | ||
} | ||
|
||
&:hover .camera-tip { | ||
transform: translateY(0); | ||
} | ||
|
||
input[type="file"] { | ||
display: none; | ||
} | ||
|
||
img { | ||
width: 100%; | ||
height: 100%; | ||
object-fit: cover; | ||
} | ||
} | ||
|
||
@media (orientation: portrait) { | ||
.image-input .image-wrapper{ | ||
width: 80vw; | ||
height: 80vw; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
.profile-page { | ||
padding-top: 120rem; | ||
margin: auto; | ||
width: 90vw; | ||
max-width: 800rem; | ||
font-size: 28rem; | ||
|
||
label { | ||
width: 100%; | ||
margin: 20rem auto; | ||
display: flex; | ||
align-items: center; | ||
span { | ||
width: 300rem; | ||
} | ||
|
||
input { | ||
flex: 1; | ||
padding: 0 8rem; | ||
box-sizing: border-box; | ||
border-radius: 8rem; | ||
background-color: transparent; | ||
border: 1px solid transparent; | ||
|
||
&:focus { | ||
outline-width: 0; | ||
} | ||
|
||
&:not(:read-only) { | ||
border-color: #052745; | ||
} | ||
} | ||
} | ||
} | ||
|
||
@media (orientation: portrait) { | ||
.profile-page { | ||
label { | ||
flex-wrap: wrap; | ||
span { | ||
basis-width: 100%; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import classNames from 'classnames'; | ||
import PropTypes from 'prop-types'; | ||
import './SwitchInput.scss'; | ||
|
||
const SwitchInput = ({ value, onClick }) => { | ||
const className = classNames('switch-input', { enabled: value }); | ||
|
||
return ( | ||
<div className={className} onClick={onClick} /> | ||
); | ||
}; | ||
|
||
SwitchInput.propTypes = { | ||
value: PropTypes.bool.isRequired, | ||
onClick: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default SwitchInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.switch-input { | ||
position: relative; | ||
display: inline-block; | ||
box-sizing: border-box; | ||
width: 60rem; | ||
height: 30rem; | ||
background-color: #ff5a67; | ||
vertical-align: middle; | ||
border-radius: 15rem; | ||
transition: background-color .3s; | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
display: block; | ||
width: 30rem; | ||
height: 30rem; | ||
background-color: white; | ||
box-shadow: 2rem 2rem 4rem #dbdbdb; | ||
border-radius: 15px; | ||
transition: left .3s; | ||
left: calc(100% - 30rem); | ||
} | ||
|
||
&.enabled { | ||
background-color: #aae88f; | ||
&::after { | ||
left: 0; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import SwitchInput from './SwitchInput'; | ||
import ImageInput from './ImageInput'; | ||
import './Profile.scss'; | ||
|
||
// XXX: mock | ||
const mockData = [ | ||
{ name:"使用者名稱", type:"string", editable: true, value: "codingbear" }, | ||
{ name:"密碼", type:"password", editable: true, value: null }, | ||
{ name:"累積發文數", type:"number", editable: false, value: 276 }, | ||
{ name:"上線顯示", type:"boolean", editable: true, value: true }, | ||
{ name:"頭貼", type:"image", editable: true, value: "https://cdn.discordapp.com/attachments/763787703958372402/1209053537628323900/image.png?ex=65e585da&is=65d310da&hm=51f9cfce771d3b41cfb53286a3ad4ec9fbea74bdec59764830c82d14bd5bd3bf&" }, | ||
]; | ||
|
||
const ProfilePage = () => { | ||
// const updateProfile = () => { | ||
// // TODO: update profile | ||
// }; | ||
|
||
const mapInputType = (type) => { | ||
switch (type) { | ||
case 'string': | ||
return 'text'; | ||
default: | ||
return type; | ||
} | ||
}; | ||
|
||
const renderInput = (data) => { | ||
const { type } = data; | ||
switch (type) { | ||
case 'string': | ||
case 'password': | ||
case 'number': | ||
return renderCommonInput(data); | ||
case 'boolean': | ||
return renderSwitchInput(data); | ||
case 'image': | ||
return renderImageInput(data); | ||
default: | ||
return null; | ||
|
||
} | ||
|
||
}; | ||
|
||
const rendetInputRow = (data) => { | ||
const { name } = data; | ||
return ( | ||
<label key={name}> | ||
<span>{name}</span> | ||
{renderInput(data)} | ||
</label> | ||
); | ||
}; | ||
|
||
const renderCommonInput = ({ name, type, editable, value }) => { | ||
const displayValue = type === 'password' ? '********' : value; | ||
const fixedType = mapInputType(type); | ||
const onChange = () => { | ||
// TODO: update value; | ||
}; | ||
return ( | ||
<input | ||
type={fixedType} | ||
name={name} | ||
readOnly={!editable} | ||
value={displayValue} | ||
onChange={onChange} | ||
/> | ||
); | ||
}; | ||
|
||
const renderSwitchInput = ({ editable, value }) => { | ||
const onClick = () => { | ||
if (editable) { | ||
// TODO: update value; | ||
} | ||
}; | ||
|
||
return ( | ||
<SwitchInput value={value} onClick={onClick} /> | ||
); | ||
}; | ||
|
||
const renderImageInput = ({ value, name, editable }) => { | ||
return ( | ||
<ImageInput value={value} name={name} editable={editable} /> | ||
); | ||
}; | ||
|
||
return ( | ||
<div className="profile-page"> | ||
{mockData.map(rendetInputRow)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default ProfilePage; |
Oops, something went wrong.